How to inject DbContext in an Action Filter

asp.net-core c# entity-framework-core

Question

I have an action filter which logs the request in a database.

I use the usual construction injector method.

public ServerLogFilterAttribute(OrderEntryContext context)
{
  this.context = context;
}

public override void OnActionExecuting(ActionExecutingContext context)
{
    var descriptor = context.ActionDescriptor;
    if (descriptor != null && descriptor.RouteValues.Count > 0)
    {
        var actionName = descriptor.RouteValues["action"];
        var controllerName = descriptor.RouteValues["controller"];

        if (context.HttpContext.User != null && context.HttpContext.User.Identity != null)
        {
            var claimsIdentity = context.HttpContext.User.Identity as ClaimsIdentity;

            if (claimsIdentity != null && claimsIdentity.IsAuthenticated)
            {
                var username = claimsIdentity.FindFirst(ClaimTypes.NameIdentifier).Value;
                var userId = claimsIdentity.Claims.First(c => c.Type == "userId").Value;

                var serverLog = new ServerLog()
                {
                    AspNetUserId = userId,
                    Controller = controllerName,
                    Action = actionName,
                    TimeStamp = DateTime.Now
                };

                LoggerRepository loggerRepository = new LoggerRepository(this.context);
                Task.Run(() => loggerRepository.InsertServerCallLog(serverLog));
            }
        }
    }

    base.OnActionExecuting(context);

}

But this throws an exception during SaveChanges():

System.ObjectDisposedException
  HResult=0x80131622
  Message=Cannot access a disposed object. A common cause of this error is disposing a context that was resolved from dependency injection and then later trying to use the same context instance elsewhere in your application. This may occur if you are calling Dispose() on the context, or wrapping the context in a using statement. If you are using dependency injection, you should let the dependency injection container take care of disposing context instances.
  Source=Microsoft.EntityFrameworkCore
  StackTrace:
   at Microsoft.EntityFrameworkCore.DbContext.CheckDisposed()
   at Microsoft.EntityFrameworkCore.DbContext.get_InternalServiceProvider()
   at Microsoft.EntityFrameworkCore.DbContext.get_DbContextDependencies()
   at Microsoft.EntityFrameworkCore.DbContext.<SaveChangesAsync>d__48.MoveNext()
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
   at GGL.OrderEntry.Data.OrderEntryContext.<SaveChangesAsync>d__251.MoveNext() in C:\Dev\GGL\GGL.OrderEntry\GGL.OrderEntry.Data\Context\OrderEntryContext.Partial.cs:line 306

As far as I have been able to figure, the issue is that the lifetime of the Filter is longer than the lifetime of the context (which was set to "Transient)

options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")), ServiceLifetime.Transient);

How then should I be injecting the DbContext?

1
0
8/30/2018 6:44:00 PM

Popular Answer

You're using Task.Run(() => loggerRepository.InsertServerCallLog(serverLog)); which assigns the context to variable loggerRepository, and telling it to run on the thread pool, thus letting the method continue execution. More than likely the method exits before the thread pool finishes execution and the object has been disposed, just let the call to InsertServerCallLog run on the main thread so that execution does not continue until it has finished that method

1
8/30/2018 6:50:59 PM


Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow