我正在根据每个web api请求使用Simple Injector注册我的DbContext:
container.RegisterWebApiRequest<IModelContext, ModelContext>();
但也有一个为每个web请求创建的代理过滤器:
container.RegisterAll<DelegatingHandler>(
...
typeof(DelegatingHandlerProxy<TraceRequestHandler>)
...
);
它将有关请求的一些信息保存在数据库中:
public class TraceRequestHandler(ITraceRequestRepository r) : DelegateHandler
public class EntityTraceRequestRepository(IModelContext c) : ITraceRequestRepository
导致各种EF多线程异常的原因:
在创建模型时不能使用上下文。如果上下文在OnModelCreating方法内部使用,或者多个线程同时访问同一上下文实例,则可能引发此异常。请注意,DbContext和相关类的实例成员不能保证是线程安全的。
在上一个异步操作完成之前,在此上下文上启动了第二个操作。在调用此上下文上的另一个方法之前,请使用"wait"确保所有异步操作都已完成。任何实例成员都不能保证是线程安全的。
连接未打开。
ObjectContext实例已被释放,不能再用于需要连接的操作
在控制器/存储库中,例如:
public ProductController(IProductRepository r) : ApiController
public EntityProductRepository(IModelContext c) : IProductRepository
一旦删除了请求跟踪,一切都会正常工作。
如何分别为"通用"模块和基础设施模块注册数据库上下文?
错误出现在问题中没有显示的代码中,即InfrastructureDelegatingHandler
:
internal sealed class InfrastructureDelegatingHandler : DelegatingHandler {
private readonly IDbContext _dbContext;
public InfrastructureDelegatingHandler(IDbContext dbContext) {
_dbContext = dbContext;
}
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request,
CancellationToken cancellationToken) {
Task.Factory.StartNew(() => TraceRequest(request));
return base.SendAsync(request, cancellationToken);
}
private void TraceRequest(HttpRequestMessage request) {
DbRawSqlQuery<string> query =
((EfDbContext)_dbContext).Database.SqlQuery<string>(
"select name from sys.indexes");
Console.WriteLine(String.Join(",", query.ToArray()));
}
}
问题出现在以下行:
Task.Factory.StartNew(() => TraceRequest(request));
在这一行中,您将启动一个名为TraceRequest的新任务。然而,TraceRequest使用_dbContext
,您永远不会等待创建的任务,这意味着它将在后台运行。换句话说,您的EfDbContext
是从多个线程同时使用的。
当您将代码更改为以下代码时,您的问题将消失:
protected override async Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request, CancellationToken cancellationToken) {
await Task.Factory.StartNew(() => TraceRequest(request));
var message = await base.SendAsync(request, cancellationToken);
return message;
}