1

首先,我不确定我是否在问一些愚蠢的事情,所以在开始之前道歉。创建一个分组几个I/O任务的任务

我有节约使用一个SqlCommand异步在DB实体的集合的方法,并返回表示异步操作的任务,到目前为止,它的作品好。基本上它会为每个实体生成一个SQL命令和一堆参数,并将它们全部添加到一个SqlCommand实例中(参数被编号)

但是,如果我尝试插入很多实体,那么当参数计数达到2100 ,SQL操作失败,因为这是限制。然后,我想批量分割实体集合,并按顺序执行它们,然后返回一个Task,直到所有子任务完成才会完成。 (可能是最后一个)

每个孩子的任务,将获得的Int32说有多少行已经改变,最后的任务必须返回所有这些的总和。所以所有的任务都是Task。如果一个失败,所有这些都必须“回滚”,所以它们必须共享连接和事务对象。

此外,我想确保我正确使用Task和SqlCommand的I/O完成端口,并且没有线程在SQL Server上的操作完成时等待/复制,因为此“保存“操作是从ASP.NET MVC中的异步控制器调用的。

这里的正确方法是什么?

问候。

回答

1

用C#5(并假设基于任务的异步),这将是相当简单:

async Task<IReadOnlyList<SomeType>> PerformUpdatesAsync(
    IEnumerable<AnotherType> data) 
{ 
    var result = new List<SomeType>(); 

    foreach (var batch in data.SplitIntoBatches(BatchSize)) 
     result.Add(await PerformUpdateAsync(batch)); 

    return result; 
} 

在预C#5是困难的,但可以使用队列例如做模拟异步foreach

Task<SomeType[]> PerformUpdatesAsync(IEnumerable<AnotherType> data) 
{ 
    var batches = new Queue<IEnumerable<AnotherType>>(
     data.SplitIntoBatches(BatchSize)); 

    var result = new List<SomeType>(); 

    var tcs = new TaskCompletionSource<SomeType[]>(); 

    AsyncCallback onEndUpdate = null; 
    onEndUpdate = 
     ar => 
     { 
      result.Add(EndUpdate(ar)); 

      if (batches.Count == 0) 
       tcs.SetResult(result.ToArray()); 
      else 
       BeginUpdate(batches.Dequeue(), onEndUpdate, null); 
     }; 

    BeginUpdate(batches.Dequeue(), onEndUpdate, null); 

    return tcs.Task; 
} 

如果你不喜欢使用闭包这种方式,你可以通过创建一个单独的类做同样的事情,将持有的所有局部变量在其领域:批量处理和“递归”拉姆达。

有一个在框架没有SplitIntoBatches(),所以你必须把它写自己(除非你已经做了)。

+0

非常感谢!非常说教。 – vtortola 2012-04-27 13:52:04

0

这是一个很好的问题。这里是一些代码:

Task<int>[] batchTasks = ...;//start the batches here. each task should return the count of changed rows 
Task<int> sumTask = Task.Factory.ContinueWhenAll(batchTasks, _ => { 
return batchTasks.Sum(x => x.Result); 
}); 

这会给你一个无阻塞的个别批次的总和。

当您启动一个批处理一定要使用异步SQL API,如BeginExecuteReader。然后,你将完全无阻塞。

+0

对不起,我做了一个错误,我想运行子任务的顺序,而不是平行的,因为我不能使用并发调用相同的连接。我编辑了这篇文章。基本上,我想返回将是链中最后一个任务并包含总结果的任务。对于那个很抱歉。 – vtortola 2012-04-20 12:40:43

1

如果你要拆分插入,但连续运行它们,你并不需要使用任务为每个呼叫。你只需要正常的顺序代码。

创建一个启动任务并返回它的方法。在任务主体中,只需执行任何你想要的正常顺序代码即可。完成后,退出任务主体并完成单个任务。

+0

好吧,差不多。这样,执行SQL操作的后台线程就会被阻塞。为了避免这种情况,您可以异步编写代码并使用TaskCompletionSource来创建和设置Task的结果。 – svick 2012-04-20 14:28:55

+0

@svick这是我不知道该怎么做。按顺序运行多个异步操作,并获得累积结果... – vtortola 2012-04-20 15:47:14