C# 同时执行两个数据库调用(使用两个DBContext)

C# 同时执行两个数据库调用(使用两个DBContext),c#,async-await,task-parallel-library,ef-core-2.2,C#,Async Await,Task Parallel Library,Ef Core 2.2,我需要从两个不同的DbContext(针对Oracle数据库的两个不同模式)获取结果,以填充页面。我希望同时执行两个数据库查询(只读、无写操作),并在它们都完成时返回结果。问题是,我是并行世界中的一个串行人,我不知道杰克关于Async/wait/TPL等的事 我有一个控制器Action,基本上看起来像这样: public Task<IActionResult> Foo(MyViewModel vm) { if (!ModelState.IsValid) return Task.R

我需要从两个不同的
DbContext
(针对Oracle数据库的两个不同模式)获取结果,以填充页面。我希望同时执行两个数据库查询(只读、无写操作),并在它们都完成时返回结果。问题是,我是并行世界中的一个串行人,我不知道杰克关于
Async
/
wait
/
TPL
等的事

我有一个控制器
Action
,基本上看起来像这样:

public Task<IActionResult> Foo(MyViewModel vm)
{
  if (!ModelState.IsValid) return Task.Run(()=> (IActionResult)View(vm));

  var filter = new FilterObject(vm);
  var firstTask = _firstContext.FilterItems(filter); // returns Task<IQueryable<Items>>
  var secondTask = _secondContext.FilterItems(filter); // returns Task<IQueryable<Items>> 

  vm.Result.Clear();
  vm.Results.AddRange(firstTask.Result);
  vm.Results.AddRange(secondTask.Result);

  return Task.Run(()=> (IActionResult)View("Index", vm));
}
。。。我的FilterObject会修改IQueryable:

public class FilterObject
{
  public Task<IQueryable<Items>> ApplyTo(IQueryable<Items> items)
  {
    return Task.Run(()=> items
      .Where(item => item.Property1.Contains(this.Property1))
      .Where(item => item.Other == this.OtherString)
       // more Where clauses ad nauseum; you get the idea
  }
}
公共类过滤器对象
{
公共任务应用程序(可查询项)
{
返回任务。运行(()=>项
.Where(item=>item.Property1.Contains(this.Property1))
.Where(item=>item.Other==this.OtherString)
//更多疯狂的Where子句;你明白了
}
}
我是在接近正确的地方这样做的吗?据我所知,对数据库的调用在
AddRange
方法之前不会真正执行,因为
Task.Result
是一个
IQueryable
-所以对我来说这似乎是错误的:
AddRange()
方法将同步执行,而不是异步执行,对吗?我所做的只是构建两个查询,而不是执行它们。我猜我需要从DbContext返回一个
列表,这将导致每个查询实际执行……但这是我唯一需要做的更改,以使这些调用同时发生,而不必执行互相阻拦


一如既往,我们非常感谢任何指导。

任务。Run
会将任务移动到另一个线程中,这并不是真的需要。使用
异步
等待
,这将在同一个线程上运行所有内容,但允许在等待结果的同时在同一个线程上完成其他工作。例如,在启动第一个查询时,它可以在等待第一个查询完成的同时启动第二个查询

似乎
filterietems
ApplyTo
只是修改查询,而不是实际执行结果,所以我不明白他们为什么需要返回
任务
。您可以使用在生成查询后实际执行查询

例如:

public async Task<IActionResult> Foo(MyViewModel vm)
{
  if (!ModelState.IsValid) return Task.Run(()=> (IActionResult)View(vm));

  var filter = new FilterObject(vm);

  //This will execute the queries
  var firstTask = _firstContext.FilterItems(filter).ToListAsync();
  var secondTask = _secondContext.FilterItems(filter).ToListAsync();

  vm.Result.Clear();
  vm.Results.AddRange(await firstTask); //add the first set when they're available
  vm.Results.AddRange(await secondTask); //add the second set when they're available

  return View("Index", vm);
}
公共类过滤器对象
{
公共可液化ApplyTo(可液化物品)
{
退货项目
.Where(item=>item.Property1.Contains(this.Property1))
.Where(item=>item.Other==this.OtherString)
//更多疯狂的Where子句;你明白了
}
}

任务。Run
会将任务移动到另一个线程中,这并不是真的需要。使用
异步
等待
,这将在同一个线程上运行所有操作,但允许在等待结果的同时在同一个线程上执行其他工作。例如,在触发第一个查询后,它可以启动第二个查询在等待第一个完成时进行查询

似乎
filterietems
ApplyTo
只是修改查询,而不是实际执行结果,所以我不明白他们为什么需要返回
任务
。您可以使用在生成查询后实际执行查询

例如:

public async Task<IActionResult> Foo(MyViewModel vm)
{
  if (!ModelState.IsValid) return Task.Run(()=> (IActionResult)View(vm));

  var filter = new FilterObject(vm);

  //This will execute the queries
  var firstTask = _firstContext.FilterItems(filter).ToListAsync();
  var secondTask = _secondContext.FilterItems(filter).ToListAsync();

  vm.Result.Clear();
  vm.Results.AddRange(await firstTask); //add the first set when they're available
  vm.Results.AddRange(await secondTask); //add the second set when they're available

  return View("Index", vm);
}
公共类过滤器对象
{
公共可液化ApplyTo(可液化物品)
{
退货项目
.Where(item=>item.Property1.Contains(this.Property1))
.Where(item=>item.Other==this.OtherString)
//更多疯狂的Where子句;你明白了
}
}

从您拥有的IQueryable中删除所有的
任务
工作。这太多了。保持简单。您有一个查询。它将返回一个
IQueryable
。然后使用
ToListSync
异步获取结果:

public async Task<IActionResult> Foo(MyViewModel vm)
{
  if (!ModelState.IsValid) return View(vm);

  var filter = new FilterObject(vm);
  var firstTask = _firstContext.YourQueryable.ToListAsync();
  var secondTask = _secondContext.YourQueryable.ToListAsync();    

  var firstResult = await firstTask;
  var secondResult = await secondTask;

  vm.Result.Clear();
  vm.Results.AddRange(firstResult);
  vm.Results.AddRange(secondResult);

  return View("Index", vm);
}
公共异步任务Foo(MyViewModel虚拟机)
{
如果(!ModelState.IsValid)返回视图(vm);
var过滤器=新过滤器对象(vm);
var firstTask=_firstContext.YourQueryable.toListSync();
var secondTask=_secondContext.YourQueryable.toListSync();
var firstResult=等待第一个任务;
var secondResult=等待第二个任务;
vm.Result.Clear();
vm.Results.AddRange(firstResult);
vm.Results.AddRange(secondResult);
返回视图(“索引”,vm);
}

从您拥有的IQueryable中删除所有的
任务
工作。这太多了。保持简单。您有一个查询。它将返回一个
IQueryable
。然后使用
ToListSync
异步获取结果:

public async Task<IActionResult> Foo(MyViewModel vm)
{
  if (!ModelState.IsValid) return View(vm);

  var filter = new FilterObject(vm);
  var firstTask = _firstContext.YourQueryable.ToListAsync();
  var secondTask = _secondContext.YourQueryable.ToListAsync();    

  var firstResult = await firstTask;
  var secondResult = await secondTask;

  vm.Result.Clear();
  vm.Results.AddRange(firstResult);
  vm.Results.AddRange(secondResult);

  return View("Index", vm);
}
公共异步任务Foo(MyViewModel虚拟机)
{
如果(!ModelState.IsValid)返回视图(vm);
var过滤器=新过滤器对象(vm);
var firstTask=_firstContext.YourQueryable.toListSync();
var secondTask=_secondContext.YourQueryable.toListSync();
var firstResult=等待第一个任务;
var secondResult=等待第二个任务;
vm.Result.Clear();
vm.Results.AddRange(firstResult);
vm.Results.AddRange(secondResult);
返回视图(“索引”,vm);
}

为什么要使用
任务。运行
进行I/O工作?
async/await
是您的朋友。在尝试创建自己的动态筛选机制之前,您需要了解EF Core和async/await。IQueryable只是查询,因此没有理由在后台运行它。如果您查看ASP.NET Core,请参阅EF Core教程您将看到代码很简单,异步和同步操作之间的唯一区别是
ToListAsync()
vs
ToList
调用。它是
ToListAsync()或
ToList()这实际上是在执行查询除非它们被注入,否则您肯定要在这两个上下文周围包装一个using语句。@user10728126。如果您想这样做,您可以这样做,但这并不重要。@Gabriel Luci如果您不使用容器,它将如何处理?为什么您要使用
Task.Run
进行I/O工作?
async/wait
是您的首选项这里是朋友。在创建之前,您需要了解EF Core和async/await
public async Task<IActionResult> Foo(MyViewModel vm)
{
  if (!ModelState.IsValid) return View(vm);

  var filter = new FilterObject(vm);
  var firstTask = _firstContext.YourQueryable.ToListAsync();
  var secondTask = _secondContext.YourQueryable.ToListAsync();    

  var firstResult = await firstTask;
  var secondResult = await secondTask;

  vm.Result.Clear();
  vm.Results.AddRange(firstResult);
  vm.Results.AddRange(secondResult);

  return View("Index", vm);
}