public virtual void LoadAggregateHistory <TAggregateRoot>(TAggregateRoot aggregate, IList <IEvent <TAuthenticationToken> > events = null, bool throwExceptionOnNoEvents = true) where TAggregateRoot : IAggregateRoot <TAuthenticationToken> { IList <IEvent <TAuthenticationToken> > theseEvents = events ?? EventStore.Get <TAggregateRoot>(aggregate.Id).ToList(); if (!theseEvents.Any()) { if (throwExceptionOnNoEvents) { throw new AggregateNotFoundException <TAggregateRoot, TAuthenticationToken>(aggregate.Id); } return; } var duplicatedEvents = theseEvents.GroupBy(x => x.Version) .Select(x => new { Version = x.Key, Total = x.Count() }) .FirstOrDefault(x => x.Total > 1); if (duplicatedEvents != null) { throw new DuplicateEventException <TAggregateRoot, TAuthenticationToken>(aggregate.Id, duplicatedEvents.Version); } aggregate.LoadFromHistory(theseEvents); }
/// <summary> /// If <paramref name="events"/> is null, loads the events from <see cref="EventStore"/>, checks for duplicates and then /// rehydrates the <paramref name="saga"/> with the events. /// </summary> /// <typeparam name="TSaga">The <see cref="Type"/> of <see cref="ISaga{TAuthenticationToken}"/>.</typeparam> /// <param name="saga">The <typeparamref name="TSaga"/> to rehydrate.</param> /// <param name="events"> /// A collection of <see cref="IEvent{TAuthenticationToken}"/> to replay on the retrieved <see cref="ISaga{TAuthenticationToken}"/>. /// If null, the <see cref="IEventStore{TAuthenticationToken}"/> will be used to retrieve a list of <see cref="IEvent{TAuthenticationToken}"/> for you. /// </param> /// <param name="throwExceptionOnNoEvents">If true will throw an instance of <see cref="SagaNotFoundException{TSaga,TAuthenticationToken}"/> if no aggregate events or provided or found in the <see cref="EventStore"/>.</param> public virtual void LoadSagaHistory <TSaga>(TSaga saga, IList <ISagaEvent <TAuthenticationToken> > events = null, bool throwExceptionOnNoEvents = true) where TSaga : ISaga <TAuthenticationToken> { IList <ISagaEvent <TAuthenticationToken> > theseEvents = events ?? EventStore.Get <TSaga>(saga.Id).Cast <ISagaEvent <TAuthenticationToken> >().ToList(); if (!theseEvents.Any()) { if (throwExceptionOnNoEvents) { throw new SagaNotFoundException <TSaga, TAuthenticationToken>(saga.Id); } return; } var duplicatedEvents = theseEvents.GroupBy(x => x.Version) .Select(x => new { Version = x.Key, Total = x.Count() }) .FirstOrDefault(x => x.Total > 1); if (duplicatedEvents != null) { throw new DuplicateSagaEventException <TSaga, TAuthenticationToken>(saga.Id, duplicatedEvents.Version); } saga.LoadFromHistory(theseEvents); }
public async Task Get_WithFromVersionSmallerThanLatestVersion_ReturnsEventsNewerThanFromVersion() { // arrange var aggregateRootId = Guid.NewGuid(); var events = new IEvent[] { new ProjectCreated { Id = aggregateRootId, Name = "AnyName", TimeStamp = DateTimeOffset.Now, Version = 1 }, new ProjectModified { Id = aggregateRootId, Name = "AnyName", Description = "Any Descrition", TimeStamp = DateTimeOffset.Now, Version = 2 }, }; var serializer = new JsonTextSerializer(); var publisher = new Mock <IEventPublisher>(); var target = new EventStore(this.context.DbContext, serializer, publisher.Object); await target.Save(events); // act IEnumerable <IEvent> actual = await target.Get(aggregateRootId, 1); // assert Assert.Equal(1, actual.Count()); }
protected virtual TAggregateRoot LoadAggregate <TAggregateRoot>(Guid id, IList <IEvent <TAuthenticationToken> > events = null) where TAggregateRoot : IAggregateRoot <TAuthenticationToken> { var aggregate = AggregateFactory.CreateAggregate <TAggregateRoot>(id); IList <IEvent <TAuthenticationToken> > theseEvents = events ?? EventStore.Get <TAggregateRoot>(id).ToList(); if (!theseEvents.Any()) { throw new AggregateNotFoundException <TAggregateRoot, TAuthenticationToken>(id); } var duplicatedEvents = theseEvents.GroupBy(x => x.Version) .Select(x => new { Version = x.Key, Total = x.Count() }) .FirstOrDefault(x => x.Total > 1); if (duplicatedEvents != null) { throw new DuplicateEventException <TAggregateRoot, TAuthenticationToken>(id, duplicatedEvents.Version); } aggregate.LoadFromHistory(theseEvents); return(aggregate); }
/// <summary> /// If <paramref name="events"/> is null, loads the events from <see cref="IEventStore{TAuthenticationToken}"/>, checks for duplicates and then /// rehydrates the <paramref name="aggregate"/> with the events. /// </summary> /// <typeparam name="TAggregateRoot">The <see cref="Type"/> of <see cref="IAggregateRoot{TAuthenticationToken}"/>.</typeparam> /// <param name="aggregate">The <typeparamref name="TAggregateRoot"/> to rehydrate.</param> /// <param name="events"> /// A collection of <see cref="IEvent{TAuthenticationToken}"/> to replay on the retrieved <see cref="IAggregateRoot{TAuthenticationToken}"/>. /// If null, the <see cref="IEventStore{TAuthenticationToken}"/> will be used to retrieve a list of <see cref="IEvent{TAuthenticationToken}"/> for you. /// </param> /// <param name="throwExceptionOnNoEvents">If true will throw an instance of <see cref="AggregateNotFoundException{TAggregateRoot,TAuthenticationToken}"/> if no aggregate events or provided or found in the <see cref="IEventStore{TAuthenticationToken}"/>.</param> public void LoadAggregateHistory <TAggregateRoot>(TAggregateRoot aggregate, IList <IEvent <TAuthenticationToken> > events = null, bool throwExceptionOnNoEvents = true) where TAggregateRoot : IAggregateRoot <TAuthenticationToken> { int snapshotVersion = TryRestoreAggregateFromSnapshot(aggregate.Id, aggregate); IEnumerable <IEvent <TAuthenticationToken> > theseEvents = events ?? EventStore.Get(aggregate.GetType(), aggregate.Id, false, snapshotVersion).Where(desc => desc.Version > snapshotVersion); aggregate.LoadFromHistory(theseEvents); }
/// <summary> /// Save and persist the provided <paramref name="saga"/>, optionally providing the version number the <see cref="ISaga{TAuthenticationToken}"/> is expected to be at. /// </summary> /// <typeparam name="TSaga">The <see cref="Type"/> of the <see cref="ISaga{TAuthenticationToken}"/>.</typeparam> /// <param name="saga">The <see cref="ISaga{TAuthenticationToken}"/> to save and persist.</param> /// <param name="expectedVersion">The version number the <see cref="ISaga{TAuthenticationToken}"/> is expected to be at.</param> public virtual void Save <TSaga>(TSaga saga, int?expectedVersion = null) where TSaga : ISaga <TAuthenticationToken> { IList <ISagaEvent <TAuthenticationToken> > uncommittedChanges = saga.GetUncommittedChanges().ToList(); IEnumerable <ICommand <TAuthenticationToken> > commandsToPublish = saga.GetUnpublishedCommands(); if (!uncommittedChanges.Any()) { PublishCommand(commandsToPublish); return; } if (expectedVersion != null) { IEnumerable <IEvent <TAuthenticationToken> > eventStoreResults = EventStore.Get(saga.GetType(), saga.Id, false, expectedVersion.Value); if (eventStoreResults.Any()) { throw new ConcurrencyException(saga.Id); } } var eventsToPublish = new List <ISagaEvent <TAuthenticationToken> >(); int i = 0; int version = saga.Version; foreach (ISagaEvent <TAuthenticationToken> @event in uncommittedChanges) { if (@event.Rsn == Guid.Empty) { @event.Rsn = saga.Id; } if (@event.Rsn == Guid.Empty) { throw new AggregateOrEventMissingIdException(saga.GetType(), @event.GetType()); } i++; version++; @event.Version = version; @event.TimeStamp = DateTimeOffset.UtcNow; @event.CorrelationId = CorrelationIdHelper.GetCorrelationId(); EventStore.Save(saga.GetType(), @event); eventsToPublish.Add(@event); } saga.MarkChangesAsCommitted(); foreach (ISagaEvent <TAuthenticationToken> @event in eventsToPublish) { PublishEvent(@event); } PublishCommand(commandsToPublish); }
/// <summary> /// Save and persist the provided <paramref name="aggregate"/>, optionally providing the version number the <see cref="IAggregateRoot{TAuthenticationToken}"/> is expected to be at. /// </summary> /// <typeparam name="TAggregateRoot">The <see cref="Type"/> of the <see cref="IAggregateRoot{TAuthenticationToken}"/>.</typeparam> /// <param name="aggregate">The <see cref="IAggregateRoot{TAuthenticationToken}"/> to save and persist.</param> /// <param name="expectedVersion">The version number the <see cref="IAggregateRoot{TAuthenticationToken}"/> is expected to be at.</param> public virtual void Save <TAggregateRoot>(TAggregateRoot aggregate, int?expectedVersion = null) where TAggregateRoot : IAggregateRoot <TAuthenticationToken> { IList <IEvent <TAuthenticationToken> > uncommittedChanges = aggregate.GetUncommittedChanges().ToList(); if (!uncommittedChanges.Any()) { return; } if (expectedVersion != null) { IEnumerable <IEvent <TAuthenticationToken> > eventStoreResults = EventStore.Get(aggregate.GetType(), aggregate.Id, false, expectedVersion.Value); if (eventStoreResults.Any()) { throw new ConcurrencyException(aggregate.Id); } } var eventsToPublish = new List <IEvent <TAuthenticationToken> >(); int i = 0; int version = aggregate.Version; foreach (IEvent <TAuthenticationToken> @event in uncommittedChanges) { if (@event.Id == Guid.Empty) { @event.Id = aggregate.Id; } if (@event.Id == Guid.Empty) { throw new AggregateOrEventMissingIdException(aggregate.GetType(), @event.GetType()); } i++; version++; @event.Version = version; @event.TimeStamp = DateTimeOffset.UtcNow; @event.CorrelationId = CorrelationIdHelper.GetCorrelationId(); EventStore.Save(aggregate.GetType(), @event); eventsToPublish.Add(@event); } aggregate.MarkChangesAsCommitted(); foreach (IEvent <TAuthenticationToken> @event in eventsToPublish) { PublishEvent(@event); } }
public TAggregateRoot Get <TAggregateRoot>(Guid aggregateId, IList <IEvent <TAuthenticationToken> > events = null) where TAggregateRoot : IAggregateRoot <TAuthenticationToken> { var aggregate = AggregateFactory.CreateAggregate <TAggregateRoot>(); int snapshotVersion = TryRestoreAggregateFromSnapshot(aggregateId, aggregate); if (snapshotVersion == -1) { return(Repository.Get <TAggregateRoot>(aggregateId)); } IEnumerable <IEvent <TAuthenticationToken> > theseEvents = events ?? EventStore.Get <TAggregateRoot>(aggregateId, false, snapshotVersion).Where(desc => desc.Version > snapshotVersion); aggregate.LoadFromHistory(theseEvents); return(aggregate); }
public virtual IServiceResponseWithResultData <IEnumerable <EventData> > GetEventData(IServiceRequestWithData <TAuthenticationToken, Guid> serviceRequest) { AuthenticationTokenHelper.SetAuthenticationToken(serviceRequest.AuthenticationToken); CorrelationIdHelper.SetCorrelationId(serviceRequest.CorrelationId); OnGetEventData(serviceRequest); IEnumerable <EventData> results = EventStore.Get(serviceRequest.Data); results = OnGotEventData(serviceRequest, results); return(new ServiceResponseWithResultData <IEnumerable <EventData> > { State = ServiceResponseStateType.Succeeded, ResultData = results, CorrelationId = CorrelationIdHelper.GetCorrelationId() }); }
public async Task Get_WithEventsFromOtherAggregateRoot_ReturnsOnlyEventsFromRequestedAggregateRoot() { // arrange var aggregateRoot1Id = Guid.NewGuid(); var events1 = new IEvent[] { new ProjectCreated { Id = aggregateRoot1Id, Name = "AnyName", TimeStamp = DateTimeOffset.Now, Version = 1 }, new ProjectModified { Id = aggregateRoot1Id, Name = "AnyName", Description = "Any Descrition", TimeStamp = DateTimeOffset.Now, Version = 2 }, }; var aggregateRoot2Id = Guid.NewGuid(); var events2 = new IEvent[] { new ProjectCreated { Id = aggregateRoot2Id, Name = "AnyName", TimeStamp = DateTimeOffset.Now, Version = 1 }, new ProjectModified { Id = aggregateRoot2Id, Name = "AnyName", Description = "Any Descrition", TimeStamp = DateTimeOffset.Now, Version = 2 }, }; var serializer = new JsonTextSerializer(); var publisher = new Mock <IEventPublisher>(); var target = new EventStore(this.context.DbContext, serializer, publisher.Object); await target.Save(events1); await target.Save(events2); // act IEnumerable <IEvent> actual = await target.Get(aggregateRoot1Id, 0); // assert Assert.Equal(2, actual.Count()); }
public TAggregateRoot Get <TAggregateRoot>(Guid aggregateId, IList <IEvent <TAuthenticationToken> > events = null) where TAggregateRoot : IAggregateRoot <TAuthenticationToken> { string idstring = aggregateId.ToString(); try { IList <IEvent <TAuthenticationToken> > theseEvents = null; lock (Locks.GetOrAdd(idstring, _ => new object())) { TAggregateRoot aggregate; if (IsTracked(aggregateId)) { aggregate = (TAggregateRoot)Cache.Get(idstring); theseEvents = events ?? EventStore.Get <TAggregateRoot>(aggregateId, false, aggregate.Version).ToList(); if (theseEvents.Any() && theseEvents.First().Version != aggregate.Version + 1) { Cache.Remove(idstring); } else { aggregate.LoadFromHistory(theseEvents); return(aggregate); } } aggregate = Repository.Get <TAggregateRoot>(aggregateId, theseEvents); Cache.Add(aggregateId.ToString(), aggregate, PolicyFactory.Invoke()); return(aggregate); } } catch (Exception) { Cache.Remove(idstring); throw; } }