public void TestStopOnFirstBeforeHandlerThatHasAnError(bool stopOnFirst) { //SETUP var options = SqliteInMemory.CreateOptions <ExampleDbContext>(); var logs = new List <LogOutput>(); var config = new GenericEventRunnerConfig { StopOnFirstBeforeHandlerThatHasAnError = stopOnFirst }; var context = options.CreateAndSeedDbWithDiForHandlers <OrderCreatedHandler>(logs, config); { var tax = new TaxRate(DateTime.Now, 6); context.Add(tax); //ATTEMPT tax.AddEvent(new EventTestBeforeReturnError()); tax.AddEvent(new EventTestBeforeReturnError()); var ex = Assert.Throws <GenericEventRunnerStatusException>(() => context.SaveChanges()); //VERIFY context.StatusFromLastSaveChanges.IsValid.ShouldBeFalse(); context.StatusFromLastSaveChanges.Errors.Count.ShouldEqual(stopOnFirst ? 1 : 2); logs.Count.ShouldEqual(stopOnFirst ? 1 : 4); } }
public void TestAddBookAddedActionToUpdateDates() { //SETUP var options = SqliteInMemory.CreateOptions <ExampleDbContext>(); var config = new GenericEventRunnerConfig(); config.AddActionToRunAfterDetectChanges <ExampleDbContext>(localContext => { foreach (var entity in localContext.ChangeTracker.Entries() .Where(e => e.State == EntityState.Added || e.State == EntityState.Modified)) { var tracked = entity.Entity as ICreatedUpdated; tracked?.LogChange(entity.State == EntityState.Added, entity); } }); var context = options.CreateAndSeedDbWithDiForHandlers <OrderCreatedHandler>(null, config); { //ATTEMPT var book = Book.CreateBookWithEvent("test"); context.Add(book); context.SaveChanges(); book.WhenCreatedUtc.Subtract(DateTime.UtcNow).TotalMilliseconds.ShouldBeInRange(-100, 10); Thread.Sleep(1000); book.ChangeTitle("new title"); context.SaveChanges(); //VERIFY book.LastUpdatedUtc.Subtract(DateTime.UtcNow).TotalMilliseconds.ShouldBeInRange(-100, 10); } }
public void TestBookDbContextAlterAuthorOk() { //SETUP var options = SqliteInMemory.CreateOptions <BookDbContext>(); var eventConfig = new GenericEventRunnerConfig { NotUsingDuringSaveHandlers = true }; using var context = options.CreateDbWithDiForHandlers <BookDbContext, ReviewAddedHandler>(null, eventConfig); context.Database.EnsureCreated(); var books = context.SeedDatabaseFourBooks(); //ATTEMPT books[0].AuthorsLink.Single().Author.Name = "Test Name"; context.SaveChanges(); //VERIFY context.ChangeTracker.Clear(); var readBooks = context.Books.ToList(); readBooks[0].AuthorsOrdered.ShouldEqual("Test Name"); readBooks[1].AuthorsOrdered.ShouldEqual("Test Name"); readBooks[2].AuthorsOrdered.ShouldNotEqual("Test Name"); readBooks[3].AuthorsOrdered.ShouldNotEqual("Test Name"); }
public void TestRegisterEventHandlersOnlyBefore() { //SETUP var services = new ServiceCollection(); var config = new GenericEventRunnerConfig(); //ATTEMPT var logs = services.RegisterGenericEventRunner(config, Assembly.GetAssembly(typeof(BeforeHandler)) ); //VERIFY services.Contains(new ServiceDescriptor(typeof(IBeforeSaveEventHandler <OrderCreatedEvent>), typeof(BeforeHandler), ServiceLifetime.Transient), new ServiceDescriptorIncludeLifeTimeCompare()).ShouldBeTrue(); foreach (var log in logs) { _output.WriteLine(log); } config.NotUsingDuringSaveHandlers.ShouldBeTrue(); config.NotUsingAfterSaveHandlers.ShouldBeTrue(); services.Count.ShouldEqual(3); }
public void TestBookDbContextAddReviewConcurrencyOk() { //SETUP var options = SqliteInMemory.CreateOptions <BookDbContext>(); var eventConfig = new GenericEventRunnerConfig { NotUsingDuringSaveHandlers = true }; using var context = options.CreateDbWithDiForHandlers <BookDbContext, ReviewAddedHandler>(null, eventConfig); context.Database.EnsureCreated(); var books = context.SeedDatabaseFourBooks(); //ATTEMPT books[0].AddReview(3, "comment", "me"); context.Database.ExecuteSqlInterpolated( $"UPDATE Books SET ReviewsCount = 2 WHERE BookId = {books[0].BookId}"); try { context.SaveChanges(); } catch (Exception e) { var status = e.HandleCacheValuesConcurrency(context); status.IsValid.ShouldBeTrue(status.GetAllErrors()); context.SaveChanges(); } //VERIFY context.ChangeTracker.Clear(); var book = context.Books.Include(x => x.Reviews).Single(x => x.BookId == books[0].BookId); book.ReviewsCount.ShouldEqual(3); book.ReviewsAverageVotes.ShouldEqual(1); }
public void TestRegisterEventHandlersWithConfigTuningOffDuringHandlers() { //SETUP var services = new ServiceCollection(); var config = new GenericEventRunnerConfig { NotUsingDuringSaveHandlers = true }; //ATTEMPT services.RegisterGenericEventRunner(config, Assembly.GetAssembly(typeof(BeforeHandlerThrowsExceptionWithAttribute))); //VERIFY services.Contains(new ServiceDescriptor(typeof(IBeforeSaveEventHandler <EventCircularEvent>), typeof(BeforeHandlerCircularEvent), ServiceLifetime.Transient), new ServiceDescriptorCompare()).ShouldBeTrue(); services.Contains(new ServiceDescriptor(typeof(IBeforeSaveEventHandler <EventTestExceptionHandlerWithAttribute>), typeof(BeforeHandlerThrowsExceptionWithAttribute), ServiceLifetime.Scoped), new ServiceDescriptorCompare()).ShouldBeTrue(); services.Contains(new ServiceDescriptor(typeof(IAfterSaveEventHandler <EventTestAfterExceptionHandler>), typeof(AfterHandlerThrowsException), ServiceLifetime.Transient), new ServiceDescriptorCompare()).ShouldBeTrue(); services.Contains(new ServiceDescriptor(typeof(IAfterSaveEventHandlerAsync <EventDoNothing>), typeof(AfterHandlerDoNothingAsync), ServiceLifetime.Transient), new ServiceDescriptorCompare()).ShouldBeTrue(); //During event handlers services.Contains(new ServiceDescriptor(typeof(IDuringSaveEventHandler <EventDoNothing>), typeof(DuringHandlerDoNothing), ServiceLifetime.Transient), new ServiceDescriptorCompare()).ShouldBeFalse(); }
public void TestBookDbContextAlterAuthorOk() { //SETUP var options = SqliteInMemory.CreateOptions <BookDbContext>(); var eventConfig = new GenericEventRunnerConfig { NotUsingDuringSaveHandlers = true }; using var context = options.CreateDbWithDiForHandlers <BookDbContext, ReviewAddedHandler>(null, eventConfig); context.Database.EnsureCreated(); var books = context.SeedDatabaseFourBooks(); //ATTEMPT books[3].AuthorsLink.Single().Author.Name = "Test Name"; context.Database.ExecuteSqlInterpolated( $"UPDATE Books SET AuthorsOrdered = 'bad name' WHERE BookId = {books[3].BookId}"); try { context.SaveChanges(); } catch (Exception e) { var status = e.HandleCacheValuesConcurrency(context); status.IsValid.ShouldBeTrue(status.GetAllErrors()); context.SaveChanges(); } //VERIFY context.ChangeTracker.Clear(); var readBook = context.Books.Single(x => x.BookId == books[3].BookId); readBook.AuthorsOrdered.ShouldEqual("Test Name"); }
public void TestAddReviewConcurrencyFixed() { //SETUP var options = SqliteInMemory.CreateOptions <SqlEventsDbContext>(); var config = new GenericEventRunnerConfig(); config.RegisterSaveChangesExceptionHandler <SqlEventsDbContext>(BookWithEventsConcurrencyHandler.HandleCacheValuesConcurrency); var context = options.CreateDbWithDiForHandlers <SqlEventsDbContext, ReviewAddedHandler>(config: config); context.Database.EnsureCreated(); var book = WithEventsEfTestData.CreateDummyBookOneAuthor(); context.Add(book); context.SaveChanges(); book.AddReview(4, "OK", "me"); //This simulates adding a review with NumStars of 2 before the AddReview context.Database.ExecuteSqlRaw( "UPDATE Books SET ReviewsCount = @p0, ReviewsAverageVotes = @p1 WHERE BookId = @p2", 1, 2, book.BookId); //ATTEMPT context.SaveChanges(); //VERIFY var foundBook = context.Find <BookWithEvents>(book.BookId); foundBook.ReviewsCount.ShouldEqual(2); foundBook.ReviewsAverageVotes.ShouldEqual(6.0 / 2.0); }
public void TestRegisterEventHandlersBeforeAlreadyRegisteredThreeProjects() { //SETUP var services = new ServiceCollection(); var config = new GenericEventRunnerConfig(); services.AddTransient <IBeforeSaveEventHandler <OrderCreatedEvent>, BeforeHandler>(); //ATTEMPT var logs = services.RegisterGenericEventRunner(config, Assembly.GetAssembly(typeof(BeforeHandler)), Assembly.GetAssembly(typeof(DuringHandler)), Assembly.GetAssembly(typeof(AfterHandler)) ); //VERIFY services.Contains(new ServiceDescriptor(typeof(IBeforeSaveEventHandler <OrderCreatedEvent>), typeof(BeforeHandler), ServiceLifetime.Transient), new ServiceDescriptorIncludeLifeTimeCompare()).ShouldBeTrue(); services.Contains(new ServiceDescriptor(typeof(IDuringSaveEventHandler <NewBookEvent>), typeof(DuringHandler), ServiceLifetime.Transient), new ServiceDescriptorIncludeLifeTimeCompare()).ShouldBeTrue(); services.Contains(new ServiceDescriptor(typeof(IAfterSaveEventHandler <OrderReadyToDispatchEvent>), typeof(AfterHandler), ServiceLifetime.Transient), new ServiceDescriptorIncludeLifeTimeCompare()).ShouldBeTrue(); foreach (var log in logs) { _output.WriteLine(log); } config.NotUsingDuringSaveHandlers.ShouldBeFalse(); config.NotUsingAfterSaveHandlers.ShouldBeFalse(); services.Count.ShouldEqual(5); }
public void TestBookDeletedConcurrencyFixed() { //SETUP var options = SqliteInMemory.CreateOptions <SqlEventsDbContext>(); var config = new GenericEventRunnerConfig(); config.RegisterSaveChangesExceptionHandler <SqlEventsDbContext>(BookWithEventsConcurrencyHandler.HandleCacheValuesConcurrency); var context = options.CreateDbWithDiForHandlers <SqlEventsDbContext, ReviewAddedHandler>(config: config); context.Database.EnsureCreated(); var books = WithEventsEfTestData.CreateDummyBooks(2); context.AddRange(books); context.SaveChanges(); //ATTEMPT books.First().AuthorsLink.Last().Author.ChangeName("New common name"); //This simulates changing the AuthorsOrdered value context.Database.ExecuteSqlRaw("DELETE FROM Books WHERE BookId = @p0", books.First().BookId); //ATTEMPT context.SaveChanges(); //VERIFY var readBooks = context.Books.ToList(); readBooks.Count().ShouldEqual(1); readBooks.First().AuthorsOrdered.ShouldEqual("Author0001, New common name"); }
// This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) //#A { services.AddControllersWithViews() //#B //.AddRazorRuntimeCompilation() //This recompile a razor page if you edit it while the app is running //Added this because my logs display needs the enum as a string .AddJsonOptions(opts => { opts.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter()); }); var bookAppSettings = Configuration.GetBookAppSettings(); services.AddSingleton(bookAppSettings); //This gets the correct sql connection string based on the BookAppSettings var sqlConnection = Configuration.GetCorrectSqlConnection(bookAppSettings); //This registers both DbContext. Each MUST have a unique MigrationsHistoryTable for Migrations to work services.AddDbContext <BookDbContext>( options => options.UseSqlServer(sqlConnection, dbOptions => dbOptions.MigrationsHistoryTable("BookMigrationHistoryName"))); services.AddDbContext <OrderDbContext>( options => options.UseSqlServer(sqlConnection, dbOptions => dbOptions.MigrationsHistoryTable("OrderMigrationHistoryName"))); services.AddHttpContextAccessor(); services.Configure <BookAppSettings>(options => Configuration.GetSection(nameof(BookAppSettings)).Bind(options)); services.AddSingleton <IMenuBuilder, MenuBuilder>(); //This registers all the services across all the projects in this application var diLogs = services.RegisterAssemblyPublicNonGenericClasses( Assembly.GetAssembly(typeof(IDisplayOrdersService)), Assembly.GetAssembly(typeof(IPlaceOrderBizLogic)), Assembly.GetAssembly(typeof(IPlaceOrderDbAccess))) .AsPublicImplementedInterfaces(); //BookApp.Books startup var test = services.RegisterBooksServices(Configuration); //Register EfCore.GenericEventRunner var eventConfig = new GenericEventRunnerConfig(); eventConfig.RegisterSaveChangesExceptionHandler <BookDbContext>(BookWithEventsConcurrencyHandler.HandleCacheValuesConcurrency); eventConfig.AddActionToRunAfterDetectChanges <BookDbContext>(BookDetectChangesExtensions.ChangeChecker); var logs = services.RegisterGenericEventRunner(eventConfig, Assembly.GetAssembly(typeof(ReviewAddedHandler)) //SQL cached values event handlers ); //Register EfCoreGenericServices services.ConfigureGenericServicesEntities(typeof(BookDbContext)) .ScanAssemblesForDtos( BooksStartupInfo.GenericServiceAssemblies.ToArray() ).RegisterGenericServices(); var softLogs = services.RegisterSoftDelServicesAndYourConfigurations(); }
public static void RegisterInfrastructureDi(this IServiceCollection services) { //This provides a SaveChangesExceptionHandler which handles concurrency issues around ReviewsCount and ReviewsAverageVotes var config = new GenericEventRunnerConfig(); config.RegisterSaveChangesExceptionHandler <SqlEventsDbContext>(BookWithEventsConcurrencyHandler.HandleCacheValuesConcurrency); //Because I haven't provided any assemblies this will scan this assembly for event handlers services.RegisterGenericEventRunner(config); }
public void TestGenericEventRunnerConfigAddActionToRunAfterDetectChangeOk() { //SETUP var options = SqliteInMemory.CreateOptions <BookDbContext>(); var eventConfig = new GenericEventRunnerConfig(); eventConfig.AddActionToRunAfterDetectChanges <BookDbContext>(localContext => localContext.ChangeChecker()); using var context = options.CreateDbWithDiForHandlers <BookDbContext, ReviewAddedHandler>(null, eventConfig); context.Database.EnsureCreated(); //ATTEMPT var books = context.SeedDatabaseFourBooks(); //VERIFY var timeNow = DateTime.UtcNow; books.ForEach(x => x.LastUpdatedUtc.ShouldBeInRange(DateTime.UtcNow.AddMilliseconds(-500), timeNow)); books.ForEach(x => x.NotUpdatedYet.ShouldBeTrue()); }
public void TestAddSaveChangesExceptionHandlerButStillFailsOnOtherDbExceptions() { //SETUP var options = SqliteInMemory.CreateOptions <SqlEventsDbContext>(); var config = new GenericEventRunnerConfig(); config.RegisterSaveChangesExceptionHandler <SqlEventsDbContext>(BookWithEventsConcurrencyHandler.HandleCacheValuesConcurrency); var context = options.CreateDbWithDiForHandlers <SqlEventsDbContext, ReviewAddedHandler>(config: config); context.Database.EnsureCreated(); var review = new ReviewWithEvents(1, "hello", "Me", Guid.NewGuid()); context.Add(review); //ATTEMPT var ex = Assert.Throws <DbUpdateException>(() => context.SaveChanges()); //VERIFY ex.InnerException.Message.ShouldEqual("SQLite Error 19: 'FOREIGN KEY constraint failed'."); }
// This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) //#A { services.AddControllersWithViews() //#B //.AddRazorRuntimeCompilation() //This recompile a razor page if you edit it while the app is running //Added this because my logs display needs the enum as a string .AddJsonOptions(opts => { opts.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter()); }); var connectionString = Configuration.GetConnectionString("DefaultConnection"); //This registers both DbContext. Each MUST have a unique MigrationsHistoryTable for Migrations to work services.AddDbContext <BookDbContext>( options => options.UseSqlServer(connectionString, dbOptions => dbOptions.MigrationsHistoryTable("BookMigrationHistoryName"))); services.AddHttpContextAccessor(); //ModMod.Books startup var test = services.RegisterBooksServices(Configuration); //Register EfCore.GenericEventRunner var eventConfig = new GenericEventRunnerConfig(); eventConfig.RegisterSaveChangesExceptionHandler <BookDbContext>(BookWithEventsConcurrencyHandler.HandleCacheValuesConcurrency); eventConfig.AddActionToRunAfterDetectChanges <BookDbContext>(BookDetectChangesExtensions.ChangeChecker); var logs = services.RegisterGenericEventRunner(eventConfig, Assembly.GetAssembly(typeof(ReviewAddedHandler)) //SQL cached values event handlers ); //Register EfCoreGenericServices services.ConfigureGenericServicesEntities(typeof(BookDbContext)) .ScanAssemblesForDtos( BooksStartupInfo.GenericServiceAssemblies.ToArray() ).RegisterGenericServices(); var softLogs = services.RegisterSoftDelServicesAndYourConfigurations(); }
public async Task TestUpdateProductStockConcurrencyWithHandlerAsync() { //SETUP var options = SqliteInMemory.CreateOptions <ExampleDbContext>(); var config = new GenericEventRunnerConfig(); config.RegisterSaveChangesExceptionHandler <ExampleDbContext>(CatchAndFixConcurrencyException); var context = options.CreateAndSeedDbWithDiForHandlers <OrderCreatedHandler>(config: config); { var stock = context.ProductStocks.OrderBy(x => x.NumInStock).First(); //ATTEMPT stock.NumAllocated = 2; context.Database.ExecuteSqlRaw( "UPDATE ProductStocks SET NumAllocated = @p0 WHERE ProductName = @p1", 3, stock.ProductName); await context.SaveChangesAsync(); //VERIFY var foundStock = context.Find <ProductStock>(stock.ProductName); foundStock.NumAllocated.ShouldEqual(5); } }
public async Task TestWriteBooksAsyncCheckAddUpdateDates() { //SETUP var fileDir = Path.Combine(TestData.GetTestDataDir()); var options = SqliteInMemory.CreateOptions <BookDbContext>(); options.TurnOffDispose(); var timeNow = DateTime.UtcNow; var eventConfig = new GenericEventRunnerConfig { NotUsingDuringSaveHandlers = true }; eventConfig.AddActionToRunAfterDetectChanges <BookDbContext>(localContext => localContext.ChangeChecker()); using var context = options.CreateDbWithDiForHandlers <BookDbContext, ReviewAddedHandler>(null, eventConfig); context.Database.EnsureCreated(); await context.SeedDatabaseWithBooksAsync(fileDir); //The LastUpdatedUtc is set via events context.ChangeTracker.Clear(); //ATTEMPT var serviceProvider = BuildServiceProvider(options); var generator = new BookGenerator(serviceProvider); await generator.WriteBooksAsync(fileDir, false, 20, true, default); //VERIFY var books = context.Books .Include(x => x.Reviews) .Include(x => x.AuthorsLink).ToList(); books.All(x => x.LastUpdatedUtc >= timeNow).ShouldBeTrue(); books.SelectMany(x => x.Reviews).All(x => x.LastUpdatedUtc >= timeNow).ShouldBeTrue(); books.SelectMany(x => x.AuthorsLink).All(x => x.LastUpdatedUtc >= timeNow).ShouldBeTrue(); options.ManualDispose(); }
public void TestBookDbContextRemoveReviewCacheUpdatedOk() { //SETUP var options = SqliteInMemory.CreateOptions <BookDbContext>(); var eventConfig = new GenericEventRunnerConfig { NotUsingDuringSaveHandlers = true }; using var context = options.CreateDbWithDiForHandlers <BookDbContext, ReviewAddedHandler>(null, eventConfig); context.Database.EnsureCreated(); var books = context.SeedDatabaseFourBooks(); //ATTEMPT books[3].RemoveReview(1); context.SaveChanges(); //VERIFY context.ChangeTracker.Clear(); var book = context.Books.Include(x => x.Reviews).Single(x => x.BookId == books[3].BookId); book.ReviewsCount.ShouldEqual(1); book.ReviewsAverageVotes.ShouldEqual(3); }
public async Task TestStopOnFirstBeforeHandlerThatHasAnError(bool stopOnFirst) { //SETUP var options = SqliteInMemory.CreateOptions <ExampleDbContext>(); var config = new GenericEventRunnerConfig { StopOnFirstBeforeHandlerThatHasAnError = stopOnFirst }; var context = options.CreateAndSeedDbWithDiForHandlers <OrderCreatedHandler>(config: config); { var tax = new TaxRate(DateTime.Now, 6); context.Add(tax); //ATTEMPT tax.AddEvent(new EventTestBeforeReturnError()); tax.AddEvent(new EventTestBeforeReturnError()); var status = await context.SaveChangesWithStatusAsync(); //VERIFY status.IsValid.ShouldBeFalse(); status.Errors.Count.ShouldEqual(stopOnFirst ? 1 : 2); context.StatusFromLastSaveChanges.Errors.Count.ShouldEqual(stopOnFirst ? 1 : 2); } }
// This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) //#A { services.AddControllersWithViews() //#B .AddRazorRuntimeCompilation() //This recompile a razor page if you edit it while the app is running //Added this because my logs display needs the enum as a string .AddJsonOptions(opts => { opts.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter()); }); var bookAppSettings = Configuration.GetBookAppSettings(); services.AddSingleton(bookAppSettings); //This gets the correct sql connection string based on the BookAppSettings var sqlConnection = Configuration.GetCorrectSqlConnection(bookAppSettings); //This registers both DbContext. Each MUST have a unique MigrationsHistoryTable for Migrations to work services.AddDbContext <BookDbContext>( options => options.UseSqlServer(sqlConnection, dbOptions => dbOptions.MigrationsHistoryTable("BookMigrationHistoryName"))); services.AddDbContext <OrderDbContext>( options => options.UseSqlServer(sqlConnection, dbOptions => dbOptions.MigrationsHistoryTable("OrderMigrationHistoryName"))); var cosmosSettings = Configuration.GetCosmosDbSettings(bookAppSettings); if (cosmosSettings != null) { services.AddDbContext <CosmosDbContext>(options => options.UseCosmos( cosmosSettings.ConnectionString, cosmosSettings.DatabaseName)); } else { services.AddSingleton <CosmosDbContext>(_ => null); } services.AddHttpContextAccessor(); services.Configure <BookAppSettings>(options => Configuration.GetSection(nameof(BookAppSettings)).Bind(options)); services.AddSingleton <IMenuBuilder, MenuBuilder>(); //This registers all the services across all the projects in this application var diLogs = services.RegisterAssemblyPublicNonGenericClasses( Assembly.GetAssembly(typeof(ICheckFixCacheValuesService)), Assembly.GetAssembly(typeof(BookListDto)), Assembly.GetAssembly(typeof(IBookToCosmosBookService)), Assembly.GetAssembly(typeof(IBookGenerator)), Assembly.GetAssembly(typeof(IPlaceOrderBizLogic)), Assembly.GetAssembly(typeof(IPlaceOrderDbAccess)), Assembly.GetAssembly(typeof(IListBooksCachedService)), Assembly.GetAssembly(typeof(ICosmosEfListNoSqlBooksService)), Assembly.GetAssembly(typeof(IListBooksService)), Assembly.GetAssembly(typeof(IDisplayOrdersService)), Assembly.GetAssembly(typeof(IListUdfsBooksService)), Assembly.GetAssembly(typeof(IListUdfsBooksService)) ) .AsPublicImplementedInterfaces(); services.AddHostedService <CheckFixCacheBackground>(); //Register EfCore.GenericEventRunner var eventConfig = new GenericEventRunnerConfig { NotUsingDuringSaveHandlers = cosmosSettings == null //This stops any attempts to update cosmos db if not turned on }; eventConfig.RegisterSaveChangesExceptionHandler <BookDbContext>(BookWithEventsConcurrencyHandler.HandleCacheValuesConcurrency); eventConfig.AddActionToRunAfterDetectChanges <BookDbContext>(BookDetectChangesExtensions.ChangeChecker); var logs = services.RegisterGenericEventRunner(eventConfig, Assembly.GetAssembly(typeof(ReviewAddedHandler)), //SQL cached values event handlers Assembly.GetAssembly(typeof(BookChangeHandlerAsync)) //Cosmos Db event handlers ); //Register EfCoreGenericServices services.ConfigureGenericServicesEntities(typeof(BookDbContext), typeof(OrderDbContext)) .ScanAssemblesForDtos( Assembly.GetAssembly(typeof(BookListDto)), Assembly.GetAssembly(typeof(AddReviewDto)) ).RegisterGenericServices(); var softLogs = services.RegisterSoftDelServicesAndYourConfigurations(); }