C# 为处理程序或筛选器等基础结构模块创建单独的DbContext

C# 为处理程序或筛选器等基础结构模块创建单独的DbContext,c#,multithreading,entity-framework,asp.net-web-api,simple-injector,C#,Multithreading,Entity Framework,Asp.net Web Api,Simple Injector,我正在根据每个web api请求使用Simple Injector注册我的DbContext: container.RegisterWebApiRequest<IModelContext, ModelContext>(); 导致各种EF多线程异常的原因: 创建模型时无法使用上下文。如果上下文在OnModelCreating方法内使用,或者如果多个线程同时访问同一上下文实例,则可能引发此异常。注意,DbContext和相关类的实例成员不能保证是线程安全的 在上一个异步操作完成之前,在

我正在根据每个web api请求使用Simple Injector注册我的DbContext:

container.RegisterWebApiRequest<IModelContext, ModelContext>();
导致各种EF多线程异常的原因:

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

在上一个异步操作完成之前,在此上下文上启动了第二个操作。使用“await”确保在此上下文上调用另一个方法之前已完成任何异步操作。任何实例成员都不能保证线程安全

连接未打开

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

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

public ProductController(IProductRepository r) : ApiController
public EntityProductRepository(IModelContext c) : IProductRepository 
一旦删除了请求跟踪,一切正常


如何分别为“通用”模块和基础结构模块注册db上下文?

问题中未显示的代码中存在缺陷,即
基础结构删除处理程序

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()));
    }
}
在这一行中,您将启动调用TraceRequest的新任务。然而,TraceRequest使用的是
\u 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;
} 
受保护的覆盖异步任务SendAsync(
HttpRequestMessage请求,CancellationToken CancellationToken){
wait Task.Factory.StartNew(()=>TraceRequest(请求));
var message=wait base.sendaync(请求、取消令牌);
返回消息;
} 

这可能表示您的
DbContext
作为单例缓存在某处。可能是您没有向我们展示您的配置的一些相关部分吗?@Steven:我似乎是一个竞赛条件,但处理程序(创建上下文、打开连接、保存数据、处理所有内容)和控制器(任务在实例化时完成,所以上下文处于不可用状态)@史蒂文:我可以使用func工厂注册处理程序并手动实例化上下文
c.register(()=>newtracerequesthandler(newmodelcontext())
,但我不知道如何将它和其他程序注册到
GlobalConfiguration.Handlers
@Steven:作为一种解决方案,您可以添加
registeral(params func[])
但不确定它与设计的其余部分有何关联。顺便说一句,我喜欢你如何使用想象中的C#6语法来定义你的类:-)耶!非常感谢。我总是想知道我是否应该等待Task.Factory.StartNew()。自.NET4.0以来,它的行为发生了变化吗?因为通常情况下,人们并不期待它的出现,我是说,从我无数次看到它是如何被使用的。很难用谷歌搜索(由于关键词不明确)这个确切的用例,结果/意见也不尽相同。@abatishchev是否需要等待取决于您想要实现什么。如果您真的希望操作在后台运行,那么显然不希望等待。但是在这种情况下,您应该在后台线程中从容器中提取一个新的对象图,以防止出现问题。阅读了解更多信息。
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));
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;
}