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

本文关键字:创建 单独 DbContext 筛选 模块 结构 程序 处理 为基础 | 更新日期: 2023-09-27 18:28:45

我正在根据每个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 

一旦删除了请求跟踪,一切都会正常工作。

如何分别为"通用"模块和基础设施模块注册数据库上下文?

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

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