public MappedCacheProvider(string directory, IProjectionCacheProvider inner = null) { _directory = directory ?? throw new ArgumentNullException(nameof(directory)); _inner = inner; Directory.CreateDirectory(_directory); }
public ReifiedProjection(IProjection <TEvent, TState> projection, IProjectionCacheProvider cacheProvider = null, ILogAdapter log = null) { if (projection == null) { throw new ArgumentNullException(nameof(projection)); } // Cache the projection's full name. This shields us against projection authors // writing changing names. Name = projection.FullName; if (Name == null) { throw new ArgumentException("Projection must have a name", nameof(projection)); } var nameRegexp = new Regex("^[-a-zA-Z0-9_]{1,16}$"); if (!nameRegexp.IsMatch(Name)) { throw new ArgumentException("Projection name must match [-a-zA-Z0-9_]{1,16}", nameof(projection)); } _projection = projection; _cacheProvider = cacheProvider; _log = log; _log?.Debug("Using projection: " + Name); Reset(); }
/// <summary> Initialize and start the service. </summary> /// <remarks> /// This will start a background task that performs all processing /// and regularly updates the contents. /// </remarks> /// <param name="storage"> Identifies where the event stream data is stored. </param> /// <param name="projections"> All available <see cref="IProjection{T}"/> instances. </param> /// <param name="projectionCache"> Used to save projected state. </param> /// <param name="log"> Used for logging. </param> /// <param name="cancel"> Stops the background tasks when called. </param> public static EventStreamService <TEvent, TState> StartNew( StorageConfiguration storage, IEnumerable <IProjection <TEvent> > projections, IProjectionCacheProvider projectionCache, ILogAdapter log, CancellationToken cancel) => new EventStreamService <TEvent, TState>( storage, projections, projectionCache, log, cancel);
/// <summary> Initialize and start the service. </summary> /// <remarks> /// This will start a background task that performs all processing /// and regularly updates the contents. /// </remarks> /// <param name="storage"> Identifies where the event stream data is stored. </param> /// <param name="projections"> All available <see cref="IProjection{T}"/> instances. </param> /// <param name="projectionCache"> Used to save projected state. </param> /// <param name="events"> /// Called on each event committed to the stream (including intial catch-up of /// already commited events). /// <see cref="EventStreamWrapper{TEvent,TState}.OnEachCommitted"/> /// </param> /// <param name="log"> Used for logging. </param> /// <param name="cancel"> Stops the background tasks when called. </param> public static EventStreamService <TEvent, TState> StartNew( StorageConfiguration storage, IEnumerable <IProjection <TEvent> > projections, IProjectionCacheProvider projectionCache, IEnumerable <Tuple <EventStream <TEvent> .Listener, uint> > events, ILogAdapter log, CancellationToken cancel) { return(new EventStreamService <TEvent, TState>( storage, projections, projectionCache, events, log, cancel)); }
/// <remarks> /// This constructor is private so that it is obvious (via <c>StartNew</c>) /// that background tasks are being creatd. /// </remarks> private EventStreamService( StorageConfiguration storage, IEnumerable <IProjection <TEvent> > projections, IProjectionCacheProvider projectionCache, ILogAdapter log, CancellationToken cancel) : base(TimeSpan.FromSeconds(30), cancel) { _log = log; _cancel = cancel; Wrapper = new EventStreamWrapper <TEvent, TState>(storage, projections, projectionCache, log); Quarantine = Wrapper.Quarantine; Ready = Task.Run(Initialize, cancel); }
/// <remarks> /// This constructor is private so that it is obvious (via <c>StartNew</c>) /// that background tasks are being creatd. /// </remarks> private EventStreamService( StorageConfiguration storage, IEnumerable <IProjection <TEvent> > projections, IProjectionCacheProvider projectionCache, IEnumerable <Tuple <EventStream <TEvent> .Listener, uint> > events, ILogAdapter log, CancellationToken cancel) { _log = log; _cancel = cancel; Wrapper = new EventStreamWrapper <TEvent, TState>(storage, projections, projectionCache, log); Quarantine = Wrapper.Quarantine; if (events != null) { foreach (var pair in events) { Wrapper.OnEachCommitted(pair.Item1, pair.Item2); } } Ready = Task.Run(Initialize, cancel); Task.Run(Loop, cancel); }
/// <summary> /// Reifies a group of projections as a <typeparamref name="TState"/> object. /// </summary> public ReifiedProjectionGroup(IEnumerable <IProjection <TEvent> > projections, IProjectionCacheProvider cacheProvider = null, ILogAdapter log = null) { // Dirty reflection work to reify the individual projections and store their // type parameter. var reifiedByType = new Dictionary <Type, IReifiedProjection <TEvent> >(); foreach (var p in projections) { if (reifiedByType.ContainsKey(p.State)) { log?.Error($"Found multiple projections for type {p.State}."); throw new ArgumentException($"Multiple projections for type '{p.State}'", nameof(projections)); } var reifiedProjectionType = typeof(ReifiedProjection <,>) .MakeGenericType(typeof(TEvent), p.State); var projectionType = typeof(IProjection <,>) .MakeGenericType(typeof(TEvent), p.State); var constructor = reifiedProjectionType .GetTypeInfo() .GetConstructor(new [] { projectionType, typeof(IProjectionCacheProvider), typeof(ILogAdapter) }); if (constructor == null) { throw new Exception("No constructor found for '" + reifiedProjectionType + "'"); } IReifiedProjection <TEvent> reified; try { reified = (IReifiedProjection <TEvent>)constructor.Invoke(new object[] { p, cacheProvider, log }); } catch (TargetInvocationException e) { log?.Error($"When creating reified projection for type {p.State}.", e.InnerException); throw e.InnerException; } reifiedByType.Add(p.State, reified); } // Easy case: a projection outputs the state type directly IReifiedProjection <TEvent> direct; if (reifiedByType.TryGetValue(typeof(TState), out direct)) { _reifiedProjections = new[] { direct }; var directCast = (ReifiedProjection <TEvent, TState>)direct; _refresh = () => directCast.Current; InvalidateCurrent(); return; } // Hard case: state is made from the results of multiple independent // projections. Need to write code to combine them. var posByType = new Dictionary <Type, int>(); var reifiedByPos = new List <IReifiedProjection <TEvent> >(); var stateConstructor = typeof(TState) .GetTypeInfo() .GetConstructors() .FirstOrDefault(c => { var p = c.GetParameters(); if (p.Length == 0) { return(false); } return(p.All(info => reifiedByType.ContainsKey(info.ParameterType))); }); if (stateConstructor == null) { throw new ArgumentException($"No compatible constructor found in '{typeof(TState)}'", nameof(projections)); } // Build an expression that will extract the state components from "array" and // pass them to the constructor. var parameters = stateConstructor.GetParameters(); var array = Expression.Parameter(typeof(IReifiedProjection <TEvent>[])); var arguments = new List <Expression>(); foreach (var p in parameters) { int pos; if (!posByType.TryGetValue(p.ParameterType, out pos)) { pos = posByType.Count; posByType.Add(p.ParameterType, pos); reifiedByPos.Add(reifiedByType[p.ParameterType]); } var argReified = Expression.Convert( Expression.ArrayIndex(array, Expression.Constant(pos)), typeof(ReifiedProjection <,>).MakeGenericType(typeof(TEvent), p.ParameterType)); arguments.Add(Expression.Property(argReified, "Current")); } var lambda = Expression.Lambda <Func <IReifiedProjection <TEvent>[], TState> >( Expression.New(stateConstructor, arguments), array); var extract = lambda.Compile(); var reifiedProjections = _reifiedProjections = reifiedByPos.ToArray(); _refresh = () => extract(reifiedProjections); InvalidateCurrent(); }
internal EventStreamWrapper(IStorageDriver storage, IEnumerable <IProjection <TEvent> > projections, IProjectionCacheProvider projectionCache, ILogAdapter log = null) { _log = log; Stream = new EventStream <TEvent>(storage, log); _projection = new ReifiedProjectionGroup <TEvent, TState>(projections, projectionCache, log); }
public EventStreamWrapper(StorageConfiguration storage, IEnumerable <IProjection <TEvent> > projections, IProjectionCacheProvider projectionCache, ILogAdapter log = null) : this(storage.Connect(), projections, projectionCache, log) { }
protected override IReifiedProjection <int, string> Make(IProjection <int, string> projection, IProjectionCacheProvider cache = null) { return(new ReifiedProjectionGroup <int, string>(new[] { projection }, cache)); }
public file_projection_cache() { _path = @"C:\LokadData\AzureEventStore\FileStorageTests\" + Guid.NewGuid(); _file = new FileCacheProvider(_path); }
protected virtual IReifiedProjection <int, string> Make(IProjection <int, string> projection, IProjectionCacheProvider cache = null) { return(new ReifiedProjection <int, string>(projection, cache)); }
public void SetUp() { _path = @"C:\LokadData\AzureEventStore\FileStorageTests\" + Guid.NewGuid(); _file = new FileProjectionCache(_path); }