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?
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
User contributions licensed under CC BY-SA 3.0