public async Task When_a_projector_update_fails_then_an_entry_is_added_to_EventHandlingErrors()
        {
            // arrange
            var errorMessage = Any.Paragraph(10);
            var productName  = Any.Paragraph();
            var projector    = new Projector <Order.ItemAdded>
            {
                OnUpdate = (work, e) => { throw new Exception(errorMessage); }
            };
            var order      = new Order();
            var repository = new SqlEventSourcedRepository <Order>();

            order.Apply(new AddItem
            {
                Price       = 1m,
                ProductName = productName
            });
            await repository.Save(order);

            // act
            using (var catchup = CreateReadModelCatchup(projector))
            {
                await catchup.Run();
            }

            // assert
            using (var db = ReadModelDbContext())
            {
                var error = db.Set <EventHandlingError>().Single(e => e.AggregateId == order.Id);
                error.StreamName.Should().Be("Order");
                error.EventTypeName.Should().Be("ItemAdded");
                error.SerializedEvent.Should().Contain(productName);
                error.Error.Should().Contain(errorMessage);
            }
        }
        public void When_the_aggregate_is_saved_then_the_reservation_is_confirmed()
        {
            // arrange
            var username = Any.Email();

            var account = new CustomerAccount();

            account.Apply(new RequestUserName
            {
                UserName  = username,
                Principal = new Customer(username)
            });
            var bus = new FakeEventBus();

            bus.Subscribe(new UserNameConfirmer());
            var repository = new SqlEventSourcedRepository <CustomerAccount>(bus);

            // act
            repository.Save(account);

            // assert
            using (var db = new ReservationServiceDbContext())
            {
                db.Set <ReservedValue>()
                .Single(v => v.Value == username && v.Scope == "UserName")
                .Expiration
                .Should()
                .BeNull();
            }
        }
        public void Posting_an_invalid_command_does_not_affect_the_aggregate_state()
        {
            var order = new Order(Guid.NewGuid(),
                                  new Order.CustomerInfoChanged { CustomerName = "Joe" },
                                  new Order.Fulfilled()).SaveToEventStore();
            var json = new AddItem
            {
                Quantity = 5,
                Price = 19.99m,
                ProductName = "Bag o' Treats"
            }.ToJson();

            var request = new HttpRequestMessage(HttpMethod.Post, string.Format("http://contoso.com/orders/{0}/additem", order.Id))
            {
                Content = new StringContent(json, Encoding.UTF8, "application/json")
            };

            var testApi = new TestApi<Order>();
            var client = testApi.GetClient();

            var response = client.SendAsync(request).Result;

            response.StatusCode.Should().Be(HttpStatusCode.BadRequest);

            var updatedOrder = new SqlEventSourcedRepository<Order>().GetLatest(order.Id);

            updatedOrder.Items.Count().Should().Be(0);
        }
