There are long-lived objects on the site, which at intervals of 4-5 minutes do something and write the results into the database. For myself, I decided that to keep the connection to the database open all the time, and Func<ApplicationDbContext> unnecessarily and pre-registered all this in the configuration.

 services.AddEntityFrameworkMySql().AddDbContextPool<ApplicationDbContext>(options => { options.UseMySql(Configuration.GetConnectionString("DefaultConnection"), b => b.MigrationsAssembly("VFL.Web").EnableRetryOnFailure()); } services.AddTransient<Func<ApplicationDbContext>>(s => s.GetRequiredService<ApplicationDbContext>); 

And all this works with periodic success sometimes giving an exception of the following form:

 2019-03-28 13:03:43.2027||ERROR|VFL.Web.Filters.PermissionHandler|Permission check failed System.InvalidOperationException: A second operation started on this context before a previous operation completed. This is usually caused by different threads using the same instance of DbContext, however instance members are not guaranteed to be thread safe. This could also be caused by a nested query being evaluated on the client, if this is the case rewrite the query avoiding nested invocations. at Microsoft.EntityFrameworkCore.Internal.ConcurrencyDetector.EnterCriticalSection() at Microsoft.EntityFrameworkCore.Internal.ConcurrencyDetector.EnterCriticalSectionAsync(CancellationToken cancellationToken) at Microsoft.EntityFrameworkCore.Query.Internal.AsyncLinqOperatorProvider.ExceptionInterceptor`1.EnumeratorExceptionInterceptor.MoveNext(CancellationToken cancellationToken) at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.ExecuteSingletonAsyncQuery[TResult](QueryContext queryContext, Func`2 compiledQuery, IDiagnosticsLogger`1 logger, Type contextType) at VFL.Web.Filters.PermissionHandler.HandleRequirementAsync(AuthorizationHandlerContext context, PermissionRequirement requirement) in /root/betprofit-server/VFL.Web/Filters/Permissions.cs:line 62 

And sometimes it changes to

Cannot access a disposed object. This is a process that has been resolved from This may occur if you are calling Dispose () If you are using the context context instances. Object name: 'ApplicationDbContext'.

All work with the database context is done using

 try { using (var db = dbFactory()) { var customer = await db.Customers.Include(c => c.Identity) .SingleOrDefaultAsync(c => c.Identity.Id == userId.Value); if (requirement.Permissions <= customer?.ClientLevel) context.Succeed(requirement); } } catch (Exception ex) { logger.LogError(ex, "Permission check failed"); } 

My base is MySQL + ASPNETCORE + EFCORE.

So how to cook it?

    1 answer 1

    Warning: This method, though it helps to avoid exceptions, but leads to memory memory .


    It is necessary to register the base context as follows.

     services.AddEntityFrameworkMySql().AddDbContext<ApplicationDbContext>(options => { options.UseMySql(Configuration.GetConnectionString("DefaultConnection"), }, ServiceLifetime.Transient); 

    In this case, for each new dependency requirement, a new connection with the database will be created.

    In order to be able to reuse the context in the already created instance, it is necessary to register such a lambda. It will be useful for logic that works throughout the life of the application.

     services.AddTransient<Func<ApplicationDbContext>>(s => s.GetRequiredService<ApplicationDbContext>); 

    Usage example:

     public class MyClass { private readonly Func<ApplicationDbContext> dbFactory; public MyClass(Func<ApplicationDbContext> dbFactory) { this.dbFactory = dbFactory; } } // где-то внутри этого класса using(var db = dbFactory()) { // Ваша логика }