Reputation: 315
I have a question about good practice with async-await in foreach loop.
My method looks like this
public async Task<List<Category>> GetCategoriesAsync(string partOfName,
CancellationToken ct = default(CancellationToken))
{
string lowerCasePartOfName = partOfName.ToLower();
var categories = await _context.ECATEGORIES
.Where(a => a.NAME.ToLower().Contains(lowerCasePartOfName))
.ProjectTo<Category>()
.ToListAsync(ct);
//version1 #Beginning
var i = 0;
foreach (var parentId in categories)
{
var categoryParent = await _context.ECATEGORIES
.Where(a => a.ID == parentId.ParentId)
.Select(s => s.NAME)
.FirstOrDefaultAsync(ct);
categories[i].CategoryParent = categoryParent;
i++;
}
//version1 #End
//version2 #Beginning
categories.ForEach(async x => x.CategoryParent = await _context.ECATEGORIES
.Where(a => a.ID == x.ParentId)
.Select(s => s.NAME).FirstOrDefaultAsync(ct));
//version2 #End
return categories;
}
Version1 and version2 gives same result but I would like to ask which is better practice for async tasks or maybe none of them.
Thanks in advance.
Upvotes: 0
Views: 276
Reputation: 62213
Both are bad in that the code could be rewritten to make use of a proper join. That would make 1 db call instead of 1 call per category + 1 (for the initial call). But strictly answering your question, it does not matter: pick the one you feel most comfortable with
Problem I had is that, parentId is often duplicated and I couldn't use .Contains
You can use a left join to do the same thing but all in 1 DB call which is cheaper than 1 call per result.
string lowerCasePartOfName = partOfName.ToLower();
var categories = await (from category in _context.ECATEGORIES.Where(a => a.NAME.ToLower().Contains(lowerCasePartOfName))
from parent in _context.ECATEGORIES.Where(parent => parent.ID == category.ParentId).DefaultIfEmpty()
select new Category
{
Id = category.ID,
CategoryParent = parent.NAME,
}).ToListAsync();
If your schema is setup to be case insensitive then you can omit the ToLower
calls as well. You can check this by looking at the COLLATION.
.Where(a => a.NAME.Contains(lowerCasePartOfName))
Upvotes: 1