Exemple #4
0
        public override async Task When_storage_fails_then_no_events_are_published()
        {
            var order           = new Order();
            var bus             = new InProcessEventBus();
            var eventsPublished = new List <IEvent>();

            bus.Events <IEvent>().Subscribe(eventsPublished.Add);
            Func <EventStoreDbContext> eventStoreDbContext = () =>
            {
                throw new Exception("oops!");
            };
            var repository = new SqlEventSourcedRepository <Order>(bus, eventStoreDbContext);

            order
            .Apply(new AddItem
            {
                ProductName = "Widget",
                Price       = 10m,
                Quantity    = 2
            });

            try
            {
                await repository.Save(order);
            }
            catch
            {
            }

            eventsPublished.Should().BeEmpty();
        }
        public async Task When_using_Update_then_failed_writes_are_logged_to_EventHandlingErrors()
        {
            // preload some events for the catchup. replay will hit the barrier on the last one.
            var order       = new Order();
            var productName = Any.Paragraph(4);

            order.Apply(new AddItem
            {
                Quantity    = 1,
                ProductName = productName,
                Price       = .01m
            });
            var repository = new SqlEventSourcedRepository <Order>(new FakeEventBus());
            await repository.Save(order);

            Projector1 projector = null;

            projector = new Projector1
            {
                OnUpdate = (_, e) =>
                {
                    using (var work = projector.Update())
                    {
                        var db = work.Resource <ReadModelDbContext>();
                        // do something that will trigger a db exception when the UnitOfWork is committed
                        var inventory = db.Set <ProductInventory>();
                        inventory.Add(new ProductInventory
                        {
                            ProductName      = e.ProductName,
                            QuantityReserved = e.Quantity
                        });
                        inventory.Add(new ProductInventory
                        {
                            ProductName      = e.ProductName,
                            QuantityReserved = e.Quantity
                        });
                        work.VoteCommit();
                    }
                }
            };

            // act
            using (var catchup = CreateReadModelCatchup(projector))
            {
                await catchup.Run();
            }

            // assert
            using (var db = new ReadModelDbContext())
            {
                var error = db.Set <EventHandlingError>().Single(e => e.AggregateId == order.Id);
                error.Error.Should()
                .Contain(
                    string.Format(
                        "Violation of PRIMARY KEY constraint 'PK_dbo.ProductInventories'. Cannot insert duplicate key in object 'dbo.ProductInventories'. The duplicate key value is ({0})",
                        productName));
            }
        }
Exemple #6
0
        public async Task When_using_Update_then_failed_writes_do_not_interrupt_catchup()
        {
            // preload some events for the catchup. replay will hit the barrier on the last one.
            var    order       = new Order();
            var    productName = Any.Paragraph(4);
            Action addEvent    = () => order.Apply(new AddItem
            {
                Quantity    = 1,
                ProductName = productName,
                Price       = .01m
            });

            Enumerable.Range(1, 30).ForEach(_ => addEvent());
            var repository = new SqlEventSourcedRepository <Order>(new FakeEventBus());
            await repository.Save(order);

            var        count     = 0;
            Projector1 projector = null;

            projector = new Projector1
            {
                OnUpdate = (_, e) =>
                {
                    using (var work = projector.Update())
                    {
                        var db = work.Resource <ReadModelDbContext>();
                        if (count++ == 15)
                        {
                            // do something that will trigger a db exception when the UnitOfWork is committed
                            var inventory = db.Set <ProductInventory>();
                            inventory.Add(new ProductInventory
                            {
                                ProductName      = e.ProductName,
                                QuantityReserved = e.Quantity
                            });
                            inventory.Add(new ProductInventory
                            {
                                ProductName      = e.ProductName,
                                QuantityReserved = e.Quantity
                            });
                        }
                        work.VoteCommit();
                    }
                }
            };

            // act
            using (var catchup = CreateReadModelCatchup(projector))
            {
                await catchup.Run();
            }

            // assert
            count.Should().Be(30);
        }
