示例#1
0
        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);
示例#4
0
 /// <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);
        }
示例#6
0
        /// <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);
        }
示例#7
0
        /// <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();
        }
示例#8
0
 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);
 }
示例#9
0
 public EventStreamWrapper(StorageConfiguration storage, IEnumerable <IProjection <TEvent> > projections, IProjectionCacheProvider projectionCache, ILogAdapter log = null)
     : this(storage.Connect(), projections, projectionCache, log)
 {
 }
示例#10
0
 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);
 }
示例#12
0
 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);
 }