Entity framework Async vs Parallel.Invoke vs Task.WhenAll用于获取数据的EntityF6查询,在ASP MVC 5 web app中
我试图找出除了同步编程之外,执行一些检索数据的EF6查询的最佳方法。我将在这里发布所有5种方法(它们发生在控制器操作中):Entity framework Async vs Parallel.Invoke vs Task.WhenAll用于获取数据的EntityF6查询,在ASP MVC 5 web app中,entity-framework,async-await,asp.net-mvc-5,parallel.invoke,Entity Framework,Async Await,Asp.net Mvc 5,Parallel.invoke,我试图找出除了同步编程之外,执行一些检索数据的EF6查询的最佳方法。我将在这里发布所有5种方法(它们发生在控制器操作中): //不“异步”ActionResult会更好吗? 公共异步任务索引{ //我依赖于此,所以我甚至不知道是否可以使其异步->你认为如何? var userinfo=_dataservice.getUserInfo(“John”); //C1:同步方式 var watch1=System.Diagnostics.Stopwatch.StartNew(); var info1=_
//不“异步”ActionResult会更好吗?
公共异步任务索引{
//我依赖于此,所以我甚至不知道是否可以使其异步->你认为如何?
var userinfo=_dataservice.getUserInfo(“John”);
//C1:同步方式
var watch1=System.Diagnostics.Stopwatch.StartNew();
var info1=_getInfoService.GetSomeInfo1(userinfo);
var info2=_getInfoService.GetSomeInfo2(userinfo);
watch1.Stop();
var t1=watch.ellapsedmillisons;//这大约需要3200秒
//C2:异步方式
var watch2=System.Diagnostics.Stopwatch.StartNew();
var infoA1=await\u getInfoService.GetSomeInfoAsync1(userinfo).ConfigureAwait(false);
var infoA2=await _getInfoService.GetSomeInfoAsync2(userinfo).ConfigureAwait(false);
watch2.Stop();
var t2=watch2.ellapsedmillisons;//这大约需要3020秒
//C2.1:异步方式发射,然后等待
var watch21=System.Diagnostics.Stopwatch.StartNew();
var infoA21=_getInfoService.GetSomeInfoAsync1(userinfo).ConfigureAwait(false);
var infoA22=_getInfoService.GetSomeInfoAsync2(userinfo).ConfigureAwait(false);
//我想,如果我先启动它们,然后等待,它会运行得更快……但不会
var a=等待信息A21;
var b=等待信息A22;
watch21.Stop();
var t21=watch21.ellapsedmillisons;//这大约需要30201秒
//C3:与Task.Run()和await.WhenAll()异步
var watch1=System.Diagnostics.Stopwatch.StartNew();
var infoT1=TaskRun(()=>getInfoService.GetSomeInfo1(userinfo));
var infoT2=TaskRun(()=>getInfoService.GetSomeInfo2(userinfo));
等待任务。WhenAll(infoT1,infoT2)
观察3.停止();
var t3=watch3.ellapsedmillisons;//这大约需要2010年
//C4:并行方式
mytypevar1;mytype2var2;
var watch4=System.Diagnostics.Stopwatch.StartNew();
并行调用(
()=>var1=\u getInfoService.GetSomeInfoAsync1(userinfo).GetAwaiter().GetResult(),//也只使用\u getInfoService.GetSomeInfo1(userinfo)-但有时会在F10调试时抛出实体错误
()=>var2=\u getInfoService.GetSomeInfoAsync2(userinfo).GetAwaiter().GetResult()//也只使用\u getInfoService.GetSomeInfo2(userinfo)-但有时会在F10调试时抛出实体错误
);
watch4.Stop();
var t4=watch4.ellapsedmillisons;//这大约需要2012年
}
实施方法:
public MyType1 GetSomeInfo1(SomeOtherType param){
// result = some LINQ queries here
Thread.Sleep(1000);
return result;
}
public MyType2 GetSomeInfo2(SomeOtherType param){
// result = some LINQ queries here
Thread.Sleep(2000);
return result;
}
public Task<MyType1> GetSomeInfoAsync1(SomeOtherType param){
// result = some LINQ queries here
Thread.Sleep(1000);
return Task.FromResult(result);
}
public Task<MyType2> GetSomeInfoAsync2(SomeOtherType param){
// result = some LINQ queries here
Thread.Sleep(2000);
return Task.FromResult(result);
}
public MyType1 GetSomeInfo1(SomeOtherType参数){
//result=此处的一些LINQ查询
睡眠(1000);
返回结果;
}
公共MyType2 GetSomeInfo2(SomeOtherType参数){
//result=此处的一些LINQ查询
《睡眠》(2000年);
返回结果;
}
公共任务GetSomeInfoAsync1(SomeOtherType参数){
//result=此处的一些LINQ查询
睡眠(1000);
返回Task.FromResult(结果);
}
公共任务GetSomeInfoAsync2(SomeOtherType参数){
//result=此处的一些LINQ查询
《睡眠》(2000年);
返回Task.FromResult(结果);
}
await
for 2个任务(如C2和C2.1)不会使它们并行运行(甚至在我先启动它们然后等待的C.1示例中也不会),它只是释放当前线程并将它们交给另外2个不同的线程来处理这些任务请让我了解这些例子,如果我对EntityF有任何的暗示,我又如何能有更好的表现。我已经阅读了几天了,我只是感到困惑,所以请不要再给我其他链接阅读:)
async
代码可以通过调用而不调用wait
,然后等待任务。WaitAll()
来混合并行性。然而,在研究并行性时,主要考虑的是确保调用的代码是线程安全的。DbContext不是线程安全的,所以要运行并行操作,需要为每个方法分别提供DbContext实例。这意味着,通常依赖依赖依赖项注入来接收DbContext/工作单元并将获得一个终生范围内的引用(如web请求)的代码不能用于并行化调用。并行化的调用需要有一个DbContext,其作用域仅限于该调用
在处理使用EF实体的并行方法时,这也意味着您需要确保将任何实体引用视为分离的实体。它们不能安全地相互关联,就好像它们是由不同并行任务中的不同DBContext返回的一样
例如,使用普通的async
和wait
:
var order = await Repository.GetOrderById(orderId);
var orderLine = await Repository.CreateOrderLineForProduct(productId, quantity);
order.OrderLines.Add(orderLine);
await Repository.SaveChanges();
作为一个非常基本的示例,repository类将注入DbContext。CreateOrderLine方法将使用DbContext加载产品,并可能使用其他详细信息生成订单行。等待时,async
变量确保一次只有一个线程访问DbContext,因此存储库可以使用同一个DbContext实例。订单、新订单行、产品等都由同一DbContext实例跟踪,因此存储库针对该单个实例发出的SaveChanges
调用将按预期工作
如果我们尝试将其并行化,如:
var orderTask = Repository.GetOrderById(orderId);
var orderLineTask = Repository.CreateOrderLineForProduct(productId, quantity);
await Task.WhenAll(orderTask, orderLineTask);
var order = orderTask.Result;
var orderLine = orderLineTask.Result;
order.OrderLines.Add(orderLine);
await Repository.SaveChanges();
这可能会导致EF的异常,即DbContext作为GetOrderById和ca跨线程访问
var orderTask = Repository.GetOrderById(orderId);
var orderLineTask = Repository.CreateOrderLineForProduct(productId, quantity);
await Task.WhenAll(orderTask, orderLineTask);
var order = orderTask.Result;
var orderLine = orderLineTask.Result;
order.OrderLines.Add(orderLine);
await Repository.SaveChanges();
public Order GetOrderById(int orderId)
{
using(var context = new AppDbContext())
{
return context.Orders
.Include(x=>x.OrderLines)
.AsNoTracking()
.Single(x => x.OrderId == orderId);
}
}
Repository.Save(order);
var query = Repository.GetOrders()
.Where(x => x.OrderStatus.OrerStatusId == OrderStatus.New
&& x.DispatchDate <= DateTime.Today());
if (searchCriteria.Any())
query = query.Where(buildCriteria(searchCriteria));
var pendingOrders = await query.Skip(pageNumber * pageSize)
.Take(PageSize)
.ProjectTo<OrderSearchResultViewModel>()
.ToListAsync();