Exemple #7
0
        public void Configuration(IAppBuilder app)
        {
            IMessageHandler messageHandler = null;

            IMessageBus messageBus = new ImmediateMessageBus(
                new Lazy <IMessageHandler>(() => messageHandler));

            var serializer = new JsonMessageSerializer();

            Func <TodoListEventStoreDbContext> dbContextFactory = () =>
            {
                var context = new TodoListEventStoreDbContext();
                context.Database.Log += m => Debug.WriteLine(m);
                return(context);
            };

            IEventSourcedRepository <Domain.TodoItem> repository =
                new SqlEventSourcedRepository <Domain.TodoItem>(
                    new SqlEventStore(
                        dbContextFactory,
                        serializer),
                    new SqlEventPublisher(
                        dbContextFactory,
                        serializer,
                        messageBus),
                    new SqlMementoStore(
                        dbContextFactory,
                        serializer),
                    Domain.TodoItem.Factory,
                    Domain.TodoItem.Factory);

            messageHandler = new CompositeMessageHandler(
                new TodoItemCommandHandler(repository),
                new ReadModelGenerator(() => new ReadModelDbContext()));

            IReadModelFacade readModelFacade =
                new ReadModelFacade(() => new ReadModelDbContext());

            app.Use(async(context, next) =>
            {
                context.Set(nameof(IMessageBus), messageBus);
                context.Set(nameof(IReadModelFacade), readModelFacade);
                await next.Invoke();
            });

            var properties = new AppProperties(app.Properties);

            repository.EventPublisher.EnqueueAll(properties.OnAppDisposing);
        }
        private void TriggerConcurrencyExceptionOnOrderCommands(Guid orderId)
        {
            var orderRepository = Configuration.Current.Repository<Order>();
            Configuration.Current.UseDependency(_ => orderRepository);
            ((SqlEventSourcedRepository<Order>) orderRepository).GetEventStoreContext = () =>
            {
                // quick, add a new event in order to trigger a concurrency exception at the moment the scheduler tries to apply the command
                var repository = new SqlEventSourcedRepository<Order>();
                var o = repository.GetLatest(orderId).Result;
                o.Apply(new Annotate<Order>("triggering a concurrency exception", Any.Guid().ToString()));
                repository.Save(o).Wait();

                return EventStoreDbContext();
            };
        }
Exemple #9
0
        private void TriggerConcurrencyExceptionOnOrderCommands(Guid orderId)
        {
            Func <EventStoreDbContext> eventStoreContext = () =>
            {
                // quick, add a new event in order to trigger a concurrency exception at the moment the scheduler tries to apply the command
                var repository = new SqlEventSourcedRepository <Order>();
                var o          = repository.GetLatest(orderId).Result;
                o.Apply(new Annotate <Order>("triggering a concurrency exception", Any.Guid().ToString()));
                repository.Save(o).Wait();

                return(EventStoreDbContext());
            };

            var orderRepository = new SqlEventSourcedRepository <Order>(createEventStoreDbContext: eventStoreContext);

            Configuration.Current.UseDependency <IEventSourcedRepository <Order> >(_ => orderRepository);
        }
        public async Task ApplyBatch_can_accept_an_array_of_commands()
        {
            var repository = new SqlEventSourcedRepository <Order>(new FakeEventBus());
            var order      = new Order();
            await repository.Save(order);

            var json = new[]
            {
                new
                {
                    AddItem = new
                    {
                        Quantity    = 1,
                        Price       = 1,
                        ProductName = "Sprocket"
                    }
                },
                new
                {
                    AddItem = new
                    {
                        Quantity    = 1,
                        Price       = 2,
                        ProductName = "Cog"
                    }
                }
            }.ToJson();

            var testApi = new TestApi <Order>();
            var client  = testApi.GetClient();

            var request = new HttpRequestMessage(HttpMethod.Post, string.Format("http://contoso.com/orders/{0}", order.Id))
            {
                Content = new StringContent(json, Encoding.UTF8, "application/json")
            };

            var response = await client.SendAsync(request);

            response.ShouldSucceed();

            order = await repository.GetLatest(order.Id);

            order.Items.Count.Should().Be(2);
            order.Balance.Should().Be(3);
        }
Exemple #11
0
        public async Task When_Run_is_called_while_already_running_then_it_skips_the_run()
        {
            var repository = new SqlEventSourcedRepository <Order>(new FakeEventBus());
            await repository.Save(new Order());

            var mre      = new ManualResetEventSlim();
            var barrier  = new Barrier(2);
            var progress = new List <ReadModelCatchupStatus>();

            Events.Write(10);
            var projector = new Projector <Order.ItemAdded>
            {
                OnUpdate = (work, e) =>
                {
                    barrier.SignalAndWait(1000);
                    mre.Wait(5000);
                }
            };

            using (var catchup = CreateReadModelCatchup(projector))
                using (catchup.Progress.Subscribe(s =>
                {
                    progress.Add(s);
                    Console.WriteLine("progress: " + s);
                }))
                {
#pragma warning disable 4014
                    // don't await
                    Task.Run(() => catchup.Run());
#pragma warning restore 4014

                    // make sure the first catchup is blocked inside the projector
                    barrier.SignalAndWait(1000);

                    // try to start another catchup
                    var result = await catchup.Run();

                    result.Should().Be(ReadModelCatchupResult.CatchupAlreadyInProgress);
                    await Task.Delay(2000);
                }

            mre.Set();
            progress.Should().ContainSingle(s => s.IsStartOfBatch);
        }
