Azure and Microsoft recommend the use of retry functionality when accessing databases in the cloud. When trying to use sqlOptions.EnableRetryOnFailure() in an ABP project, it causes OpenIddict inside ABP to misbehave due to the database queries not being wrapped in a way that allows the auto-retry to function. As a result, either we have to wrap each and every database use in the application, or not use retry functionality at all. Neither is a viable solution for a production environment in the cloud.
In responce i tried to work around it by having 2 DBContexts pointing to the same database, one for the tools, the other with auto retry for our own code. Sadly this then failed to resolve DBContexts to i assume this is not a possible route either.
Is there a viable workaround for this hat we are not aware of? Otherwise we will have to stop using ABP altogether just for being incompatible with cloud stability. Resouces referenced: No Support 1 No support 2 No Support 3 Microsoft Recommendations
7 Answer(s)
-
-1
ABP Framework currently does not natively support global EnableRetryOnFailure for all database operations, especially for OpenIddict and some internal modules. This is a known limitation, as documented in the following discussions:
- Database exceptions while running in Azure - how to handle without EnableRetryOnFailure
- [ABP GitHub Issue #17479](https://github.com/abpframework/abp/issues/17479)
- EnableRetryOnFailure support discussion
The root cause is that OpenIddict (and some other ABP modules) use their own DbContext or transaction management, which may not be compatible with EF Core's built-in connection resiliency when EnableRetryOnFailure is enabled globally. This can lead to unexpected behavior or errors.
As of ABP v9.1.3, there is no official workaround that enables seamless, global retry logic for all database operations—including those performed by OpenIddict—without risking side effects or instability.
The only reliable approaches currently are:
- Manually wrap critical database operations in your own retry logic where possible.
- Avoid enabling EnableRetryOnFailure globally if you rely on OpenIddict or other ABP modules that are not compatible.
- Monitor ABP's GitHub and release notes for future updates, as this is a recognized issue and may be addressed in upcoming versions.
For general EF Core connection resiliency recommendations, see the official Microsoft documentation:
If global retry support is a strict requirement for your production environment, you may need to consider alternative authentication modules or frameworks until ABP provides full compatibility.
Sources:
- https://abp.io/support/questions/7848/Database-exceptions-while-running-in-Azure-how-to-handle-without-EnableRetryOnFailure
- https://github.com/abpframework/abp/issues/17479
- https://abp.io/support/questions/3781/EnableRetryOnFailure
- https://learn.microsoft.com/en-us/ef/core/miscellaneous/connection-resiliency
This response was generated by AI. If it didn’t solve your problem, feel free to post a new answer — our support team will jump in to help you soon.
-
0
Kindly piss off with your AI generated answer of reitterating stuff i already knew and demonstrated👍
-
0
hi
In responce i tried to work around it by having 2 DBContexts pointing to the same database, one for the tools, the other with auto retry for our own code. Sadly this then failed to resolve DBContexts to i assume this is not a possible route either.
It seems possible. Can you share your custom code?
If you get any exception, please share the error logs.
Thanks.
-
0
[liming.ma@volosoft.com] said: It seems possible. Can you share your custom code?
If you get any exception, please share the error logs.
Not as much custom code, as trying to add a 2nd nearly identical DbContext. For now i have just duplicated the 1st dbContext, because it seems near impossible to get them to inherit from one another, purely due to the type being different.
public override void ConfigureServices(ServiceConfigurationContext context) { IServiceCollection services = context.Services; services.AddAbpDbContext<MyDbContext>(options => { /* Remove "includeAllEntities: true" to create * default repositories only for aggregate roots */ options.AddDefaultRepositories(includeAllEntities: true); }); services.AddAbpDbContext<MyRetryDbContext>(options => { /* Remove "includeAllEntities: true" to create * default repositories only for aggregate roots */ options.AddDefaultRepositories(includeAllEntities: true); }); Configure<AbpDbContextOptions>(options => { options.Configure<MyDbContext>(c => { #if DEBUG c.UseSqlServer() .AddInterceptors(ignoringIdentityResolutionInterceptor) .EnableSensitiveDataLogging(); #else c.UseSqlServer() .AddInterceptors(ignoringIdentityResolutionInterceptor); #endif }); options.Configure<MyRetryDbContext>(c => { #if DEBUG c.UseSqlServer(sqlOptions => { sqlOptions.EnableRetryOnFailure(); }) .AddInterceptors(ignoringIdentityResolutionInterceptor) .EnableSensitiveDataLogging(); #else c.UseSqlServer(sqlOptions => { sqlOptions.EnableRetryOnFailure(); }) .AddInterceptors(ignoringIdentityResolutionInterceptor); #endif }); });
As a result everything i do is met with this exception, even though it worked fine with just 1 config. And i am not sure how to debug it, as all examples i found did not use the 2 near identical dbcontexts.
Volo.Abp.AbpException: No configuration found for Microsoft.EntityFrameworkCore.DbContext, Microsoft.EntityFrameworkCore, Version=9.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60! Use services.Configure<AbpDbContextOptions>(...) to configure it. at Volo.Abp.EntityFrameworkCore.DependencyInjection.DbContextOptionsFactory.Configure[TDbContext](AbpDbContextOptions options, AbpDbContextConfigurationContext`1 context) at Volo.Abp.EntityFrameworkCore.DependencyInjection.DbContextOptionsFactory.Create[TDbContext](IServiceProvider serviceProvider) at Autofac.Extensions.DependencyInjection.AutofacRegistration.<>c__DisplayClass5_0.<Register>b__1(IComponentContext context, IEnumerable`1 parameters) at Autofac.Core.Activators.Delegate.DelegateActivator.ActivateInstance(IComponentContext context, IEnumerable`1 parameters) at Autofac.Core.Activators.Delegate.DelegateActivator.<ConfigurePipeline>b__2_0(ResolveRequestContext context, Action`1 next) at Autofac.Core.Resolving.Middleware.DelegateMiddleware.Execute(ResolveRequestContext context, Action`1 next) at Autofac.Core.Resolving.Pipeline.ResolvePipelineBuilder.<>c__DisplayClass14_0.<BuildPipeline>b__1(ResolveRequestContext context) at Autofac.Core.Resolving.Middleware.DisposalTrackingMiddleware.Execute(ResolveRequestContext context, Action`1 next) at Autofac.Core.Resolving.Pipeline.ResolvePipelineBuilder.<>c__DisplayClass14_0.<BuildPipeline>b__1(ResolveRequestContext context) at Autofac.Extensions.DependencyInjection.KeyedServiceMiddleware.Execute(ResolveRequestContext context, Action`1 next) at Autofac.Core.Resolving.Pipeline.ResolvePipelineBuilder.<>c__DisplayClass14_0.<BuildPipeline>b__1(ResolveRequestContext context) at Autofac.Core.Resolving.Middleware.ActivatorErrorHandlingMiddleware.Execute(ResolveRequestContext context, Action`1 next) --- End of inner exception stack trace --- at Autofac.Core.Resolving.Middleware.ActivatorErrorHandlingMiddleware.Execute(ResolveRequestContext context, Action`1 next) at Autofac.Core.Resolving.Pipeline.ResolvePipelineBuilder.<>c__DisplayClass14_0.<BuildPipeline>b__1(ResolveRequestContext context) at Autofac.Core.Pipeline.ResolvePipeline.Invoke(ResolveRequestContext context) at Autofac.Core.Resolving.Middleware.RegistrationPipelineInvokeMiddleware.Execute(ResolveRequestContext context, Action`1 next) at Autofac.Core.Resolving.Pipeline.ResolvePipelineBuilder.<>c__DisplayClass14_0.<BuildPipeline>b__1(ResolveRequestContext context) at Autofac.Core.Resolving.Middleware.SharingMiddleware.Execute(ResolveRequestContext context, Action`1 next) at Autofac.Core.Resolving.Pipeline.ResolvePipelineBuilder.<>c__DisplayClass14_0.<BuildPipeline>b__1(ResolveRequestContext context) at Autofac.Core.Resolving.Middleware.ScopeSelectionMiddleware.Execute(ResolveRequestContext context, Action`1 next) at Autofac.Core.Resolving.Pipeline.ResolvePipelineBuilder.<>c__DisplayClass14_0.<BuildPipeline>b__1(ResolveRequestContext context) at Autofac.Core.Resolving.Middleware.CircularDependencyDetectorMiddleware.Execute(ResolveRequestContext context, Action`1 next) at Autofac.Core.Resolving.Pipeline.ResolvePipelineBuilder.<>c__DisplayClass14_0.<BuildPipeline>b__1(ResolveRequestContext context) at Autofac.Core.Pipeline.ResolvePipeline.Invoke(ResolveRequestContext context) at Autofac.Core.Resolving.ResolveOperation.GetOrCreateInstance(ISharingLifetimeScope currentOperationScope, ResolveRequest& request) at Autofac.Core.Resolving.ResolveOperation.ExecuteOperation(ResolveRequest& request) at Autofac.Core.Resolving.ResolveOperation.Execute(ResolveRequest& request) at Autofac.Core.Lifetime.LifetimeScope.ResolveComponent(ResolveRequest& request) at Autofac.Core.Lifetime.LifetimeScope.Autofac.IComponentContext.ResolveComponent(ResolveRequest& request) at Autofac.ResolutionExtensions.TryResolveService(IComponentContext context, Service service, IEnumerable`1 parameters, Object& instance) at Autofac.ResolutionExtensions.ResolveService(IComponentContext context, Service service, IEnumerable`1 parameters) at Autofac.ResolutionExtensions.Resolve(IComponentContext context, Type serviceType, IEnumerable`1 parameters) at Autofac.ResolutionExtensions.Resolve(IComponentContext context, Type serviceType) at Autofac.Extensions.DependencyInjection.AutofacServiceProvider.GetRequiredService(Type serviceType) at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService[T](IServiceProvider provider) at Volo.Abp.Uow.EntityFrameworkCore.UnitOfWorkDbContextProvider`1.CreateDbContextAsync(IUnitOfWork unitOfWork) at Volo.Abp.Uow.EntityFrameworkCore.UnitOfWorkDbContextProvider`1.CreateDbContextAsync(IUnitOfWork unitOfWork, String connectionStringName, String connectionString) at Volo.Abp.Uow.EntityFrameworkCore.UnitOfWorkDbContextProvider`1.GetDbContextAsync() at Volo.Abp.Domain.Repositories.EntityFrameworkCore.EfCoreRepository`2.GetDbSetAsync() at Volo.Abp.BackgroundJobs.EntityFrameworkCore.EfCoreBackgroundJobRepository.GetWaitingListQueryAsync(Int32 maxResultCount) at Volo.Abp.BackgroundJobs.EntityFrameworkCore.EfCoreBackgroundJobRepository.GetWaitingListAsync(Int32 maxResultCount, CancellationToken cancellationToken) at Castle.DynamicProxy.AsyncInterceptorBase.ProceedAsynchronous[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo) at Volo.Abp.Castle.DynamicProxy.CastleAbpMethodInvocationAdapterWithReturnValue`1.ProceedAsync() at Volo.Abp.Uow.UnitOfWorkInterceptor.InterceptAsync(IAbpMethodInvocation invocation) at Volo.Abp.Castle.DynamicProxy.CastleAsyncAbpInterceptorAdapter`1.InterceptAsync[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo, Func`3 proceed) at Volo.Abp.BackgroundJobs.BackgroundJobStore.GetWaitingJobsAsync(Int32 maxResultCount) at Volo.Abp.BackgroundJobs.BackgroundJobWorker.DoWorkAsync(PeriodicBackgroundWorkerContext workerContext) at Volo.Abp.BackgroundJobs.BackgroundJobWorker.DoWorkAsync(PeriodicBackgroundWorkerContext workerContext) at Volo.Abp.BackgroundWorkers.Hangfire.HangfirePeriodicBackgroundWorkerAdapter`1.DoWorkAsync(CancellationToken cancellationToken) at InvokeStub_TaskAwaiter.GetResult(Object, Object, IntPtr*) at System.Reflection.MethodBaseInvoker.InvokeWithNoArgs(Object obj, BindingFlags invokeAttr)
Hope you can shed some light on this!
-
0
hi
The new
MyRetryDbContext
should not be used with ABPAddAbpDbContext/Repository
, etc..You can use the
AddDbContext
method to add it to the Dependency Injection system and configure it to enableEnableRetryOnFailure
. Then injectAddDbContext
into your service and use it.This will lose some features of ABP. But still can use it to query and add/update new entites.
Thanks.
-
0
What features would we lose over it then? Because we use a lot of them. I think i am most worried about behavioral changes like UOW or authentication/permissions etc.
-
0
hi
Only
Data Access
is affected. You need to useMyRetryDbContext
to manage transactions and perform CRUD operations on the entities.