同时执行两个数据库调用(使用两个 DbContext)
Execute Two Database Calls (with Two DbContexts) Simultaneously
我需要从两个不同的 DbContext
(针对 Oracle 数据库的两个不同模式)获取结果来填充页面。我想同时执行两个数据库查询(只读,无写操作),并在它们都完成时 return 结果。麻烦的是,我是平行世界里的连载小子,我不知道杰克 Async
/Await
/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));
}
我的 DbContext
调用过滤器来完成它的工作:
public class FirstContext : DbContext
{
public DbQuery<Items> Items{get;set;}
public Task<IQueryable<Items>> FilterItems(FilterObject filter)
{
filter.ApplyTo(Items.AsQueryable());
}
}
... 并且我的 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
}
}
我这样做是否接近正确?据我了解,对数据库的调用直到 AddRange
方法才会真正执行,因为 Task.Result
是一个 IQueryable
- 所以这对我来说似乎是错误的: AddRange()
方法将同步执行,而不是异步执行,对吗?我所做的只是构建两个查询,而不是执行它们。我猜我需要 return 来自 DbContext 的 List<Item>
,这将导致每个查询实际执行......但这是我需要做的唯一改变,以便这些调用发生在同时,互不阻挡?
一如既往,非常感谢任何指导。
Task.Run
会将任务移至另一个线程,这并不是真正必要的。使用 async
和 await
,这将 运行 同一线程上的所有内容,但允许在等待结果时在同一线程上完成其他工作。例如,在触发第一个查询后,它可以在等待第一个查询完成时启动第二个查询。
好像FilterItems
和ApplyTo
只是在修改查询,并没有实际执行结果,所以我不明白为什么他们需要return一个Task
完全没有。在构建查询后,您可以使用 ToListAsync()
实际执行查询。
例如:
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);
}
您在 FilterItems
中没有 return
,但我认为它应该在那里:
public class FirstContext : DbContext
{
public DbQuery<Items> Items{get;set;}
public IQueryable<Items> FilterItems(FilterObject filter)
{
return filter.ApplyTo(Items.AsQueryable());
}
}
public class FilterObject
{
public IQueryable<Items> ApplyTo(IQueryable<Items> items)
{
return items
.Where(item => item.Property1.Contains(this.Property1))
.Where(item => item.Other == this.OtherString)
// more Where clauses ad nauseum; you get the idea
}
}
从您拥有的 IQueryables 中删除所有 Task
工作。那一层太多了。把事情简单化。你有一个查询。那将 return 和 IQueryable<>
。然后你使用ToListAsync
异步获取结果:
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);
}
我需要从两个不同的 DbContext
(针对 Oracle 数据库的两个不同模式)获取结果来填充页面。我想同时执行两个数据库查询(只读,无写操作),并在它们都完成时 return 结果。麻烦的是,我是平行世界里的连载小子,我不知道杰克 Async
/Await
/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));
}
我的 DbContext
调用过滤器来完成它的工作:
public class FirstContext : DbContext
{
public DbQuery<Items> Items{get;set;}
public Task<IQueryable<Items>> FilterItems(FilterObject filter)
{
filter.ApplyTo(Items.AsQueryable());
}
}
... 并且我的 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
}
}
我这样做是否接近正确?据我了解,对数据库的调用直到 AddRange
方法才会真正执行,因为 Task.Result
是一个 IQueryable
- 所以这对我来说似乎是错误的: AddRange()
方法将同步执行,而不是异步执行,对吗?我所做的只是构建两个查询,而不是执行它们。我猜我需要 return 来自 DbContext 的 List<Item>
,这将导致每个查询实际执行......但这是我需要做的唯一改变,以便这些调用发生在同时,互不阻挡?
一如既往,非常感谢任何指导。
Task.Run
会将任务移至另一个线程,这并不是真正必要的。使用 async
和 await
,这将 运行 同一线程上的所有内容,但允许在等待结果时在同一线程上完成其他工作。例如,在触发第一个查询后,它可以在等待第一个查询完成时启动第二个查询。
好像FilterItems
和ApplyTo
只是在修改查询,并没有实际执行结果,所以我不明白为什么他们需要return一个Task
完全没有。在构建查询后,您可以使用 ToListAsync()
实际执行查询。
例如:
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);
}
您在 FilterItems
中没有 return
,但我认为它应该在那里:
public class FirstContext : DbContext
{
public DbQuery<Items> Items{get;set;}
public IQueryable<Items> FilterItems(FilterObject filter)
{
return filter.ApplyTo(Items.AsQueryable());
}
}
public class FilterObject
{
public IQueryable<Items> ApplyTo(IQueryable<Items> items)
{
return items
.Where(item => item.Property1.Contains(this.Property1))
.Where(item => item.Other == this.OtherString)
// more Where clauses ad nauseum; you get the idea
}
}
从您拥有的 IQueryables 中删除所有 Task
工作。那一层太多了。把事情简单化。你有一个查询。那将 return 和 IQueryable<>
。然后你使用ToListAsync
异步获取结果:
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);
}