Exemple #12
0
        public async Task When_not_using_Update_then_failed_writes_do_not_interrupt_catchup()
        {
            // arrange
            // preload some events for the catchup. replay will hit the barrier on the last one.
            var    order       = new Order();
            var    productName = Any.Paragraph(3);
            Action addEvent    = () => order.Apply(new AddItem
            {
                Quantity    = 1,
                ProductName = productName,
                Price       = .01m
            });

            Enumerable.Range(1, 30).ForEach(_ => addEvent());
            var repository = new SqlEventSourcedRepository <Order>(new FakeEventBus());
            await repository.Save(order);

            var count     = 0;
            var projector = new Projector1
            {
                OnUpdate = (work, e) =>
                {
                    using (var db = ReadModelDbContext())
                    {
                        // throw one exception in the middle
                        if (count++ == 15)
                        {
                            throw new Exception("drat!");
                        }
                        db.SaveChanges();
                    }
                }
            };

            // act
            using (var catchup = CreateReadModelCatchup(projector))
            {
                await catchup.Run();
            }

            // assert
            count.Should().Be(30);
        }
Exemple #13
0
        public override void SetUp()
        {
            base.SetUp();

            using (VirtualClock.Start(DateTimeOffset.Now.AddMonths(1)))
            {
                disposables      = new CompositeDisposable();
                Settings.Sources = new ISettingsSource[] { new ConfigDirectorySettings(@"c:\dev\.config") }.Concat(Settings.Sources);

                serviceBusSettings                = Settings.Get <ServiceBusSettings>();
                serviceBusSettings.NamePrefix     = "itscqrstests";
                serviceBusSettings.ConfigureQueue = q =>
                {
                    q.AutoDeleteOnIdle = TimeSpan.FromMinutes(15);
                };

                bus             = new FakeEventBus();
                orderRepository = new SqlEventSourcedRepository <Order>(bus);

                var configuration = new Configuration()
                                    .UseSqlEventStore(() => new EventStoreDbContext())
                                    .UseEventBus(bus)
                                    .UseSqlCommandScheduling()
                                    .UseDependency <IEventSourcedRepository <Order> >(t => orderRepository);

                var clockName = Any.Paragraph(4);
                scheduler = new SqlCommandScheduler(configuration)
                {
                    GetClockName = @event => clockName
                };

                queueSender = new ServiceBusCommandQueueSender(serviceBusSettings)
                {
                    MessageDeliveryOffsetFromCommandDueTime = TimeSpan.FromSeconds(30)
                };

                disposables.Add(scheduler.Activity.Subscribe(s => Console.WriteLine("SqlCommandScheduler: " + s.ToJson())));
                disposables.Add(queueSender.Messages.Subscribe(s => Console.WriteLine("ServiceBusCommandQueueSender: " + s.ToJson())));
                disposables.Add(bus.Subscribe(scheduler));
                disposables.Add(configuration);
                disposables.Add(ConfigurationContext.Establish(configuration));
            }
        }
        public async Task ReadModelCatchup_only_queries_events_since_the_last_consumed_event_id()
        {
            var bus        = new FakeEventBus();
            var repository = new SqlEventSourcedRepository <Order>(bus);

            // save the order with no projectors running
            var order = new Order();

            order.Apply(new AddItem
            {
                Price       = 1m,
                ProductName = "Widget"
            });
            await repository.Save(order);

            // subscribe one projector for catchup
            var projector1 = new Projector1();

            using (var catchup = CreateReadModelCatchup(projector1))
            {
                catchup.Progress.ForEachAsync(s => Console.WriteLine(s));
                await catchup.Run();
            }

            order.Apply(new AddItem
            {
                Price       = 1m,
                ProductName = "Widget"
            });
            await repository.Save(order);

            // subscribe both projectors
            var projector2 = new Projector2();

            using (var catchup = CreateReadModelCatchup(projector1, projector2))
            {
                catchup.Progress.ForEachAsync(s => Console.WriteLine(s));
                await catchup.Run();
            }

            projector1.CallCount.Should().Be(2, "A given event should only be passed to a given projector once");
            projector2.CallCount.Should().Be(2, "A projector should be passed events it has not previously seen.");
        }
        public override void SetUp()
        {
            base.SetUp();

            using (VirtualClock.Start(DateTimeOffset.Now.AddMonths(1)))
            {
                disposables = new CompositeDisposable();
                Settings.Sources = new ISettingsSource[] { new ConfigDirectorySettings(@"c:\dev\.config") }.Concat(Settings.Sources);

                serviceBusSettings = Settings.Get<ServiceBusSettings>();
                serviceBusSettings.NamePrefix = "itscqrstests";
                serviceBusSettings.ConfigureQueue = q =>
                {
                    q.AutoDeleteOnIdle = TimeSpan.FromMinutes(15);
                };

                bus = new FakeEventBus();
                orderRepository = new SqlEventSourcedRepository<Order>(bus);

                var configuration = new Configuration()
                    .UseSqlEventStore(() => new EventStoreDbContext())
                    .UseEventBus(bus)
                    .UseSqlCommandScheduling()
                    .UseDependency<IEventSourcedRepository<Order>>(t => orderRepository);

                var clockName = Any.Paragraph(4);
                scheduler = new SqlCommandScheduler(configuration) { GetClockName = @event => clockName };

                queueSender = new ServiceBusCommandQueueSender(serviceBusSettings)
                {
                    MessageDeliveryOffsetFromCommandDueTime = TimeSpan.FromSeconds(30)
                };

                disposables.Add(scheduler.Activity.Subscribe(s => Console.WriteLine("SqlCommandScheduler: " + s.ToJson())));
                disposables.Add(queueSender.Messages.Subscribe(s => Console.WriteLine("ServiceBusCommandQueueSender: " + s.ToJson())));
                disposables.Add(bus.Subscribe(scheduler));
                disposables.Add(configuration);
                disposables.Add(ConfigurationContext.Establish(configuration));
            }
        }
