与EF Core并行运行多次相同的任务

时间:2019-10-30 17:28:37

标签: c# entity-framework-core asp.net-core-2.2

我有一个任务可以为订单生成PDF文件(创建一个PDF大约需要10秒):

public async Task GeneratePDF(Guid Id) {
   var order = await 
      _context
      .Orders
      .Include(order => order.Customer)
      ... //a lot more Include and ThenInclude statements
      .FirstOrDefaultAsync(order ==> order.Id == Id);
   var document = ...  //PDF generated here, takes about 10 seconds
   order.PDF = document ;
   _context.SaveChangesAsync();
}

我尝试了以下操作:

public async Task GenerateAllPDFs() {
   var orderIds = await _context.Orders.Select(order=> order.Id).ToListAsync();
   foreach (var id in orderIds)
   {
      _ = GeneratePDF(id).ContinueWith(t => Console.WriteLine(t.Exception), TaskContinuationOptions.OnlyOnFaulted);
   }
}

这给了我错误:

  

System.ObjectDisposedException:无法访问已处置的对象。导致此错误的常见原因是,处理从依赖项注入中解决的上下文,然后稍后尝试在应用程序中的其他位置使用相同的上下文实例。如果在上下文上调用Dispose()或将上下文包装在using语句中,则可能会发生这种情况。如果您正在使用依赖项注入,则应让依赖项注入容器来处理上下文实例。

如果我按以下方式更改任务...

public async Task GenerateAllPDFs() {
   var orderIds = await _context.Orders.Select(order=> order.Id).ToListAsync();
   foreach (var id in orderIds)
   {
      _ = await GeneratePDF(id);
   }
}

...它按顺序运行每个订单的任务,需要花很多时间才能完成(我有数千个订单,每个订单大约需要10秒)...

如何在上下文中针对所有订单并行运行此任务,以使完成时间比顺序处理少得多?

4 个答案:

答案 0 :(得分:2)

您可以将订单ID映射到任务并等待它们,例如:

public async Task GeneratePDF(Order order) {
   var document = ...  //PDF generated here, takes about 10 seconds
   order.PDF = document ;
}

public async Task GenerateAllPDFs() {
   var orderIds = await _context.Orders.ToListAsync();
   var tasks = orderIds.Select((order) => GeneratePDF(order).ContinueWith(t => Console.WriteLine(t.Exception), TaskContinuationOptions.OnlyOnFaulted));
   await Task.WhenAll(tasks);
   await _context.SaveChangesAsync();
}

答案 1 :(得分:1)

这是我从评论中提出的建议作为答案。我将其分为三个部分:

1)获得所有订单,

2)然后执行Parallel.Foreach并行生成所有文档。并按正确顺序分配每个文档,最后

3)进行一次_context.SaveChangesAsync();来对服务器上的数据进行批量更新

public async Task GenerateAllPDFs()
{
    var allOrders = await _context.Orders.ToListAsync();
    System.Threading.Tasks.Parallel.ForEach(allOrders, order => 
    {
        var document = ...  //PDF generated here, takes about 10 seconds
        order.PDF = document ;
    });
    await _context.SaveChangesAsync();
}

答案 2 :(得分:0)

您需要实现并行编程。

https://docs.microsoft.com/en-us/dotnet/standard/parallel-programming/task-based-asynchronous-programming

 Private Sub Form_Load()

     Dim db As DAO.Database
     Dim qdfSelectQuery As DAO.queryDef
     Dim argumentsStringArray() As String

  If Len(Me.OpenArgs) > 0 Then

     argumentsStringArray = Split(Me.OpenArgs, "|")

    …...Blah blah initialize some variables using the argumentsStringArray Blah blah …..;
  End If

 End Sub

答案 3 :(得分:0)

我想我必须“复制” GeneratePDF方法以通过实现其他答案来方便进行批处理,因为我也需要在非批处理模式下使用此方法...