From 0372fada824862626dbfc3bdab5987e7ea80bdd1 Mon Sep 17 00:00:00 2001 From: dsanz Date: Fri, 24 Mar 2017 12:37:44 +0100 Subject: [PATCH 1/3] IntegrationEventLogService refactoring --- .../Services}/IIntegrationEventLogService.cs | 5 +-- .../Services}/IntegrationEventLogService.cs | 34 ++++++++++--------- .../Controllers/CatalogController.cs | 20 ++++++----- src/Services/Catalog/Catalog.API/Startup.cs | 28 +++++++-------- 4 files changed, 46 insertions(+), 41 deletions(-) rename src/{Services/Catalog/Catalog.API/IntegrationEvents => BuildingBlocks/EventBus/IntegrationEventLogEF/Services}/IIntegrationEventLogService.cs (60%) rename src/{Services/Catalog/Catalog.API/IntegrationEvents => BuildingBlocks/EventBus/IntegrationEventLogEF/Services}/IntegrationEventLogService.cs (66%) diff --git a/src/Services/Catalog/Catalog.API/IntegrationEvents/IIntegrationEventLogService.cs b/src/BuildingBlocks/EventBus/IntegrationEventLogEF/Services/IIntegrationEventLogService.cs similarity index 60% rename from src/Services/Catalog/Catalog.API/IntegrationEvents/IIntegrationEventLogService.cs rename to src/BuildingBlocks/EventBus/IntegrationEventLogEF/Services/IIntegrationEventLogService.cs index 423a0eb98..ed1f74616 100644 --- a/src/Services/Catalog/Catalog.API/IntegrationEvents/IIntegrationEventLogService.cs +++ b/src/BuildingBlocks/EventBus/IntegrationEventLogEF/Services/IIntegrationEventLogService.cs @@ -1,14 +1,15 @@ using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events; using System; using System.Collections.Generic; +using System.Data.Common; using System.Linq; using System.Threading.Tasks; -namespace Catalog.API.IntegrationEvents +namespace Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF.Services { public interface IIntegrationEventLogService { - Task SaveEventAsync(IntegrationEvent @event); + Task SaveEventAsync(IntegrationEvent @event, DbTransaction transaction); Task MarkEventAsPublishedAsync(IntegrationEvent @event); } } diff --git a/src/Services/Catalog/Catalog.API/IntegrationEvents/IntegrationEventLogService.cs b/src/BuildingBlocks/EventBus/IntegrationEventLogEF/Services/IntegrationEventLogService.cs similarity index 66% rename from src/Services/Catalog/Catalog.API/IntegrationEvents/IntegrationEventLogService.cs rename to src/BuildingBlocks/EventBus/IntegrationEventLogEF/Services/IntegrationEventLogService.cs index e9859c5ae..bef74b452 100644 --- a/src/Services/Catalog/Catalog.API/IntegrationEvents/IntegrationEventLogService.cs +++ b/src/BuildingBlocks/EventBus/IntegrationEventLogEF/Services/IntegrationEventLogService.cs @@ -1,37 +1,39 @@ -using System; -using System.Collections.Generic; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Storage; +using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events; +using System.Data.Common; using System.Linq; using System.Threading.Tasks; -using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events; -using Microsoft.eShopOnContainers.Services.Catalog.API.Infrastructure; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Storage; -using Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF; -using Microsoft.EntityFrameworkCore.Infrastructure; +using System; -namespace Catalog.API.IntegrationEvents +namespace Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF.Services { public class IntegrationEventLogService : IIntegrationEventLogService { private readonly IntegrationEventLogContext _integrationEventLogContext; - private readonly CatalogContext _catalogContext; + private readonly DbConnection _dbConnection; - public IntegrationEventLogService(CatalogContext catalogContext) + public IntegrationEventLogService(DbConnection dbConnection) { - _catalogContext = catalogContext; + _dbConnection = dbConnection?? throw new ArgumentNullException("dbConnection"); _integrationEventLogContext = new IntegrationEventLogContext( new DbContextOptionsBuilder() - .UseSqlServer(catalogContext.Database.GetDbConnection()) + .UseSqlServer(_dbConnection) .ConfigureWarnings(warnings => warnings.Throw(RelationalEventId.QueryClientEvaluationWarning)) .Options); } - public Task SaveEventAsync(IntegrationEvent @event) + public Task SaveEventAsync(IntegrationEvent @event, DbTransaction transaction) { + if(transaction == null) + { + throw new ArgumentNullException("transaction", $"A {typeof(DbTransaction).FullName} is required as a pre-requisite to save the event."); + } + var eventLogEntry = new IntegrationEventLogEntry(@event); - // as a constraint this transaction has to be done together with a catalogContext transaction - _integrationEventLogContext.Database.UseTransaction(_catalogContext.Database.CurrentTransaction.GetDbTransaction()); + _integrationEventLogContext.Database.UseTransaction(transaction); _integrationEventLogContext.IntegrationEventLogs.Add(eventLogEntry); return _integrationEventLogContext.SaveChangesAsync(); diff --git a/src/Services/Catalog/Catalog.API/Controllers/CatalogController.cs b/src/Services/Catalog/Catalog.API/Controllers/CatalogController.cs index bbc30a772..f7b95a069 100644 --- a/src/Services/Catalog/Catalog.API/Controllers/CatalogController.cs +++ b/src/Services/Catalog/Catalog.API/Controllers/CatalogController.cs @@ -1,15 +1,17 @@ -using Catalog.API.IntegrationEvents; -using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Storage; +using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; -using Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF; +using Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF.Services; using Microsoft.eShopOnContainers.Services.Catalog.API.Infrastructure; using Microsoft.eShopOnContainers.Services.Catalog.API.IntegrationEvents.Events; using Microsoft.eShopOnContainers.Services.Catalog.API.Model; using Microsoft.eShopOnContainers.Services.Catalog.API.ViewModel; using Microsoft.Extensions.Options; +using System; using System.Collections.Generic; +using System.Data.Common; using System.Linq; using System.Threading.Tasks; @@ -21,14 +23,14 @@ namespace Microsoft.eShopOnContainers.Services.Catalog.API.Controllers private readonly CatalogContext _catalogContext; private readonly IOptionsSnapshot _settings; private readonly IEventBus _eventBus; - private readonly IIntegrationEventLogService _integrationEventLogService; + private readonly Func _integrationEventLogServiceFactory; - public CatalogController(CatalogContext Context, IOptionsSnapshot settings, IEventBus eventBus, IIntegrationEventLogService integrationEventLogService) + public CatalogController(CatalogContext Context, IOptionsSnapshot settings, IEventBus eventBus, Func integrationEventLogServiceFactory) { _catalogContext = Context; _settings = settings; _eventBus = eventBus; - _integrationEventLogService = integrationEventLogService; + _integrationEventLogServiceFactory = integrationEventLogServiceFactory; ((DbContext)Context).ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking; } @@ -152,20 +154,22 @@ namespace Microsoft.eShopOnContainers.Services.Catalog.API.Controllers var oldPrice = item.Price; item.Price = product.Price; var @event = new ProductPriceChangedIntegrationEvent(item.Id, item.Price, oldPrice); + var eventLogService = _integrationEventLogServiceFactory(_catalogContext.Database.GetDbConnection()); using (var transaction = _catalogContext.Database.BeginTransaction()) { _catalogContext.CatalogItems.Update(item); await _catalogContext.SaveChangesAsync(); - await _integrationEventLogService.SaveEventAsync(@event); + + await eventLogService.SaveEventAsync(@event, _catalogContext.Database.CurrentTransaction.GetDbTransaction()); transaction.Commit(); } _eventBus.Publish(@event); - await _integrationEventLogService.MarkEventAsPublishedAsync(@event); + await eventLogService.MarkEventAsPublishedAsync(@event); } return Ok(); diff --git a/src/Services/Catalog/Catalog.API/Startup.cs b/src/Services/Catalog/Catalog.API/Startup.cs index 1598eb5a0..4a6961ba3 100644 --- a/src/Services/Catalog/Catalog.API/Startup.cs +++ b/src/Services/Catalog/Catalog.API/Startup.cs @@ -1,6 +1,5 @@ namespace Microsoft.eShopOnContainers.Services.Catalog.API { - using global::Catalog.API.IntegrationEvents; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.EntityFrameworkCore; @@ -8,12 +7,14 @@ using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; using Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ; using Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF; + using Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF.Services; using Microsoft.eShopOnContainers.Services.Catalog.API.Infrastructure; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; - using System.Data.SqlClient; + using System; + using System.Data.Common; using System.Reflection; public class Startup @@ -39,23 +40,15 @@ public void ConfigureServices(IServiceCollection services) { - var sqlConnection = new SqlConnection(Configuration["ConnectionString"]); - services.AddDbContext(c => { - c.UseSqlServer(sqlConnection); + c.UseSqlServer(Configuration["ConnectionString"]); // Changing default behavior when client evaluation occurs to throw. // Default in EF Core would be to log a warning when client evaluation is performed. c.ConfigureWarnings(warnings => warnings.Throw(RelationalEventId.QueryClientEvaluationWarning)); //Check Client vs. Server evaluation: https://docs.microsoft.com/en-us/ef/core/querying/client-eval }); - services.AddDbContext(c => - { - c.UseSqlServer(sqlConnection, b => b.MigrationsAssembly("Catalog.API")); - c.ConfigureWarnings(warnings => warnings.Throw(RelationalEventId.QueryClientEvaluationWarning)); - }); - services.Configure(Configuration); // Add framework services. @@ -81,8 +74,9 @@ .AllowCredentials()); }); - services.AddTransient(); - + services.AddTransient>( + sp => (DbConnection c) => new IntegrationEventLogService(c)); + var serviceProvider = services.BuildServiceProvider(); var configuration = serviceProvider.GetRequiredService>().Value; services.AddSingleton(new EventBusRabbitMQ(configuration.EventBusConnection)); @@ -90,7 +84,7 @@ services.AddMvc(); } - public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, IntegrationEventLogContext integrationEventLogContext) + public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) { //Configure logs @@ -112,7 +106,11 @@ //Seed Data CatalogContextSeed.SeedAsync(app, loggerFactory) .Wait(); - + + var integrationEventLogContext = new IntegrationEventLogContext( + new DbContextOptionsBuilder() + .UseSqlServer(Configuration["ConnectionString"], b => b.MigrationsAssembly("Catalog.API")) + .Options); integrationEventLogContext.Database.Migrate(); } From f974d5ea865226c15eaea4bde51f02e52e2579c6 Mon Sep 17 00:00:00 2001 From: dsanz Date: Mon, 27 Mar 2017 10:31:25 +0200 Subject: [PATCH 2/3] Fix test after changes in Catalog Controller --- .../Services/IntegrationEventsScenarios.cs | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/test/Services/FunctionalTests/Services/IntegrationEventsScenarios.cs b/test/Services/FunctionalTests/Services/IntegrationEventsScenarios.cs index 57254db1f..8ed11be3a 100644 --- a/test/Services/FunctionalTests/Services/IntegrationEventsScenarios.cs +++ b/test/Services/FunctionalTests/Services/IntegrationEventsScenarios.cs @@ -43,7 +43,7 @@ namespace FunctionalTests.Services var itemToModify = basket.Items[2]; var oldPrice = itemToModify.UnitPrice; var newPrice = oldPrice + priceModification; - var pRes = await catalogClient.PostAsync(CatalogScenariosBase.Post.UpdateCatalogProduct, new StringContent(ChangePrice(itemToModify, newPrice), UTF8Encoding.UTF8, "application/json")); + var pRes = await catalogClient.PostAsync(CatalogScenariosBase.Post.UpdateCatalogProduct, new StringContent(ChangePrice(itemToModify, newPrice, originalCatalogProducts), UTF8Encoding.UTF8, "application/json")); var modifiedCatalogProducts = await GetCatalogAsync(catalogClient); @@ -100,14 +100,11 @@ namespace FunctionalTests.Services return JsonConvert.DeserializeObject>(items); } - private string ChangePrice(BasketItem itemToModify, decimal newPrice) + private string ChangePrice(BasketItem itemToModify, decimal newPrice, PaginatedItemsViewModel catalogProducts) { - var item = new CatalogItem() - { - Id = int.Parse(itemToModify.ProductId), - Price = newPrice - }; - return JsonConvert.SerializeObject(item); + var catalogProduct = catalogProducts.Data.Single(pr => pr.Id == int.Parse(itemToModify.ProductId)); + catalogProduct.Price = newPrice; + return JsonConvert.SerializeObject(catalogProduct); } private CustomerBasket ComposeBasket(string customerId, IEnumerable items) From d8d1e5a8b29d39a27ee1e954662a4fa919ef2d28 Mon Sep 17 00:00:00 2001 From: dsanz Date: Mon, 27 Mar 2017 11:55:54 +0200 Subject: [PATCH 3/3] Remove unused code --- src/Services/Catalog/Catalog.API/Startup.cs | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/src/Services/Catalog/Catalog.API/Startup.cs b/src/Services/Catalog/Catalog.API/Startup.cs index 593d5f577..7ebe47b44 100644 --- a/src/Services/Catalog/Catalog.API/Startup.cs +++ b/src/Services/Catalog/Catalog.API/Startup.cs @@ -41,9 +41,6 @@ public void ConfigureServices(IServiceCollection services) { - //Using the same SqlConnection for both DbContexts (CatalogContext and IntegrationEventLogContext) - //var sqlConnection = new SqlConnection(Configuration["ConnectionString"]); - services.AddDbContext(options => { options.UseSqlServer(Configuration["ConnectionString"], @@ -59,19 +56,6 @@ //Check Client vs. Server evaluation: https://docs.microsoft.com/en-us/ef/core/querying/client-eval }); - //services.AddDbContext(options => - //{ - // options.UseSqlServer(sqlConnection, - // sqlServerOptionsAction: sqlOptions => - // { - // sqlOptions.MigrationsAssembly(typeof(Startup).GetTypeInfo().Assembly.GetName().Name); - // //Configuring Connection Resiliency: https://docs.microsoft.com/en-us/ef/core/miscellaneous/connection-resiliency - // sqlOptions.EnableRetryOnFailure(maxRetryCount: 5, maxRetryDelay: TimeSpan.FromSeconds(30), errorNumbersToAdd: null); - // }); - - // options.ConfigureWarnings(warnings => warnings.Throw(RelationalEventId.QueryClientEvaluationWarning)); - //}); - services.Configure(Configuration); // Add framework services.