Exemple #16
0
        public void When_Run_is_called_while_already_running_then_it_skips_the_run()
        {
            var repository = new SqlEventSourcedRepository <Order>(new FakeEventBus());

            new Order().Apply(new AddItem
            {
                Price       = 1m,
                ProductName = MethodBase.GetCurrentMethod().Name
            });
            repository.Save(new Order());
            var mre      = new ManualResetEventSlim();
            var progress = new List <ReadModelCatchupStatus>();

            var signal    = new ReplaySubject <Unit>();
            var projector = new Projector <Order.ItemAdded>(() => new ReadModelDbContext())
            {
                OnUpdate = (work, e) =>
                {
                    signal.OnNext(Unit.Default);
                    mre.Wait(20000);
                }
            };

            using (var catchup = CreateReadModelCatchup(projector))
            {
                catchup.Progress.ForEachAsync(s =>
                {
                    progress.Add(s);
                    Console.WriteLine(s);
                });

                Task.Run(() => { catchup.Run(); });

                mre.Set();
                catchup.Run();
            }

            progress.Should().ContainSingle(s => s.IsStartOfBatch);
        }
        public async Task An_ETag_header_is_applied_to_the_command()
        {
            var order = new Order(Guid.NewGuid(),
                                  new Order.CustomerInfoChanged { CustomerName = "Joe" }).SaveToEventStore();
            var json = new AddItem
            {
                Quantity = 5,
                Price = 19.99m,
                ProductName = "Bag o' Treats"
            }.ToJson();
            
            var etag = new EntityTagHeaderValue("\"" + Any.Guid() + "\"");

            Func<HttpRequestMessage> createRequest = () =>
            {
                var request = new HttpRequestMessage(HttpMethod.Post, string.Format("http://contoso.com/orders/{0}/additem", order.Id))
                {
                    Content = new StringContent(json, Encoding.UTF8, "application/json"),
                };
                request.Headers.IfNoneMatch.Add(etag);
                return request;
            };

            var testApi = new TestApi<Order>();
            var client = testApi.GetClient();

            // act: send the request twice
            var response1 = await client.SendAsync(createRequest());
            var response2 = await client.SendAsync(createRequest());

            // assert
            response1.ShouldSucceed(HttpStatusCode.OK);
            response2.ShouldFailWith(HttpStatusCode.NotModified);

            var updatedOrder = new SqlEventSourcedRepository<Order>().GetLatest(order.Id);
            updatedOrder.Items.Single().Quantity.Should().Be(5);
        }
