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));
}
}
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));
}
}
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With