从DbContextPool(EF Core 2.0)获取Context实例以在Task中使用它

时间:2017-08-17 18:29:03

标签: entity-framework entity-framework-core

实体框架核心2.0引入了DbContext Pooling。 在我的代码中,我在Tasks中做了很多工作,因为我在数据库上做了一些独立的繁重操作。

我的老方法是:

Task.Run(() =>
{
    AppDbContext c = new AppDbContext(this.config); 

如何从EF Core 2.0 DbContext池中获取实例?

编辑:

我正在使用DI:public CategoryController(AppDbContext context, ... 这样做的原因是更快地执行Rest API方法。

例如,我认为这应该更快完成

    List<AppUser> users;
    List<DbGroup> groups;
    Task task1 = Task.Run(async() => {
        users = await ContextFromConnectionPool.Users.Where(t => t.Id == 1).ToListAsync();
    });
    Task task2 = Task.Run(async () => {
        groups = await ContextFromConnectionPool.Groups.Where(t => t.Id == 1).ToListAsync();
    });
    var tags = await this.context.Tags.ToListAsync();
    Task.WaitAll(task1, task2);
    //process all 3 results

然后这个:

    List<AppUser> users = await this.context.Users.Where(t => t.Id == 1).ToListAsync();
    List<DbGroup> groups = await this.context.Groups.Where(t => t.Id == 1).ToListAsync();
    var tags = await this.context.Tags.ToListAsync();
    //process all 3 results

在第二个示例中,第二个查询在第一个完成后执行 如果在第一个示例方法中,每个查询都需要150ms,请执行大约150ms,但大约在450ms中执行。我是对的吗? 唯一的问题是如何在第一种方法中从连接池获取上下文。

1 个答案:

答案 0 :(得分:2)

支持连接池的ASP.NET Core 2.0和Entity Framework Core 2.0的功能 - 不以任何方式 - 阻止您一次执行耗时的查询。池的整个概念是允许在多个请求中重用连接,而不是每次新请求进入时都必须重新创建实例。有时,它可以带来好处,有时可能会有垮台。现在,对于您的问题,有两种途径,

  1. 允许框架在Startup类中汇集连接,然后在您需要的任何地方重用这些对象。您可以在操作中捕获它们,以及您拥有的任何其他私有或本地函数。
  2. 不要使用DI和数据库上下文池,而是继续做你正在做的事情。请注意,您从未使用过DI,因此无需在Startup类中注册数据库上下文。但是你必须注意创建实例,手动处理实例。
  3. 由于许多原因,第二种方法不适合,也不是一种好的方法。如果要考虑第一种方法,则可以将控制器更改为接受类型数据库上下文的属性,例如,

    public class YourController : Controller {
        public AppDbContext c { get; set; }
    
        public YourController (AppDbContext c) {
            this.c = c;
        }
    }
    

    现在,如果你有了这个,那么你可以在你的任务中使用这个c变量,并在该函数中运行耗时的查询 - 这在任何方面都是无用的。你可以这样做,

    Task.Run(() => 
    {
        // Use c here.
    });
    

    请记住以下几点:

    1. 最好构建您的查询,然后调用ToListAsync() - ToList()可能不合适,请考虑使用ToListAsync()并应用await关键字异步捕获数据。
    2. 当您调用ToList或任何类似的函数时,您的查询仅在数据库服务器上执行。
    3. 在并行运行任务时,您还必须处理查询可能破坏策略的任何情况,例如数据完整性或数据库中的类似情况。捕获异常始终是最佳实践。
    4. 在您的情况下,为了更好地练习,您可能需要考虑将代码包装在using块中,

      Task.Run(() => {
          using (var context = new AppDbContext) {
              // use context here.
          }
      }
      

      这是我能说的最好的帮助,因为你还没有分享1)不使用DI的目的,2)你的查询样本(为什么不使用LINQ构建查询然后在服务器上执行?) 3)要使用的任何示例代码。我希望这会让你知道为什么你应该考虑使用DI并使用从那里返回的实例。