为基础结构模块(如处理程序或过滤器)创建单独的DbContext

时间:2014-11-02 21:23:01

标签: c# multithreading entity-framework asp.net-web-api simple-injector

我在每个网络API请求的基础上使用Simple Injector注册我的DbContext:

container.RegisterWebApiRequest<IModelContext, ModelContext>();

但是也要为每个Web请求创建一个委托过滤器:

container.RegisterAll<DelegatingHandler>(
    ...
    typeof(DelegatingHandlerProxy<TraceRequestHandler>)
    ...
);

保存有关db中请求的一些信息:

public class TraceRequestHandler(ITraceRequestRepository r) : DelegateHandler
public class EntityTraceRequestRepository(IModelContext c) : ITraceRequestRepository 

导致各种EF多线程异常的原因:

  

创建模型时无法使用上下文。如果在OnModelCreating方法中使用上下文,或者同时由多个线程访问相同的上下文实例,则可能抛出此异常。请注意,DbContext和相关类的实例成员不保证是线程安全的。

     

在上一次异步操作完成之前,在此上下文中启动了第二个操作。使用&#39;等待&#39;确保在调用此上下文中的另一个方法之前已完成任何异步操作。不保证任何实例成员都是线程安全的。

     

连接未打开。

     

ObjectContext实例已被释放,不能再用于需要连接的操作

在控制器/存储库中,例如:

public ProductController(IProductRepository r) : ApiController
public EntityProductRepository(IModelContext c) : IProductRepository 

删除请求跟踪后,一切正常。

如何为“通用”单独注册数据库上下文&#39;模块和基础设施模块?

1 个答案:

答案 0 :(得分:1)

错误出现在您未在问题中显示的代码中,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;
}