Exemple #18
0
        public async Task When_Save_fails_then_a_scheduled_command_error_is_recorded()
        {
            // arrange
            var order           = CommandSchedulingTests_EventSourced.CreateOrder();
            var innerRepository = new SqlEventSourcedRepository <Order>();
            var saveCount       = 0;

            Configuration.Current
            .Container
            .Register <IEventSourcedRepository <Order> >(c => new FakeEventSourcedRepository <Order>(innerRepository)
            {
                OnSave = async o =>
                {
                    saveCount++;

                    // throw on the second save attempt, which is when the clock is advanced delivering the scheduled command
                    if (saveCount == 2)
                    {
                        throw new Exception("oops!");
                    }
                    await innerRepository.Save(o);
                }
            });

            // act
            order.Apply(new ShipOn(Clock.Now().AddDays(30)));
            await Save(order);
            await AdvanceClock(TimeSpan.FromDays(31));

            //assert
            using (var db = CommandSchedulerDbContext())
            {
                var error = db.Errors.Single(c => c.ScheduledCommand.AggregateId == order.Id).Error;
                error.Should().Contain("oops!");
            }
        }
        public async Task When_a_scheduled_command_fails_then_the_error_is_recorded()
        {
            // arrange
            var order = CommandSchedulingTests.CreateOrder();
            var innerRepository = new SqlEventSourcedRepository<Order>();
            var saveCount = 0;
            Configuration.Current
                         .Container
                         .Register<IEventSourcedRepository<Order>>(c => new FakeEventSourcedRepository<Order>(innerRepository)
                         {
                             OnSave = async o =>
                             {
                                 if (saveCount > 0)
                                 {
                                     throw new Exception("oops!");
                                 }
                                 await innerRepository.Save(o);
                                 saveCount++;
                             }
                         });

            // act
            order.Apply(new ShipOn(Clock.Now().AddDays(30)));
            await Configuration.Current.Repository<Order>().Save(order);
            await clockTrigger.AdvanceClock(clockName, TimeSpan.FromDays(31));

            //assert 
            using (var db = new CommandSchedulerDbContext())
            {
                var error = db.Errors.Single(c => c.ScheduledCommand.AggregateId == order.Id).Error;
                error.Should().Contain("oops!");
            }
        }
 public OrderController(SqlEventSourcedRepository <Order> sqlEventSourcedRepository) : base(sqlEventSourcedRepository)
 {
 }
        public async Task When_Save_fails_then_a_scheduled_command_error_is_recorded()
        {
            // arrange
            var order = CommandSchedulingTests_EventSourced.CreateOrder();
            var innerRepository = new SqlEventSourcedRepository<Order>();
            var saveCount = 0;
            Configuration.Current
                .Container
                .Register<IEventSourcedRepository<Order>>(c => new FakeEventSourcedRepository<Order>(innerRepository)
                {
                    OnSave = async o =>
                    {
                        saveCount++;

                        // throw on the second save attempt, which is when the clock is advanced delivering the scheduled command
                        if (saveCount == 2)
                        {
                            throw new Exception("oops!");
                        }
                        await innerRepository.Save(o);
                    }
                });

            // act
            order.Apply(new ShipOn(Clock.Now().AddDays(30)));
            await Save(order);
            await AdvanceClock(TimeSpan.FromDays(31));

            //assert 
            using (var db = CommandSchedulerDbContext())
            {
                var error = db.Errors.Single(c => c.ScheduledCommand.AggregateId == order.Id).Error;
                error.Should().Contain("oops!");
            }
        }
