I recently posted another question about how to process a list of items from a database and retry 3 times if the process fails.
The question can be found here: C# process large list of items in a loop and retry if fails
I made some changes to the answer I received to that question and here is the code:
I have an abstract class which inherits ApiController
class and all my Web Api controllers inherit the ApiBaseController
:
In the ApiBaseController
I have defined the UnitOfWork that uses Repository pattern in order to CRUD the SQL db. The UnitOfWork and repository pattern works fine, I have tested it and I have no problems with it.
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);
}
}
Here I have a JobController
which inherits the ApiBaseController
which inherits the ApiController
that makes it a Web Api Controller.
This controller has one endpoint api/endpoint/sendrequests
which when is called will get all the Jobs
from the db and process them in batches of 10.
The method ProcessTaskAsync
will process every individual task retrieved from database and if it fails then it will try to process it 2 more times until the task will be ignored.
Everything works fine and nice except that in the ProcessTaskAsync
after the task is processed I try to save the result of the task to the database using the UnitOfWork await Uow.Results.AddAsync(result);
. Here it fails! The problem is the Uow
object is null and I don't get it why. I am thinking is because the tasks are processed asynchronously the controller execution ends that means the controller is disposed and so the UnitOfWork.
Any idea why the Uow
is null and how can I solve this?
[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);
}
}
This is used in order to return a List as List paged by 10. This is located into a static class.
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;
}
}