Add shared scope transaction between updating catalog product priceand store ProductPriceChangedIntegrationEvent. Added service to encapsulate logic for storage of integration event logs.
This commit is contained in:
		
							parent
							
								
									dee6ea7342
								
							
						
					
					
						commit
						6e4d9461de
					
				| @ -8,6 +8,7 @@ namespace Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF | |||||||
| { | { | ||||||
|     public class IntegrationEventLogEntry |     public class IntegrationEventLogEntry | ||||||
|     { |     { | ||||||
|  |         private IntegrationEventLogEntry() { } | ||||||
|         public IntegrationEventLogEntry(IntegrationEvent @event) |         public IntegrationEventLogEntry(IntegrationEvent @event) | ||||||
|         { |         { | ||||||
|             EventId = @event.Id; |             EventId = @event.Id; | ||||||
|  | |||||||
| @ -1,5 +1,7 @@ | |||||||
| using Microsoft.AspNetCore.Mvc; | using Catalog.API.IntegrationEvents; | ||||||
|  | using Microsoft.AspNetCore.Mvc; | ||||||
| using Microsoft.EntityFrameworkCore; | using Microsoft.EntityFrameworkCore; | ||||||
|  | using Microsoft.EntityFrameworkCore.Storage; | ||||||
| using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; | using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; | ||||||
| using Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF; | using Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF; | ||||||
| using Microsoft.eShopOnContainers.Services.Catalog.API.Infrastructure; | using Microsoft.eShopOnContainers.Services.Catalog.API.Infrastructure; | ||||||
| @ -17,18 +19,18 @@ namespace Microsoft.eShopOnContainers.Services.Catalog.API.Controllers | |||||||
|     public class CatalogController : ControllerBase |     public class CatalogController : ControllerBase | ||||||
|     { |     { | ||||||
|         private readonly CatalogContext _catalogContext; |         private readonly CatalogContext _catalogContext; | ||||||
|         private readonly IntegrationEventLogContext _integrationEventLogContext; |  | ||||||
|         private readonly IOptionsSnapshot<Settings> _settings; |         private readonly IOptionsSnapshot<Settings> _settings; | ||||||
|         private readonly IEventBus _eventBus; |         private readonly IEventBus _eventBus; | ||||||
|  |         private readonly IIntegrationEventLogService _integrationEventLogService; | ||||||
| 
 | 
 | ||||||
|         public CatalogController(CatalogContext catalogContext, IntegrationEventLogContext integrationEventLogContext, IOptionsSnapshot<Settings> settings, IEventBus eventBus) |         public CatalogController(CatalogContext Context, IOptionsSnapshot<Settings> settings, IEventBus eventBus, IIntegrationEventLogService integrationEventLogService) | ||||||
|         { |         { | ||||||
|             _catalogContext = catalogContext; |             _catalogContext = Context; | ||||||
|             _integrationEventLogContext = integrationEventLogContext; |  | ||||||
|             _settings = settings; |             _settings = settings; | ||||||
|             _eventBus = eventBus; |             _eventBus = eventBus; | ||||||
|  |             _integrationEventLogService = integrationEventLogService; | ||||||
| 
 | 
 | ||||||
|             ((DbContext)catalogContext).ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking; |             ((DbContext)Context).ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         // GET api/v1/[controller]/items[?pageSize=3&pageIndex=10] |         // GET api/v1/[controller]/items[?pageSize=3&pageIndex=10] | ||||||
| @ -149,21 +151,21 @@ namespace Microsoft.eShopOnContainers.Services.Catalog.API.Controllers | |||||||
|             { |             { | ||||||
|                 var oldPrice = item.Price; |                 var oldPrice = item.Price; | ||||||
|                 item.Price = product.Price; |                 item.Price = product.Price; | ||||||
|                 _catalogContext.CatalogItems.Update(item); |  | ||||||
| 
 |  | ||||||
|                 var @event = new ProductPriceChangedIntegrationEvent(item.Id, item.Price, oldPrice); |                 var @event = new ProductPriceChangedIntegrationEvent(item.Id, item.Price, oldPrice); | ||||||
|                 var eventLogEntry = new IntegrationEventLogEntry(@event);                 |  | ||||||
|                 _integrationEventLogContext.IntegrationEventLogs.Add(eventLogEntry);                 |  | ||||||
| 
 | 
 | ||||||
|                 await _integrationEventLogContext.SaveChangesAsync(); |                 using (var transaction = _catalogContext.Database.BeginTransaction()) | ||||||
|                 await _catalogContext.SaveChangesAsync(); |                 { | ||||||
|                  |                     _catalogContext.CatalogItems.Update(item); | ||||||
|  |                     await _catalogContext.SaveChangesAsync(); | ||||||
|  | 
 | ||||||
|  |                     await _integrationEventLogService.SaveEventAsync(@event); | ||||||
|  | 
 | ||||||
|  |                     transaction.Commit(); | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|                 _eventBus.Publish(@event); |                 _eventBus.Publish(@event); | ||||||
|                  | 
 | ||||||
|                 eventLogEntry.TimesSent++; |                 await _integrationEventLogService.MarkEventAsPublishedAsync(@event);                | ||||||
|                 eventLogEntry.State = EventStateEnum.Published; |  | ||||||
|                 _integrationEventLogContext.IntegrationEventLogs.Update(eventLogEntry); |  | ||||||
|                 await _integrationEventLogContext.SaveChangesAsync(); |  | ||||||
|             }          |             }          | ||||||
| 
 | 
 | ||||||
|             return Ok(); |             return Ok(); | ||||||
|  | |||||||
| @ -0,0 +1,14 @@ | |||||||
|  | using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events; | ||||||
|  | using System; | ||||||
|  | using System.Collections.Generic; | ||||||
|  | using System.Linq; | ||||||
|  | using System.Threading.Tasks; | ||||||
|  | 
 | ||||||
|  | namespace Catalog.API.IntegrationEvents | ||||||
|  | { | ||||||
|  |     public interface IIntegrationEventLogService | ||||||
|  |     { | ||||||
|  |         Task SaveEventAsync(IntegrationEvent @event); | ||||||
|  |         Task MarkEventAsPublishedAsync(IntegrationEvent @event); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -0,0 +1,51 @@ | |||||||
|  | using System; | ||||||
|  | using System.Collections.Generic; | ||||||
|  | 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; | ||||||
|  | 
 | ||||||
|  | namespace Catalog.API.IntegrationEvents | ||||||
|  | { | ||||||
|  |     public class IntegrationEventLogService : IIntegrationEventLogService | ||||||
|  |     { | ||||||
|  |         private readonly IntegrationEventLogContext _integrationEventLogContext; | ||||||
|  |         private readonly CatalogContext _catalogContext; | ||||||
|  | 
 | ||||||
|  |         public IntegrationEventLogService(CatalogContext catalogContext) | ||||||
|  |         { | ||||||
|  |             _catalogContext = catalogContext; | ||||||
|  |             _integrationEventLogContext = new IntegrationEventLogContext( | ||||||
|  |                 new DbContextOptionsBuilder<IntegrationEventLogContext>() | ||||||
|  |                     .UseSqlServer(catalogContext.Database.GetDbConnection()) | ||||||
|  |                     .ConfigureWarnings(warnings => warnings.Throw(RelationalEventId.QueryClientEvaluationWarning)) | ||||||
|  |                     .Options); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public Task SaveEventAsync(IntegrationEvent @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.IntegrationEventLogs.Add(eventLogEntry); | ||||||
|  | 
 | ||||||
|  |             return _integrationEventLogContext.SaveChangesAsync(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public Task MarkEventAsPublishedAsync(IntegrationEvent @event) | ||||||
|  |         { | ||||||
|  |             var eventLogEntry = _integrationEventLogContext.IntegrationEventLogs.Single(ie => ie.EventId == @event.Id); | ||||||
|  |             eventLogEntry.TimesSent++; | ||||||
|  |             eventLogEntry.State = EventStateEnum.Published; | ||||||
|  | 
 | ||||||
|  |             _integrationEventLogContext.IntegrationEventLogs.Update(eventLogEntry); | ||||||
|  | 
 | ||||||
|  |             return _integrationEventLogContext.SaveChangesAsync(); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -1,5 +1,6 @@ | |||||||
| namespace Microsoft.eShopOnContainers.Services.Catalog.API | namespace Microsoft.eShopOnContainers.Services.Catalog.API | ||||||
| { | { | ||||||
|  |     using global::Catalog.API.IntegrationEvents; | ||||||
|     using Microsoft.AspNetCore.Builder; |     using Microsoft.AspNetCore.Builder; | ||||||
|     using Microsoft.AspNetCore.Hosting; |     using Microsoft.AspNetCore.Hosting; | ||||||
|     using Microsoft.EntityFrameworkCore; |     using Microsoft.EntityFrameworkCore; | ||||||
| @ -12,6 +13,7 @@ | |||||||
|     using Microsoft.Extensions.DependencyInjection; |     using Microsoft.Extensions.DependencyInjection; | ||||||
|     using Microsoft.Extensions.Logging; |     using Microsoft.Extensions.Logging; | ||||||
|     using Microsoft.Extensions.Options; |     using Microsoft.Extensions.Options; | ||||||
|  |     using System.Data.SqlClient; | ||||||
|     using System.Reflection; |     using System.Reflection; | ||||||
| 
 | 
 | ||||||
|     public class Startup |     public class Startup | ||||||
| @ -37,9 +39,11 @@ | |||||||
| 
 | 
 | ||||||
|         public void ConfigureServices(IServiceCollection services) |         public void ConfigureServices(IServiceCollection services) | ||||||
|         { |         { | ||||||
|  |             var sqlConnection = new SqlConnection(Configuration["ConnectionString"]); | ||||||
|  | 
 | ||||||
|             services.AddDbContext<CatalogContext>(c => |             services.AddDbContext<CatalogContext>(c => | ||||||
|             { |             { | ||||||
|                 c.UseSqlServer(Configuration["ConnectionString"]); |                 c.UseSqlServer(sqlConnection); | ||||||
|                 // Changing default behavior when client evaluation occurs to throw.  |                 // Changing default behavior when client evaluation occurs to throw.  | ||||||
|                 // Default in EF Core would be to log a warning when client evaluation is performed. |                 // Default in EF Core would be to log a warning when client evaluation is performed. | ||||||
|                 c.ConfigureWarnings(warnings => warnings.Throw(RelationalEventId.QueryClientEvaluationWarning)); |                 c.ConfigureWarnings(warnings => warnings.Throw(RelationalEventId.QueryClientEvaluationWarning)); | ||||||
| @ -48,7 +52,7 @@ | |||||||
| 
 | 
 | ||||||
|             services.AddDbContext<IntegrationEventLogContext>(c => |             services.AddDbContext<IntegrationEventLogContext>(c => | ||||||
|             { |             { | ||||||
|                 c.UseSqlServer(Configuration["ConnectionString"], b => b.MigrationsAssembly("Catalog.API"));                |                 c.UseSqlServer(sqlConnection, b => b.MigrationsAssembly("Catalog.API"));                | ||||||
|                 c.ConfigureWarnings(warnings => warnings.Throw(RelationalEventId.QueryClientEvaluationWarning));                 |                 c.ConfigureWarnings(warnings => warnings.Throw(RelationalEventId.QueryClientEvaluationWarning));                 | ||||||
|             }); |             }); | ||||||
| 
 | 
 | ||||||
| @ -77,6 +81,8 @@ | |||||||
|                     .AllowCredentials()); |                     .AllowCredentials()); | ||||||
|             }); |             }); | ||||||
| 
 | 
 | ||||||
|  |             services.AddTransient<IIntegrationEventLogService, IntegrationEventLogService>(); | ||||||
|  |              | ||||||
|             var serviceProvider = services.BuildServiceProvider(); |             var serviceProvider = services.BuildServiceProvider(); | ||||||
|             var configuration = serviceProvider.GetRequiredService<IOptionsSnapshot<Settings>>().Value; |             var configuration = serviceProvider.GetRequiredService<IOptionsSnapshot<Settings>>().Value; | ||||||
|             services.AddSingleton<IEventBus>(new EventBusRabbitMQ(configuration.EventBusConnection)); |             services.AddSingleton<IEventBus>(new EventBusRabbitMQ(configuration.EventBusConnection)); | ||||||
| @ -106,9 +112,8 @@ | |||||||
|             //Seed Data |             //Seed Data | ||||||
|             CatalogContextSeed.SeedAsync(app, loggerFactory) |             CatalogContextSeed.SeedAsync(app, loggerFactory) | ||||||
|                 .Wait(); |                 .Wait(); | ||||||
| 
 |              | ||||||
|             // TODO: move this creation to a db initializer |             integrationEventLogContext.Database.Migrate(); | ||||||
|              integrationEventLogContext.Database.Migrate(); |  | ||||||
| 
 | 
 | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user