public async Task AppendsEventWhenLastEventIsFromDifferentDay()
        {
            var now                 = new DateTime(2019, 12, 25, 12, 00, 00);
            var utcNow              = now.ToUniversalTime();
            var clockProviderStub   = new ClockProviderStub(now);
            var inMemoryStreamStore = new InMemoryStreamStore(() => utcNow);
            var clockHasTicked      = new ClockHasTicked(new DateTime(2019, 12, 24, 12, 00, 00));

            var cancellationToken = CancellationToken.None;
            await inMemoryStreamStore.AppendToStream(
                streamId : new StreamId(ClockTickingService.ClockStreamId),
                expectedVersion : ExpectedVersion.Any,
                message : new NewStreamMessage(
                    messageId: Guid.NewGuid(),
                    type: _eventMapping.GetEventName(clockHasTicked.GetType()),
                    jsonData: JsonConvert.SerializeObject(clockHasTicked)),
                cancellationToken : cancellationToken);

            var sut = new ClockTickingService(inMemoryStreamStore, clockProviderStub);

            await sut.StartAsync(cancellationToken);

            var forwards = await inMemoryStreamStore.ReadAllForwards(Position.Start, Int32.MaxValue, true, cancellationToken);

            forwards.Messages.Should().HaveCount(2);

            var message = forwards.Messages[1];

            message.StreamId.Should().Be(ClockTickingService.ClockStreamId);
            message.StreamVersion.Should().Be(1);

            var messageData = JsonConvert.DeserializeObject <ClockHasTicked>(await message.GetJsonData(cancellationToken));

            messageData.DateTime.Should().Be(now);
        }
示例#2
0
        private static async Task <long> AddSqlStreamStore(
            Func <IStreamStore> getStreamStore,
            Func <ConcurrentUnitOfWork> getUnitOfWork,
            EventMapping eventMapping,
            EventSerializer eventSerializer,
            CommandMessage message,
            CancellationToken ct)
        {
            var aggregate = getUnitOfWork().GetChanges().SingleOrDefault();

            if (aggregate == null)
            {
                return(-1L);
            }

            if (!message.Metadata.ContainsKey("CommandId"))
            {
                message.Metadata.Add("CommandId", message.CommandId);
            }

            var i      = 1;
            var result = await getStreamStore().AppendToStream(
                aggregate.Identifier,
                aggregate.ExpectedVersion,
                aggregate.Root.GetChangesWithMetadata()
                .Select(o =>
                        new NewStreamMessage(
                            messageId: Deterministic.Create(Deterministic.Namespaces.Events, $"{message.CommandId}-{i++}"),
                            type: eventMapping.GetEventName(o.Event.GetType()),
                            jsonData: eventSerializer.SerializeObject(o.Event),
                            jsonMetadata: eventSerializer.SerializeObject(GetMetadata(message.Metadata, o.Metadata))))
                .ToArray(), ct);

            return(result.CurrentPosition);
        }
        private Func<TestMessage, Task<AppendResult>> CreateMessageStorerAndStartHandling(ConnectedProjection<TestDbContext> projection)
        {
            var eventMapping =
                new EventMapping(EventMapping.DiscoverEventNamesInAssembly(typeof(RunnerTests).Assembly));
            var testRunner =
                new TestRunner(
                    new EnvelopeFactory(
                        eventMapping,
                        new EventDeserializer(JsonConvert.DeserializeObject)),
                    new Logger<TestRunner>(new LoggerFactory()),
                    projection);

            var options = new DbContextOptionsBuilder<TestDbContext>()
                .UseInMemoryDatabase("testing")
                .Options;
            using (var testDbContext = new TestDbContext(options))
            {
                testDbContext.Database.EnsureDeleted();
            }

            var inMemoryStreamStore = new InMemoryStreamStore(() => DateTime.UtcNow);

            testRunner.Handle(
                inMemoryStreamStore,
                () => new Owned<TestDbContext>(new TestDbContext(options), this));

            return async message => await inMemoryStreamStore.AppendToStream(new StreamId("Bla"), ExpectedVersion.Any,
                new NewStreamMessage(Guid.NewGuid(), eventMapping.GetEventName(typeof(TestMessage)), JsonConvert.SerializeObject(message)));
        }
        private static async Task <long> AddSqlStreamStore(
            Func <IStreamStore> getStreamStore,
            Func <ConcurrentUnitOfWork> getUnitOfWork,
            EventMapping eventMapping,
            EventSerializer eventSerializer,
            CommandMessage message,
            CancellationToken ct)
        {
            var uow = getUnitOfWork();

            var aggregate = uow.GetChanges().SingleOrDefault();

            if (aggregate == null)
            {
                return(-1L);
            }

            var streamStore = getStreamStore();

            if (!message.Metadata.ContainsKey("CommandId"))
            {
                message.Metadata.Add("CommandId", message.CommandId);
            }

            var i = 1;

            var events  = aggregate.Root.GetChangesWithMetadata().ToImmutableList();
            var changes = events
                          .Select(o =>
                                  new NewStreamMessage(
                                      messageId: Deterministic.Create(Deterministic.Namespaces.Events, $"{message.CommandId}-{i++}"),
                                      type: eventMapping.GetEventName(o.Event.GetType()),
                                      jsonData: eventSerializer.SerializeObject(o.Event),
                                      jsonMetadata: eventSerializer.SerializeObject(GetMetadata(message.Metadata, o.Metadata))))
                          .ToArray();

            var result = await streamStore.AppendToStream(
                aggregate.Identifier,
                aggregate.ExpectedVersion,
                changes,
                ct);

            if (aggregate.Root is ISnapshotable support)
            {
                await CreateSnapshot(
                    support,
                    new SnapshotStrategyContext(
                        aggregate,
                        events,
                        result.CurrentPosition),
                    streamStore,
                    uow,
                    eventMapping,
                    eventSerializer,
                    ct);
            }

            return(result.CurrentPosition); // Position of the last event written
        }
