From d1871fd152a65c2250af8abeb4e8f1362ab3fd45 Mon Sep 17 00:00:00 2001 From: StuartFerguson Date: Sun, 23 Aug 2026 05:50:38 +0100 Subject: [PATCH 1/2] configurable retry --- .../Bootstrapper/RepositoryRegistry.cs | 44 +++++++++++++++---- 1 file changed, 36 insertions(+), 8 deletions(-) diff --git a/EstateReportingAPI/Bootstrapper/RepositoryRegistry.cs b/EstateReportingAPI/Bootstrapper/RepositoryRegistry.cs index ebb3fa3..71a5f6e 100644 --- a/EstateReportingAPI/Bootstrapper/RepositoryRegistry.cs +++ b/EstateReportingAPI/Bootstrapper/RepositoryRegistry.cs @@ -7,6 +7,7 @@ namespace EstateReportingAPI.Bootstrapper; using Lamar; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Internal; +using Microsoft.Extensions.Options; using Shared.EntityFramework; using Shared.General; using System.Diagnostics.CodeAnalysis; @@ -21,17 +22,44 @@ public RepositoryRegistry(){ } this.AddSingleton(); this.AddSingleton(typeof(IDbContextResolver<>), typeof(DbContextResolverX<>)); + if (Startup.WebHostEnvironment.IsEnvironment("IntegrationTest") || Startup.Configuration.GetValue("ServiceOptions:UseInMemoryDatabase") == true) { - this.AddDbContext(builder => builder.UseInMemoryDatabase("TransactionProcessorReadModel")); + this.AddDbContext(builder => { + builder.UseInMemoryDatabase("TransactionProcessorReadModel"); + }); } - else { - this.AddSingleton(); - this.AddDbContext((sp, options) => + else + { + SqlServerRetryOptions retryOptions; + try { - options.UseSqlServer(ConfigurationReader.GetConnectionString("TransactionProcessorReadModel")); - options.AddInterceptors(sp.GetRequiredService()); - }); + retryOptions = ConfigurationReader.GetSection("AppSettings:SqlServerRetry"); + } + catch (KeyNotFoundException) + { + retryOptions = null; + } + + if (retryOptions != null) + { + this.AddDbContext(options => { + + options.UseSharedSqlServer(ConfigurationReader.GetConnectionString("TransactionProcessorReadModel"), retry => { + retry.AdditionalTransientErrorNumbers = retryOptions.AdditionalTransientErrorNumbers; + retry.MaxRetryCount = retryOptions.MaxRetryCount; + retry.MaxRetryDelay = retryOptions.MaxRetryDelay; + }); + }); + } + else + { + this.AddDbContext(options => { + options.UseSqlServer(ConfigurationReader.GetConnectionString("TransactionProcessorReadModel"), retry => { + retry.EnableRetryOnFailure(); + }); + }); + + } } - } } \ No newline at end of file From a23948cf0639e97c615884edc3504d7281c858b6 Mon Sep 17 00:00:00 2001 From: StuartFerguson Date: Sun, 23 Aug 2026 06:01:19 +0100 Subject: [PATCH 2/2] :| --- .../QueryTimingInterceptor.cs | 38 ++++++++++++++++--- .../Bootstrapper/RepositoryRegistry.cs | 38 +++++++------------ 2 files changed, 46 insertions(+), 30 deletions(-) diff --git a/EstateReportingAPI.BusinessLogic/QueryTimingInterceptor.cs b/EstateReportingAPI.BusinessLogic/QueryTimingInterceptor.cs index f07b6f5..950e576 100644 --- a/EstateReportingAPI.BusinessLogic/QueryTimingInterceptor.cs +++ b/EstateReportingAPI.BusinessLogic/QueryTimingInterceptor.cs @@ -5,13 +5,14 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Shared.EntityFramework; +using Shared.General; using Shared.Logger; using System; using System.Collections.Generic; using System.Data.Common; using System.Diagnostics; using System.Text; -using Shared.General; +using TransactionProcessor.Database.Contexts; namespace EstateReportingAPI.BusinessLogic; @@ -83,10 +84,37 @@ public ResolvedDbContext Resolve(String connectionStringKey, // Create an isolated service collection and provider ServiceCollection services = new(); - services.AddDbContext(options => { - options.UseSqlServer(connectionString, sqlOptions => sqlOptions.EnableRetryOnFailure()); - options.AddInterceptors(Interceptor); // attach here - }); + + SqlServerRetryOptions retryOptions; + try + { + retryOptions = ConfigurationReader.GetSection("AppSettings:SqlServerRetry"); + } + catch (KeyNotFoundException) + { + retryOptions = null; + } + + if (retryOptions != null) + { + services.AddDbContext(options => { + options.UseSharedSqlServer(ConfigurationReader.GetConnectionString("TransactionProcessorReadModel"), retry => { + retry.AdditionalTransientErrorNumbers = retryOptions.AdditionalTransientErrorNumbers; + retry.MaxRetryCount = retryOptions.MaxRetryCount; + retry.MaxRetryDelay = retryOptions.MaxRetryDelay; + }); + options.AddInterceptors(Interceptor); + }); + } + else + { + services.AddDbContext(options => { + options.UseSqlServer(ConfigurationReader.GetConnectionString("TransactionProcessorReadModel"), retry => { retry.EnableRetryOnFailure(); }); + options.AddInterceptors(Interceptor); + }); + + } + ServiceProvider provider = services.BuildServiceProvider(); scope = provider.CreateScope(); diff --git a/EstateReportingAPI/Bootstrapper/RepositoryRegistry.cs b/EstateReportingAPI/Bootstrapper/RepositoryRegistry.cs index 71a5f6e..aaf93cb 100644 --- a/EstateReportingAPI/Bootstrapper/RepositoryRegistry.cs +++ b/EstateReportingAPI/Bootstrapper/RepositoryRegistry.cs @@ -14,37 +14,29 @@ namespace EstateReportingAPI.Bootstrapper; [ExcludeFromCodeCoverage] public class RepositoryRegistry : ServiceRegistry{ - public RepositoryRegistry(){ + public RepositoryRegistry() { String? inTestMode = Environment.GetEnvironmentVariable("InTestMode"); - if (String.Compare(inTestMode, Boolean.TrueString, StringComparison.InvariantCultureIgnoreCase) != 0) - { + if (String.Compare(inTestMode, Boolean.TrueString, StringComparison.InvariantCultureIgnoreCase) != 0) { this.AddSingleton(); } + this.AddSingleton(); this.AddSingleton(typeof(IDbContextResolver<>), typeof(DbContextResolverX<>)); - - if (Startup.WebHostEnvironment.IsEnvironment("IntegrationTest") || Startup.Configuration.GetValue("ServiceOptions:UseInMemoryDatabase") == true) - { - this.AddDbContext(builder => { - builder.UseInMemoryDatabase("TransactionProcessorReadModel"); - }); + + if (Startup.WebHostEnvironment.IsEnvironment("IntegrationTest") || Startup.Configuration.GetValue("ServiceOptions:UseInMemoryDatabase") == true) { + this.AddDbContext(builder => { builder.UseInMemoryDatabase("TransactionProcessorReadModel"); }); } - else - { + else { SqlServerRetryOptions retryOptions; - try - { + try { retryOptions = ConfigurationReader.GetSection("AppSettings:SqlServerRetry"); } - catch (KeyNotFoundException) - { + catch (KeyNotFoundException) { retryOptions = null; } - if (retryOptions != null) - { + if (retryOptions != null) { this.AddDbContext(options => { - options.UseSharedSqlServer(ConfigurationReader.GetConnectionString("TransactionProcessorReadModel"), retry => { retry.AdditionalTransientErrorNumbers = retryOptions.AdditionalTransientErrorNumbers; retry.MaxRetryCount = retryOptions.MaxRetryCount; @@ -52,14 +44,10 @@ public RepositoryRegistry(){ }); }); } - else - { - this.AddDbContext(options => { - options.UseSqlServer(ConfigurationReader.GetConnectionString("TransactionProcessorReadModel"), retry => { - retry.EnableRetryOnFailure(); - }); - }); + else { + this.AddDbContext(options => { options.UseSqlServer(ConfigurationReader.GetConnectionString("TransactionProcessorReadModel"), retry => { retry.EnableRetryOnFailure(); }); }); } } + } } \ No newline at end of file