Implemented EF Core DB connections resiliency with explicit retries and execution strategy when using multiple DbContexts
This commit is contained in:
parent
c07665aef6
commit
0d78461a08
@ -158,18 +158,26 @@ namespace Microsoft.eShopOnContainers.Services.Catalog.API.Controllers
|
|||||||
//Update current product
|
//Update current product
|
||||||
catalogItem = productToUpdate;
|
catalogItem = productToUpdate;
|
||||||
|
|
||||||
// Achieving atomicity between original Catalog database operation and the IntegrationEventLog thanks to a local transaction
|
//Use of an EF Core resiliency strategy when using multiple DbContexts within an explicit BeginTransaction():
|
||||||
using (var transaction = _catalogContext.Database.BeginTransaction())
|
//See: https://docs.microsoft.com/en-us/ef/core/miscellaneous/connection-resiliency
|
||||||
|
var strategy = _catalogContext.Database.CreateExecutionStrategy();
|
||||||
|
|
||||||
|
await strategy.ExecuteAsync(async () =>
|
||||||
{
|
{
|
||||||
|
// Achieving atomicity between original Catalog database operation and the IntegrationEventLog thanks to a local transaction
|
||||||
|
using (var transaction = _catalogContext.Database.BeginTransaction())
|
||||||
|
{
|
||||||
_catalogContext.CatalogItems.Update(catalogItem);
|
_catalogContext.CatalogItems.Update(catalogItem);
|
||||||
await _catalogContext.SaveChangesAsync();
|
await _catalogContext.SaveChangesAsync();
|
||||||
|
|
||||||
//Save to EventLog only if product price changed
|
//Save to EventLog only if product price changed
|
||||||
if(raiseProductPriceChangedEvent)
|
if (raiseProductPriceChangedEvent)
|
||||||
await _integrationEventLogService.SaveEventAsync(priceChangedEvent);
|
await _integrationEventLogService.SaveEventAsync(priceChangedEvent);
|
||||||
|
|
||||||
transaction.Commit();
|
transaction.Commit();
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
//Publish to Event Bus only if product price changed
|
//Publish to Event Bus only if product price changed
|
||||||
if (raiseProductPriceChangedEvent)
|
if (raiseProductPriceChangedEvent)
|
||||||
|
@ -13,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;
|
||||||
using System.Data.SqlClient;
|
using System.Data.SqlClient;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
|
||||||
@ -39,21 +40,35 @@
|
|||||||
|
|
||||||
public void ConfigureServices(IServiceCollection services)
|
public void ConfigureServices(IServiceCollection services)
|
||||||
{
|
{
|
||||||
|
//Using the same SqlConnection for both DbContexts (CatalogContext and IntegrationEventLogContext)
|
||||||
var sqlConnection = new SqlConnection(Configuration["ConnectionString"]);
|
var sqlConnection = new SqlConnection(Configuration["ConnectionString"]);
|
||||||
|
|
||||||
services.AddDbContext<CatalogContext>(c =>
|
services.AddDbContext<CatalogContext>(options =>
|
||||||
{
|
{
|
||||||
c.UseSqlServer(sqlConnection);
|
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);
|
||||||
|
});
|
||||||
// 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));
|
options.ConfigureWarnings(warnings => warnings.Throw(RelationalEventId.QueryClientEvaluationWarning));
|
||||||
//Check Client vs. Server evaluation: https://docs.microsoft.com/en-us/ef/core/querying/client-eval
|
//Check Client vs. Server evaluation: https://docs.microsoft.com/en-us/ef/core/querying/client-eval
|
||||||
});
|
});
|
||||||
|
|
||||||
services.AddDbContext<IntegrationEventLogContext>(c =>
|
services.AddDbContext<IntegrationEventLogContext>(options =>
|
||||||
{
|
{
|
||||||
c.UseSqlServer(sqlConnection, b => b.MigrationsAssembly("Catalog.API"));
|
options.UseSqlServer(sqlConnection,
|
||||||
c.ConfigureWarnings(warnings => warnings.Throw(RelationalEventId.QueryClientEvaluationWarning));
|
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<Settings>(Configuration);
|
services.Configure<Settings>(Configuration);
|
||||||
|
@ -52,10 +52,14 @@
|
|||||||
services.AddEntityFrameworkSqlServer()
|
services.AddEntityFrameworkSqlServer()
|
||||||
.AddDbContext<OrderingContext>(options =>
|
.AddDbContext<OrderingContext>(options =>
|
||||||
{
|
{
|
||||||
options.UseSqlServer(Configuration["ConnectionString"],
|
options.UseSqlServer(Configuration["ConnectionString"],
|
||||||
sqlop => sqlop.MigrationsAssembly(typeof(Startup).GetTypeInfo().Assembly.GetName().Name));
|
sqlServerOptionsAction: sqlOptions =>
|
||||||
|
{
|
||||||
|
sqlOptions.MigrationsAssembly(typeof(Startup).GetTypeInfo().Assembly.GetName().Name);
|
||||||
|
sqlOptions.EnableRetryOnFailure(maxRetryCount: 5, maxRetryDelay: TimeSpan.FromSeconds(30), errorNumbersToAdd: null);
|
||||||
|
});
|
||||||
},
|
},
|
||||||
ServiceLifetime.Scoped //DbContext is shared across the HTTP request scope (graph of objects started in the HTTP request)
|
ServiceLifetime.Scoped //Showing explicitly that the DbContext is shared across the HTTP request scope (graph of objects started in the HTTP request)
|
||||||
);
|
);
|
||||||
|
|
||||||
services.AddSwaggerGen();
|
services.AddSwaggerGen();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user