示例#5
0
        public static ICommandHandlerBuilder <IRoadRegistryContext, TCommand> UseRoadRegistryContext <TCommand>(
            this ICommandHandlerBuilder <TCommand> builder, IStreamStore store, IRoadNetworkSnapshotReader snapshotReader, EventEnricher enricher)
        {
            if (store == null)
            {
                throw new ArgumentNullException(nameof(store));
            }
            if (snapshotReader == null)
            {
                throw new ArgumentNullException(nameof(snapshotReader));
            }
            if (enricher == null)
            {
                throw new ArgumentNullException(nameof(enricher));
            }
            return(builder.Pipe <IRoadRegistryContext>(next => async(message, ct) =>
            {
                var map = new EventSourcedEntityMap();
                var context = new RoadRegistryContext(map, store, snapshotReader, SerializerSettings, EventMapping);

                await next(context, message, ct);

                foreach (var entry in map.Entries)
                {
                    var events = entry.Entity.TakeEvents();
                    if (events.Length != 0)
                    {
                        var messageId = message.MessageId.ToString("N");
                        var version = entry.ExpectedVersion;
                        Array.ForEach(events, @event => enricher(@event));
                        var messages = Array.ConvertAll(
                            events,
                            @event =>
                            new NewStreamMessage(
                                Deterministic.Create(Deterministic.Namespaces.Events,
                                                     $"{messageId}-{version++}"),
                                EventMapping.GetEventName(@event.GetType()),
                                JsonConvert.SerializeObject(@event, SerializerSettings)
                                ));
                        await store.AppendToStream(entry.Stream, entry.ExpectedVersion, messages, ct);
                    }
                }
            }
                                                       ));
        }
            public async Task WhenMessageIsAppended()
            {
                var mapping       = new EventMapping(EventMapping.DiscoverEventNamesInAssembly(typeof(RoadNetworkEvents).Assembly));
                var settings      = EventsJsonSerializerSettingsProvider.CreateSerializerSettings();
                var archiveStream = new StreamName("archive-1");
                var commandStream = new StreamName("road-network-commands");
                var id            = Guid.NewGuid();
                var reaction      = new ReactionScenarioBuilder()
                                    .Given(new RecordedEvent(archiveStream, new Messages.UploadRoadNetworkChangesArchive {
                    ArchiveId = "123"
                }).WithMessageId(id))
                                    .Then(new RecordedEvent(commandStream, new Messages.UploadRoadNetworkChangesArchive {
                    ArchiveId = "123"
                }).WithMessageId(id).WithMetadata(new { Position = 1 }))
                                    .Build();

                var sut = new Subscriber(_store, commandStream);

                using (sut)
                {
                    sut.Start();

                    //Act
                    foreach (var stream in reaction.Givens.GroupBy(given => given.Stream))
                    {
                        await _store.AppendToStream(
                            stream.Key,
                            ExpectedVersion.NoStream,
                            stream.Select((given, index) => new NewStreamMessage(
                                              Deterministic.Create(Deterministic.Namespaces.Events,
                                                                   $"{given.Stream}-{index}"),
                                              mapping.GetEventName(given.Event.GetType()),
                                              JsonConvert.SerializeObject(given.Event, settings),
                                              given.Metadata != null ? JsonConvert.SerializeObject(given.Metadata, settings) : null
                                              )).ToArray());
                    }


                    //Await
                    var page = await _store.ReadStreamForwards(commandStream, StreamVersion.Start, 1);

                    while (page.Status == PageReadStatus.StreamNotFound)
                    {
                        page = await _store.ReadStreamForwards(commandStream, StreamVersion.Start, 1);
                    }

                    //Assert
                    //Assert.Equal(_messageId, page.Messages[0].MessageId);
                }

                await sut.Disposed;
            }
        private async Task AppendClockHasTicked(
            CancellationToken cancellationToken,
            StreamId clockStreamId,
            EventMapping eventMapping)
        {
            var clockHasTicked = new ClockHasTicked(_clockProvider.Now);

            await _streamStore.AppendToStream(
                streamId : clockStreamId,
                expectedVersion : ExpectedVersion.Any,
                message : new NewStreamMessage(
                    messageId: Guid.NewGuid(),
                    type: eventMapping.GetEventName(clockHasTicked.GetType()),
                    jsonData: JsonConvert.SerializeObject(clockHasTicked)),
                cancellationToken : cancellationToken);
        }
        public async Task WriteAsync(IEnumerable <StreamEvent> events)
        {
            if (events == null)
            {
                throw new ArgumentNullException(nameof(events));
            }
            var expectedVersions = new ConcurrentDictionary <StreamId, int>();

            foreach (var batch in events.Batch(1000))
            {
                foreach (var stream in batch.GroupBy(item => item.Stream, item => item.Event))
                {
                    if (!expectedVersions.TryGetValue(stream.Key, out var expectedVersion))
                    {
                        expectedVersion = ExpectedVersion.NoStream;
                    }

                    var watch = Stopwatch.StartNew();

                    var appendResult = await _streamStore.AppendToStream(
                        stream.Key,
                        expectedVersion,
                        stream
                        .Select(@event => new NewStreamMessage(
                                    Deterministic.Create(Deterministic.Namespaces.Events, $"{stream.Key}-{expectedVersion++}"),
                                    Mapping.GetEventName(@event.GetType()),
                                    JsonConvert.SerializeObject(@event, SerializerSettings),
                                    JsonConvert.SerializeObject(new Dictionary <string, string> {
                        { "$version", "0" }
                    }, SerializerSettings)
                                    ))
                        .ToArray()
                        );

                    _logger.LogInformation("Append took {0}ms for stream {1}@{2}",
                                           watch.ElapsedMilliseconds,
                                           stream.Key,
                                           appendResult.CurrentVersion);

                    expectedVersions[stream.Key] = appendResult.CurrentVersion;
                }
            }
        }
