Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

The tasks argument included a null value

Tags:

c#

.net

task

I'm using mvc 5 and follow this sample to generate an array Task.

I'm sure database contains 1 row at least with the query. I think I'm getting the problem with Task, because it threw me error message

The tasks argument included a null value.

when I tried:

// I'm sure `cg` was not null and `type` was not empty
var cg = new List<string>();
var type = "";

var db = new MyDbContext();
var list = new List<TopicViewModels>();

if (cg != null && cg.Count > 0)
{
   var tasks = new Task<List<TopicViewModels>>[13];
   byte i = 0;

   while (i < cg.Count)
   {
      string _cg = cg[i];

      tasks[i] = Task.Run(async () => 
      {
         return await db.Topics.Where(m => m.Type == type && m.Category == _cg)
            .ToListAsync();
      });

      i++;
   }

   var continuation = Task.WhenAll(tasks); //the tasks argument included a null value

   // never go to this loop...
   foreach (var topics in continuation.Result)
   {
      topics.ForEach(x => list.Add(x));
   }
}

I've set breakpoint to check array tasks, tasks[0] was not null. It's appened correctly.

Can you explain me why?

UPDATE: (based on @YacoubMassad comment)

await Task.WhenAll(tasks); //same error here...

//never go to this loop, too...
foreach (var task in tasks)
{
   //
}

UPDATE 2: (based on @DavidPine answer)

if (cg != null && cg.Count > 0)
{
   var tasks = new List<Task<List<TopicViewModels>>>();
   cg.ForEach(x =>
   {
      tasks.Add(Task.Run(async () =>
      {
         return await db.Topics.Where(m => m.Type == type && m.Category == x)
            .ToListAsync();
      }));
   });

   foreach (var topics in await Task.WhenAll(tasks.ToArray()))
   {
      topics.ForEach(x => list.Add(x));
   }
}
like image 995
Tân Avatar asked Dec 18 '15 13:12

Tân


2 Answers

There are a few issues here:

1. When working with Task or Task<T> always use async / await if it is available, i.e.; you're working with .NET 4.5.

2. When you invoke Task.WhenAll(tasks), it is possible that there is a task in the array that is null. You need to check that for that and handle it correctly.

3. You need to await the when all to ensure the work is done, await Task.WhenAll(tasks).

var cg = new List<string>();
var type = "";

var db = new MyDbContext();
var list = new List<TopicViewModels>();

if (cg != null && cg.Count > 0)
{
   var tasks = new Task<List<TopicViewModels>>[13];
   byte i = 0;

   while (i < cg.Count)
   {
      string _cg = cg[i];

      tasks[i] = Task.Run(async () => 
      {
         // Isn't it possible that the where clause filters out and returns null?
         return await db.Topics
                        .Where(m => m.Type == type && m.Category == _cg)
                        .ToListAsync();
      });

      i++;
   }

   // Use await keyword to ensure that work is done
   var continuation = await Task.WhenAll(tasks.Where(t => t != null).ToArray());

   // never go to this loop...
   foreach (var topics in continuation.Result)
   {
      topics.ForEach(x => list.Add(x));
   }
}
like image 72
David Pine Avatar answered Sep 22 '22 09:09

David Pine


There are a couple of problems in your code.

First of all, you using naturally asynchronous methods that query a database, you don't need to execute then on a threadpool thread, that's redundant, especially in ASP.NET which already uses thread pool threads to serve requests.

Second of all, you're not awaiting on the task returned by Task.WhenAll, you're synchronously blocking when iterating in the foreach loop using Task.Result.

You're code should be:

var queryTasks = cg.Select(cg => db.Topics.Where(m => m.Type == type && m.Category == cg).ToListAsync());
return await Task.WhenAll(queryTasks);

Do note that that EF DbContext is not thread-safe, and doesn't allow concurrent operations to be executed on it. If that is the case here, you'll need to await each query independently.

like image 31
Yuval Itzchakov Avatar answered Sep 21 '22 09:09

Yuval Itzchakov