Ho un filtro azione che registra la richiesta in un database.
Io uso il solito metodo dell'iniettore di costruzione.
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);
}
Ma questo genera un'eccezione durante 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
Per quanto sono stato in grado di capire, il problema è che la durata del filtro è più lunga della durata del contesto (che era impostata su "Transiente")
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")), ServiceLifetime.Transient);
Come devo quindi iniettare DbContext?
Stai utilizzando Task.Run(() => loggerRepository.InsertServerCallLog(serverLog));
che assegna il contesto alla variabile loggerRepository
e gli dice di eseguirlo nel pool di thread, consentendo così al metodo di continuare l'esecuzione. È più probabile che il metodo esca prima che il pool di thread finisca l'esecuzione e che l'oggetto sia stato eliminato, quindi lascia che la chiamata a InsertServerCallLog
eseguita sul thread principale in modo che l'esecuzione non continui finché non ha terminato quel metodo