/// <summary>
        /// Creates the current state of an entity from the event stream.
        /// </summary>
        /// <typeparam name="T">Type of the resulting entity</typeparam>
        /// <param name="stream">Event stream</param>
        /// <param name="resourceType">Resource type</param>
        /// <param name="id"></param>
        /// <returns>The resulting entity</returns>
        public static async Task <T> GetCurrentEntityAsync <T>(this IEventStream stream, ResourceType resourceType, int id) where T : class, new()
        {
            if (ResourceType.ResourceTypeDictionary.ContainsKey(resourceType.Name))
            {
                var targetType = typeof(T);
                if (!targetType.IsAssignableFrom(resourceType.Type))
                {
                    throw new ArgumentException("The type parameter doesn't match up with the associated type of the ResourceType");
                }

                var enumerator = stream.GetEnumerator();
                var obj        = default(T);

                while (await enumerator.MoveNextAsync())
                {
                    switch (enumerator.Current)
                    {
                    case CreatedEvent createdEv:
                        if (createdEv.ResourceTypeName.Equals(resourceType.Name) && createdEv.Id == id)
                        {
                            obj = Activator.CreateInstance <T>();
                        }
                        break;

                    case PropertyChangedEvent propertyEv:
                        if (!Equals(obj, default(T)) && Equals(propertyEv.ResourceTypeName, resourceType.Name) && propertyEv.Id == id)
                        {
                            var propertyInfo = targetType.GetProperty(propertyEv.PropertyName);
                            propertyInfo.SetValue(obj, propertyEv.Value);
                        }
                        break;

                    case DeletedEvent deletedEv:
                        if (Equals(deletedEv.ResourceTypeName, resourceType.Name) && deletedEv.Id == id)
                        {
                            obj = default(T);     // entity might be recreated later
                        }
                        break;
                    }
                }

                return(obj);
            }
            else
            {
                throw new ArgumentException("A resource type with the given name does not exist");
            }
        }
예제 #2
0
        /// <summary>
        /// Walks through the specified event stream to obtain a summary of when the specified entity
        /// was created, modified and deleted. This is a potentially expensive operation.
        /// </summary>
        /// <remarks>
        /// This method only looks for standard CRUD events (<see cref="ICreateEvent"/>, <see cref="IUpdateEvent"/>,
        /// <see cref="IDeleteEvent"/>). If for your specific entity type there are other event types the semantically
        /// represent a change to the entity, these won't be part of the generated summary.
        ///
        /// In case the entity lived different lives (i.e. has been recreated after deletion), only the most
        /// recent "life" is considered for the history. Consider the following example event stream (with timestamps):
        /// create (12:00), update (12:04), delete (13:20), create (13:55), delete (14:00), create (16:10), update (16:25).
        /// In this case only the events from 16:10 and 16:25 will be considered and the property
        /// <see cref="HistorySummary.Deleted"/> will not be set.
        ///
        /// The event stream is assumed to be consistent. If the stream is inconsistent (e.g. has a create event
        /// immediately followed by another create event), the behavior and resulting summary is undefined.
        /// </remarks>
        public static async Task <HistorySummary> GetSummaryAsync(IEventStream eventStream, EntityId entityId)
        {
            var enumerator = eventStream.GetEnumerator();
            var summary    = new HistorySummary();

            while (await enumerator.MoveNextAsync())
            {
                if (enumerator.Current is ICrudEvent crudEvent &&
                    crudEvent.GetEntityType() == entityId.Type && crudEvent.Id == entityId.Id)
                {
                    var timestamp = crudEvent.Timestamp;
                    var user      = (crudEvent as IUserActivityEvent)?.UserId;

                    switch (crudEvent)
                    {
                    case ICreateEvent _:
                        if (summary.Created.HasValue)
                        {
                            // assumption: entity was deleted before and is now recreated (we don't check if there
                            // was a delete event before; it's not our job to validate the stream's consistency)
                            summary = new HistorySummary();
                        }

                        summary.Owner        = user;
                        summary.Created      = timestamp;
                        summary.LastModified = timestamp;
                        summary.Changes.Add(new HistorySummary.Change(timestamp, "Created", user));
                        break;

                    case IUpdateEvent _:
                        summary.LastModified = timestamp;
                        summary.Changes.Add(new HistorySummary.Change(timestamp, "Updated", user));
                        break;

                    case IDeleteEvent _:
                        summary.LastModified = timestamp;
                        summary.Deleted      = timestamp;
                        summary.Changes.Add(new HistorySummary.Change(timestamp, "Deleted", user));
                        break;
                    }
                }
            }

            return(summary);
        }
예제 #3
0
 public IAsyncEnumerator <IEvent> GetExistingEvents() => _stream.GetEnumerator();