public EventSourcedAggregateFactoryTests()
        {
            eventStreamUpgrader = Substitute.For <IEventStreamUpgrader>();
            eventStreamUpgrader.UpgradeStream(null, null).ReturnsForAnyArgs(ci =>
            {
                var stream            = ci.Arg <IEnumerable <IEventMessage <DomainAggregateEvent> > >();
                var aggregateMetadata = ci.Arg <IReadOnlyDictionary <string, string> >();
                return(eventStreamUpgradeFunc(stream, aggregateMetadata));
            });

            DomainClassInfo[] domainClasses = new[]
            {
                new DomainClassInfo(entity1ClassId, null, typeof(MyEntity)),
                new DomainClassInfo(entity2ClassId, null, typeof(MyEntity2))
            };

            entityTypeManager = Substitute.For <IEntityTypeManager>();
            entityTypeManager.GetClassInfoByClassId(Guid.Empty)
            .ReturnsForAnyArgs(ci => domainClasses.Single(x => x.Id == ci.Arg <Guid>()));
            entityTypeManager.TryGetClassInfoByClrType(null)
            .ReturnsForAnyArgs(ci => domainClasses.SingleOrDefault(x => x.ClrType == ci.Arg <Type>()));
            entityTypeManager.GetClassInfoByClrType(null)
            .ReturnsForAnyArgs(ci => domainClasses.Single(x => x.ClrType == ci.Arg <Type>()));

            sut = new EventSourcedAggregateFactory(eventStreamUpgrader, new EntityFactory(), entityTypeManager);
        }
        public IEventSourcedAggregateRoot ConstructAndLoadEntityFromEvents(Guid aggregateId, IReadOnlyDictionary <string, string> eventStreamMetadata,
                                                                           IReadOnlyCollection <IEventStoreRecord> eventRecords)
        {
            if (!eventStreamMetadata.TryGetValue(AggregateEventStreamMetadataNames.ClassId, out string classIdString))
            {
                throw new InvalidOperationException($"Cannot load event sourced aggregate ID {aggregateId}: aggregate class ID not found in event stream metadata");
            }

            Guid classId    = Guid.Parse(classIdString);
            Type entityType = entityTypeManager.GetClassInfoByClassId(classId).ClrType;

            IEnumerable <IEventMessage <DomainAggregateEvent> > eventMessages;

            try
            {
                eventMessages = eventRecords.Select(EventStoreEventMessage.FromRecord)
                                .Cast <IEventMessage <DomainAggregateEvent> >();
            }
            catch (InvalidCastException e)
            {
                throw new InvalidOperationException(
                          $"Cannot load event sourced aggregate ID {aggregateId}: event stream contains non-DomainAggregateEvent events",
                          e);
            }

            var upgradedEvents = eventStreamUpgrader.UpgradeStream(eventMessages, eventStreamMetadata);
            var events         = upgradedEvents.Select(x => x.Event).ToArray();

            int version = (int)(eventRecords.LastOrDefault()?.AdditionalMetadata.GetAggregateVersion() // use non-upgraded event records to preserve the versions
                                ?? eventRecords.LastOrDefault()?.StreamSequenceNumber
                                ?? 0);

            AggregateState state = new AggregateState(version, events);

            IEventSourcedAggregateRoot aggregate = (IEventSourcedAggregateRoot)ConstructEntity(entityType, aggregateId);

            aggregate.LoadState(state);

            return(aggregate);
        }