Exemple #22
0
        public async Task Insertion_of_new_events_during_catchup_does_not_interrupt_catchup()
        {
            var barrier = new Barrier(2);

            // preload some events for the catchup. replay will hit the barrier on the last one.
            var    order    = new Order();
            Action addEvent = () => order.Apply(new AddItem
            {
                Quantity    = 1,
                ProductName = "Penny candy",
                Price       = .01m
            });

            Enumerable.Range(1, 100).ForEach(_ => addEvent());
            var repository = new SqlEventSourcedRepository <Order>(new FakeEventBus());
            await repository.Save(order);

            // queue the catchup on a background task
#pragma warning disable 4014
            // don't await
            Task.Run(() =>
#pragma warning restore 4014
            {
                var projector = new Projector1
                {
                    OnUpdate = (work, e) =>
                    {
                        if (e.SequenceNumber == 10)
                        {
                            Console.WriteLine("pausing read model catchup");
                            barrier.SignalAndWait(MaxWaitTime); //1
                            barrier.SignalAndWait(MaxWaitTime); //2
                            Console.WriteLine("resuming read model catchup");
                        }
                    }
                };

                using (var db = EventStoreDbContext())
                    using (var catchup = CreateReadModelCatchup(projector))
                    {
                        var events = db.Events.Where(e => e.Id > HighestEventId);
                        Console.WriteLine(string.Format("starting read model catchup for {0} events", events.Count()));
                        catchup.Run().Wait();
                        Console.WriteLine("done with read model catchup");
                        barrier.SignalAndWait(MaxWaitTime); //3
                    }
            });

            Console.WriteLine("queued read model catchup task");
            barrier.SignalAndWait(MaxWaitTime); //1

            EventStoreDbContext().DisposeAfter(c =>
            {
                Console.WriteLine("adding one more event, bypassing read model tracking");
                c.Events.Add(new Order.ItemAdded
                {
                    AggregateId    = Guid.NewGuid(),
                    SequenceNumber = 1
                }.ToStorableEvent());
                c.SaveChanges();
                Console.WriteLine("done adding one more event");
            });

            barrier.SignalAndWait(MaxWaitTime); //2
            barrier.SignalAndWait(MaxWaitTime); //3

            // check that everything worked:
            var projector2    = new Projector1();
            var projectorName = ReadModelInfo.NameForProjector(projector2);
            using (var readModels = ReadModelDbContext())
            {
                var readModelInfo = readModels.Set <ReadModelInfo>().Single(i => i.Name == projectorName);

                readModelInfo.CurrentAsOfEventId.Should().Be(HighestEventId + 101);

                using (var catchup = CreateReadModelCatchup(projector2))
                {
                    await catchup.Run();
                }

                readModels.Entry(readModelInfo).Reload();
                readModelInfo.CurrentAsOfEventId.Should().Be(HighestEventId + 102);
            }
        }
Exemple #23
0
 public FakeEventSourcedRepository(SqlEventSourcedRepository<TAggregate> innerRepository)
 {
     this.innerRepository = innerRepository;
     OnSave = innerRepository.Save;
 }