与EF Core并行运行同一任务多次

问题描述 投票:0回答:3

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

public async Task GeneratePDF(Guid Id) {
   var order = await _context.Orders.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秒)...

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

c# entity-framework-core asp.net-core-2.2
3个回答
1
投票

这是我在评论中的建议作为答案。我将其分为3部分:

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();
}

1
投票

您可以将订单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();
}

0
投票

您需要实现并行编程。

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

public class Example
{
   public static void Main()
   {
      Task[] taskArray = new Task[10];
      for (int i = 0; i < taskArray.Length; i++) {
         taskArray[i] = Task.Factory.StartNew( (Object obj ) => {
                                                  CustomData data = obj as CustomData;
                                                  if (data == null) 
                                                     return;

                                                  data.ThreadNum = Thread.CurrentThread.ManagedThreadId;
                                               },
                                               new CustomData() {Name = i, CreationTime = DateTime.Now.Ticks} );
      }
      Task.WaitAll(taskArray);     
      foreach (var task in taskArray) {
         var data = task.AsyncState as CustomData;
         if (data != null)
            Console.WriteLine("Task #{0} created at {1}, ran on thread #{2}.",
                              data.Name, data.CreationTime, data.ThreadNum);
      }                     
   }
}
© www.soinside.com 2019 - 2024. All rights reserved.