如何在主线程处理后再次获取 DBContext?

How to get DBContext again after the Main Thread disposes?

网芯。我正在使用 EF 核心、工作单元和存储库模式。在我的代码中,我使用 await Task.Delay(150) 释放我的主线程;然后我试图获取 DB 上下文,但异常显示 Dbcontext 已处理。那么有什么办法可以在主线程处理后再次获取 DBContext 吗?下面是我的代码片段,

services.AddDbContext<MyContext>(options =>
           options.UseSqlServer(this.Configuration["AzureSQLConnectionString"]));
services.AddScoped<IUnitOfWork, UnitOfWork>();

下面是我的方法

public Class MyTestBusinessLogic : IMyTestBusinessLogic 
{
    public MyTestBusinessLogic(IUnitOfWork unitOfWork)
    {
        UnitOfWork = unitOfWork ?? throw new ArgumentNullException(nameof(unitOfWork));
        MyTestRepository= UnitOfWork.GetRepository<MyTest>();
    }
    private IDbRepository<MyTest> MyTestRepository{ get; set; }
    public async Task MyMethod()
    {
        await MyTestRepository.GetFirstOrDefaultAsync(x => x.Id == Id); // works here 
        await Task.Delay(150);
        // here after my context disposes
        await MyTestRepository.GetFirstOrDefaultAsync(x => x.Id == Id); //this wont work and It will throw error inside GetFirstOrDefaultAsync method.
    }
}

GetFirstOrDefaultAsync 方法

public class DbRepository<T> : BaseRepository<T>, IDbRepository<T> where T : class
{
    private readonly DbContext dbContext;
    private readonly DbSet<T> dbSet;
    public DbRepository(DbContext dbContext)
    {
        this.dbContext = dbContext;
        this.dbSet = dbContext?.Set<T>();
    }

    public async Task<T> GetFirstOrDefaultAsync(Expression<Func<T, bool>> filter = null, Func<IQueryable<T>, IOrderedQueryable<T>> orderBy = null, params Expression<Func<T, object>>[] includes)
    {
        IQueryable<T> query = this.dbSet; // throws exception saying cannot use disposed object
        foreach (Expression<Func<T, object>> include in includes)
        {
            query = query.Include(include);
        }

        if (filter != null)
        {
            query = query.Where(filter);
        }

        if (orderBy != null)
        {
            query = orderBy(query);
        }

        return await query.FirstOrDefaultAsync().ConfigureAwait(false);
    }
}

有人可以帮我解决这个问题吗?任何帮助,将不胜感激。谢谢

我假设你 运行 MyMethod 没有 awaiting 它。因此,当主线程完成执行时,它将销毁 dbcontext 或从 DI.

注入的任何 scoped 对象

为了满足您的要求,您可以使用 IServiceScopeFactory,它是 singleton 对象并由 .net core 框架添加。使用它,您可以使用 serviceScopeFactory.CreateScope() 创建 scope 并使用 scope.ServiceProvider.GetRequiredService<MyDbContext>();.

创建所需 dependencies 的新实例

所以在这种情况下你可以像下面这样使用。请参考评论以了解。我不知道您的应用程序的确切结构,所以我将 IServiceScopeFactory 添加到您的 MyTest 中以获得 example/test。但是根据您的代码,您可以将其移动到其他地方,我猜是 UnitOfWork.

public Class MyTestBusinessLogic : IMyTestBusinessLogic 
{

    // Add serviceScopeFactory
    private readonly IServiceScopeFactory serviceScopeFactory;
    
    // Inject IServiceScopeFactory and assign to class object.
    public MyTestBusinessLogic(IServiceScopeFactory serviceScopeFactory)
    {
        this.serviceScopeFactory = serviceScopeFactory;
        
        // Create scope with IServiceScopeFactory.CreateScope()
        var scope = serviceScopeFactory.CreateScope();
        
        // GetRequiredService unitOfWork
        UnitOfWork = scope.ServiceProvider.GetRequiredService<IUnitOfWork>();
        // UnitOfWork = unitOfWork ?? throw new ArgumentNullException(nameof(unitOfWork));
        
        MyTestRepository= UnitOfWork.GetRepository<MyTest>();
    }
    
    private IDbRepository<MyTest> MyTestRepository{ get; set; }
    
    public async Task MyMethod()
    {
        await MyTestRepository.GetFirstOrDefaultAsync(x => x.Id == Id); // works here 
        await Task.Delay(150);
        // here after my context disposes
        await MyTestRepository.GetFirstOrDefaultAsync(x => x.Id == Id); //this wont work and It will throw error inside GetFirstOrDefaultAsync method.
    }
}

I am calling without await

这是你的问题。 So-called“即发即弃”在 ASP.NET 上是危险的,并且仅在 代码 运行 与否 时才适用.在您的情况下,由于您需要 运行 的代码,因此省略 await 是不合适的。

正确的解决方法是用await调用MyMethod

如果你绝对必须 return早,那么next-best解决方案是引入一个可靠的队列(例如,Azure Queue / Amazon SMS)以及处理该队列上消息的后台处理器(例如,Azure Function / Amazon Lambda / .NET Core BackgroundService)。一旦你有了这些,当你遇到需要提前 return 并稍后完成工作的情况时,该代码会将一条消息放入队列,然后 return 一个结果。当后台处理器从队列中检索到消息时,它就会开始工作。