我最近发布了另一个关于如何处理数据库中的项目列表的问题,如果流程失败,则重试3次。
问题可以在这里找到:C# process large list of items in a loop and retry if fails
我对收到的问题做了一些修改,这是代码:
我有一个继承ApiController
类的抽象类,我的所有Web Api控制器都继承ApiBaseController
:
在ApiBaseController
中,我定义了使用存储库模式的UnitOfWork,以便CRUD SQL数据库。 UnitOfWork和存储库模式工作正常,我已经测试过它,我没有遇到任何问题。
public abstract class ApiBaseController : ApiController
{
protected UnitOfWork Uow { get; set; }
protected override void Dispose(bool disposing)
{
if (Uow != null && Uow is IDisposable)
{
((IDisposable)Uow).Dispose();
Uow = null;
}
base.Dispose(disposing);
}
}
这里我有一个JobController
,它继承了ApiBaseController
,它继承了ApiController
,使其成为Web Api控制器。
此控制器有一个端点api/endpoint/sendrequests
,调用它时将从数据库中获取所有Jobs
,并以10个批次处理它们。
方法ProcessTaskAsync
将处理从数据库中检索到的每个单独的任务,如果它失败,它将再次尝试处理它2次,直到该任务被忽略。
除了在处理任务后的ProcessTaskAsync
中我尝试使用UnitOfWork await Uow.Results.AddAsync(result);
将任务结果保存到数据库之外,一切都运行良好。在这里它失败了!问题是Uow
对象为空,我不明白为什么。我的想法是因为任务是异步处理的,控制器执行结束意味着控制器被处理掉了所以UnitOfWork。
知道为什么Uow
为空,我该如何解决?
[AllowAnonymous]
[RoutePrefix("api/endpoint")]
public class JobController : ApiBaseController
{
private const int PAGED_LIST_SIZE = 10;
private const int MAX_RETRY_COUNT = 3;
public JobController()
{
Uow = new UnitOfWork();
}
[HttpPost]
[AllowAnonymous]
[Route("sendrequests")]
public async Task<IHttpActionResult> SendRequests()
{
try
{
var items = await Uow.Jobs.GetAllAsync();
await ProcessTasks(items);
return Ok();
}
catch (Exception ex)
{
return BadRequest();
}
}
private async Task ProcessTasks(List<Job> items)
{
var pagedResults = items.Paged<Job>(PAGED_LIST_SIZE);
if (pagedResults != null && pagedResults.Count() > 0)
{
foreach (var collection in pagedResults)
{
List<Task> tasks = new List<Task>();
foreach (var item in collection.ToList())
{
var task = Task.Factory.StartNew(() => ProcessTaskAsync(item));
tasks.Add(task);
}
try
{
await Task.WhenAll(tasks.ToArray());
}
catch (Exception ez)
{
//log here the failed ones
}
}
}
}
private async void ProcessTaskAsync(Job item)
{
if (item.Processed >= MAX_RETRY_COUNT)
{
Debug.WriteLine(string.Format("Max count {0} reached for task: {1} ", item.Processed.ToString(), item.Name));
return;
}
item.Processed++;
try
{
Result result = await item.Process();
if (result != null)
{
await Uow.Results.AddAsync(result);
}
Debug.WriteLine(string.Format("working item name: {0}", item.Name));
}
catch (Exception ex)
{
Debug.WriteLine(string.Format("Exception occured: {0}", ex.Message.ToString()));
ProcessTaskAsync(item);
}
}
这用于将List作为List返回页面返回10.这位于静态类中。
public static IEnumerable<IEnumerable<T>> Paged<T>(this IEnumerable<T> enumerable, int chunkSize = 10)
{
int itemsReturned = 0;
var list = enumerable.ToList();
while (itemsReturned < list.Count)
{
int currentChunkSize = Math.Min(chunkSize, list.Count - itemsReturned);
yield return list.GetRange(itemsReturned, currentChunkSize);
itemsReturned += currentChunkSize;
}
}
答案 0 :(得分:5)
private async void ProcessTaskAsync(Job item)
实际工作方法必须返回您可以等待的Task
。 async void
可能仅适用于顶级UI事件,绝对不是值得期待的工作。
您使用Task.Factory.StartNew
生成此类工作并等待结果。但是你正在等待的只是外部任务(并且没有内部任务,它是void
)开始工作。我认为你的控制器在工作方法使用你的UoW
实例之前就已经处理好了,因为你实际上并没有在Controller方法中等待这项工作。
你应该做的是摆脱Task.Factory.StartNew
(用于并行工作,而不是异步),并在实际工作时使ProcessTaskAsync
返回Task
信号。完成。然后,您可以使用Task
等待那些WhenAll
,它应该按预期工作。
可能是async
相关内容的最佳参考/资源是Stephen Cleary的博客。我强烈建议您阅读TPL文章http://blog.stephencleary.com/