Entity framework Async vs Parallel.Invoke vs Task.WhenAll用于获取数据的EntityF6查询,在ASP MVC 5 web app中

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=_

我试图找出除了同步编程之外,执行一些检索数据的EF6查询的最佳方法。我将在这里发布所有5种方法(它们发生在控制器操作中):

//不“异步”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个不同的线程来处理这些任务
  • 实际上,Task.Run()将与Invoke.Parallel一样,将工作分散到两个不同的CPU上,使它们并行运行
  • 先启动然后等待(C.1示例)不应该让它们以某种并行方式运行吗
  • 不使用异步或并行会更好吗

  • 请让我了解这些例子,如果我对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();