示例#9
0
//        private class PointMComparer : BaseTypeComparer
//        {
//            public PointMComparer(RootComparer comparer)
//                :base(comparer)
//            {
//            }
//
//            public override void CompareType(CompareParms parms)
//            {
//                var left = (PointM)parms.Object1;
//                var right = (PointM)parms.Object2;
//                if(!Equals(left.X, right.X)
//                   || !Equals(left.Y, right.Y)
//                   || !Equals(left.Z, right.Z)
//                   || !Equals(left.M, right.M))
//                {
//                    var difference = new Difference
//                    {
//                        Object1 = left,
//                        Object1TypeName = left.GetType().Name,
//                        Object1Value = left.ToString(),
//                        Object2 = right,
//                        Object2TypeName = right.GetType().Name,
//                        Object2Value = right.ToString(),
//                        ParentObject1 = parms.ParentObject1,
//                        ParentObject2 = parms.ParentObject2
//                    };
//                    parms.Result.Differences.Add(difference);
//                }
//            }
//
//            public override bool IsTypeMatch(Type type1, Type type2)
//            {
//                return type1 == typeof(PointM) && type2 == typeof(PointM);
//            }
//        }

        private async Task <long> WriteGivens(RecordedEvent[] givens)
        {
            var checkpoint = SqlStreamStore.Streams.Position.Start;

            foreach (var stream in givens.GroupBy(given => given.Stream))
            {
                var result = await _store.AppendToStream(
                    _converter(new StreamName(stream.Key)).ToString(),
                    ExpectedVersion.NoStream,
                    stream.Select((given, index) => new NewStreamMessage(
                                      Deterministic.Create(Deterministic.Namespaces.Events,
                                                           $"{given.Stream}-{index}"),
                                      _mapping.GetEventName(given.Event.GetType()),
                                      JsonConvert.SerializeObject(given.Event, _settings)
                                      )).ToArray());

                checkpoint = result.CurrentPosition + 1;
            }
            return(checkpoint);
        }
        public async Task <long> PersistFacts(Fact[] facts)
        {
            var factsByAggregate = facts.GroupBy(x => x.Identifier);

            AppendResult result = null;

            foreach (var aggregateWithEvents in factsByAggregate)
            {
                result = await _streamStore.AppendToStream(
                    aggregateWithEvents.Key,
                    ExpectedVersion.Any,
                    aggregateWithEvents
                    .Select(e => new NewStreamMessage(
                                Guid.NewGuid(),
                                _eventMapping.GetEventName(e.Event.GetType()),
                                _eventSerializer.SerializeObject(e.Event))).ToArray());
            }

            return(result?.CurrentPosition ?? await _streamStore.ReadHeadPosition());
        }
        private static async Task CreateSnapshot(
            ISnapshotable snapshotSupport,
            SnapshotStrategyContext context,
            IStreamStore streamStore,
            ConcurrentUnitOfWork uow,
            EventMapping eventMapping,
            EventSerializer eventSerializer,
            CancellationToken ct)
        {
            if (!snapshotSupport.Strategy.ShouldCreateSnapshot(context))
            {
                return;
            }

            var snapshot = snapshotSupport.TakeSnapshot();

            if (snapshot == null)
            {
                throw new InvalidOperationException("Snapshot missing.");
            }

            var snapshotContainer = new SnapshotContainer
            {
                Data = eventSerializer.SerializeObject(snapshot),
                Info =
                {
                    Type     = eventMapping.GetEventName(snapshot.GetType()),
                    Position = context.SnapshotPosition
                }
            };

            await streamStore.AppendToStream(
                uow.GetSnapshotIdentifier(context.Aggregate.Identifier),
                ExpectedVersion.Any,
                new NewStreamMessage(
                    Deterministic.Create(Deterministic.Namespaces.Events, $"snapshot-{context.SnapshotPosition}"),
                    $"SnapshotContainer<{snapshotContainer.Info.Type}>",
                    eventSerializer.SerializeObject(snapshotContainer)),
                ct);
        }