I'm not sure if this code is Asynchronous. I call this function using await from my main controller and within the function I use await on the LINQ query and .ToListAsync() - but after the query I have the foreach loop which may defeat the purpose of async on the query.
Main Controller Call:
case "getassets":
reply = await GetAssets();
break;
Function:
public async Task<ReplyObj> GetAssets()
{
ReplyObj obj = new ReplyObj();
obj.Result = new List<dynamic>();
dynamic AssetRecords = await _context.Asset.FromSql("SELECT * FROM Asset").ToListAsync();
foreach (var objAsset in AssetRecords)
{
obj.Result.Add(new Asset()
{
AssetId = objAsset.AssetId,
Name = objAsset.Name,
Description = objAsset.Description,
PriceDecimals = objAsset.PriceDecimals
});
}
obj.Success = true;
obj.Message = "";
return obj;
}
This call will have many requests hitting it, I want to know for sure that its using async correctly. Thank you!
To begin, here's a couple of references for async/await in C# that I'd suggest reviewing:
Microsoft Docs
SO Community Answer
The simple (high-level) answer is that awaiting your sql call will return control up the call stack and continue execution. In this case, that means it will go up to:
reply = await GetAssets();
Which will in turn return control to whatever function called that, etc. etc..
With that said, if all of your async calls in your call stack are immediately being awaited, then async won't end up buying you anything/changing the flow of control. To say, keep in mind that async != threading.
Few things that I want to point out:
dynamic AssetRecords = await _context.Asset.FromSql("SELECT * FROM Asset").ToListAsync(); When you are using _context.Asset it will return all the Asset rows that you have. Why would you execute another query on the table when the Asset it self is giving all that you need? Hence, to me this is redundant thing.
And if you use select method and then get the list asyncronously then you will have eliminated the the foreach loop and it will cost only one await call and query processing. See code sample below:
public async Task<ReplyObj> GetAssets()
{
ReplyObj obj = new ReplyObj();
return obj.Result = await _context.Asset.Select(s => new
{
AssetId = s.AssetId,
Name = s.Name,
Description = s.Description,
PriceDecimals = s.PriceDecimals
}).ToListAsync();
}
This could now be your true async method.
PS:
If you want to show your code to experts for review, I would suggest joining StackExchange CodeReview Community
Related
I have the following code, that I intend to run asynchronously. My goal is that GetPictureForEmployeeAsync() is called in parallel as many times as needed. I'd like to make sure that 'await' on CreatePicture does not prevent me from doing so.
public Task<Picture[]> GetPictures(IDictionary<string, string> tags)
{
var query = documentRepository.GetRepositoryQuery();
var employees = query.Where(doc => doc.Gender == tags["gender"]);
return Task.WhenAll(employees.Select(employee => GetPictureForEmployeeAsync(employee, tags)));
}
private Task<Picture> GetPictureForEmployeeAsync(Employee employee, IDictionary<string, string> tags)
{
var base64PictureTask = blobRepository.GetBase64PictureAsync(employee.ID.ToString());
var documentTask = documentRepository.GetItemAsync(employee.ID.ToString());
return CreatePicture(tags, base64PictureTask, documentTask);
}
private static async Task<Picture> CreatePicture(IDictionary<string, string> tags, Task<string> base64PictureTask, Task<Employee> documentTask)
{
var document = await documentTask;
return new Picture
{
EmployeeID = document.ID,
Data = await base64PictureTask,
ID = document.ID.ToString(),
Tags = tags,
};
}
If I understand it correctly, Task.WhenAll() is not affected by the two awaited tasks inside CreatePicture() because GetPictureForEmployeeAsync() is not awaited. Am I right about this? If not, how should I restructure the code to achieve what I want?
I'd like to make sure that 'await' on CreatePicture does not prevent me from doing so.
It doesn't.
If I understand it correctly, Task.WhenAll() is not affected by the two awaited tasks inside CreatePicture() because GetPictureForEmployeeAsync() is not awaited. Am I right about this?
Yes and no. The WhenAll isn't limited in any way by the awaited tasks in CreatePicture, but that has nothing to do with whether GetPictureForEmployeeAsync is awaited or not. These two lines of code are equivalent in terms of behavior:
return Task.WhenAll(employees.Select(employee => GetPictureForEmployeeAsync(employee, tags)));
return Task.WhenAll(employees.Select(async employee => await GetPictureForEmployeeAsync(employee, tags)));
I recommend reading my async intro to get a good understanding of how async and await work with tasks.
Also, since GetPictures has non-trivial logic (GetRepositoryQuery and evaluating tags["gender"]), I recommend using async and await for GetPictures, as such:
public async Task<Picture[]> GetPictures(IDictionary<string, string> tags)
{
var query = documentRepository.GetRepositoryQuery();
var employees = query.Where(doc => doc.Gender == tags["gender"]);
var tasks = employees.Select(employee => GetPictureForEmployeeAsync(employee, tags)).ToList();
return await Task.WhenAll(tasks);
}
As a final note, you may find your code cleaner if you don't pass around "tasks meant to be awaited" - instead, await them first and pass their result values:
async Task<Picture> GetPictureForEmployeeAsync(Employee employee, IDictionary<string, string> tags)
{
var base64PictureTask = blobRepository.GetBase64PictureAsync(employee.ID.ToString());
var documentTask = documentRepository.GetItemAsync(employee.ID.ToString());
await Task.WhenAll(base64PictureTask, documentTask);
return CreatePicture(tags, await base64PictureTask, await documentTask);
}
static Picture CreatePicture(IDictionary<string, string> tags, string base64Picture, Employee document)
{
return new Picture
{
EmployeeID = document.ID,
Data = base64Picture,
ID = document.ID.ToString(),
Tags = tags,
};
}
The thing to keep in mind about calling an async method is that, as soon as an await statement is reached inside that method, control immediately goes back to the code that invoked the async method -- no matter where the await statement happens to be in the method. With a 'normal' method, control doesn't go back to the code that invokes that method until the end of that method is reached.
So in your case, you can do the following:
private async Task<Picture> GetPictureForEmployeeAsync(Employee employee, IDictionary<string, string> tags)
{
// As soon as we get here, control immediately goes back to the GetPictures
// method -- no need to store the task in a variable and await it within
// CreatePicture as you were doing
var picture = await blobRepository.GetBase64PictureAsync(employee.ID.ToString());
var document = await documentRepository.GetItemAsync(employee.ID.ToString());
return CreatePicture(tags, picture, document);
}
Because the first line of code in GetPictureForEmployeeAsync has an await, control will immediately go right back to this line...
return Task.WhenAll(employees.Select(employee => GetPictureForEmployeeAsync(employee, tags)));
...as soon as it is invoked. This will have the effect of all of the employee items getting processed in parallel (well, sort of -- the number of threads that will be allotted to your application will be limited).
As an additional word of advice, if this application is hitting a database or web service to get the pictures or documents, this code will likely cause you issues with running out of available connections. If this is the case, consider using System.Threading.Tasks.Parallel and setting the maximum degree of parallelism, or use SemaphoreSlim to control the number of connections used simultaneously.
I'm introducing asynchronous programming to my existing code base and having some trouble with calling Select() on the result of GetStudents() - the error message received is as follows "Task<List<ApplicationUser>> does not contain a defintion for Select". I think it'll be due to incorrect syntax, but any guidance would be appreciated - thanks.
public async Task<List<ApplicationUser>> GetStudents()
{
return await Task.Run(() => _context.Users.ToList());
}
public async Task<StudentIndexViewModel> CreateStudentRegisterViewModel()
{
var model = new StudentIndexViewModel();
var students = await _studentRepo.GetStudents().
Select(x => new StudentViewModel
{
Forename = x.Forename,
Surname = x.Surname
}).ToListAsync();
model.Students = students;
return model;
}
As it was mentioned, the error comes from trying to call Select on a Task<T>, which is not valid. However, the problem is much bigger than that. The code is currently getting the entire table from the database just to get a few values from the result in-memory. This is a waste of processing time in both the database and the application server.
Not only that, but also using a thread pool thread just to wait on an I/O operation is another waste.
Overall, the code should be something like this.
public async Task<List<ApplicationUser>> GetApplicationUsersAsync()
{
// use Entity Framework properly with ToListAsync
// this returns the entire table
return await _context.Users.ToListAsync();
}
public async Task<List<StudentViewModel>> GetStudentsAsync()
{
// use Entity Framework properly with ToListAsync
return await _context.Users
// this only returns the 2 needed properties
.Select(x => new StudentViewModel
{
Forename = x.Forename,
Surname = x.Surname
})
.ToListAsync();
}
public async Task<StudentIndexViewModel> CreateStudentRegisterViewModel()
{
var model = new StudentIndexViewModel();
model.Students = await _studentRepo.GetStudentsAsync();
return model;
}
_studentRepo.GetStudents() returns a Task<List<...>>.
As the error is telling you, Task isn't a collection and doesn't have a Select() method.
You can use await to get the collection inside the task, but you need to call Select() on the awaited value (your code currently awaits Select()).
You need to add parentheses:
(await ...).Select(...);
I am return a list which basically calls the two async operations:
[HttpPost]
public ActionResult List(DataSourceRequest command, ProductListModel model)
{
var categories = _productService.GetAllProducts(model.SearchProductName,
command.Page - 1, command.PageSize);
var gridModel = new DataSourceResult
{
Data = categories.Select(async x =>
{
var productModel = x.ToModel();
var manufacturer = await _manufacturerService.GetManufacturerById(x.ManufacturerId);
var category = await _categoryService.GetCategoryById(x.CategoryId);
productModel.Category = category.Name;
productModel.Manufacturer = manufacturer.Name;
return productModel;
}),
Total = categories.TotalCount
};
return Json(gridModel);
}
It is an ajax request (from client side) but on front-end it never returns. Is there any a deadlock ?
Building up my answer from several comments and #usr's answer:
Data in the code above is actually IEnumerable<Task<ProductModel>>, not IEnumerable<ProductModel>. This is because the lambda passed to Select is async.
Most likely, the JSON serializer is going over this structure and enumerating the properties on the Task<ProductModel> instances, including Result.
I explain on my blog why accessing Result will cause a deadlock in this case. In a nutshell, it's because the async lambda will attempt to resume execution on the ASP.NET request context after its await. However, the ASP.NET request context is blocked on the call to Result, locking a thread inside that request context until the Task<T> completes. Since the async lambda cannot resume, it cannot complete that task. So both things are waiting for each other, and you get a classic deadlock.
There are some suggestions to use await Task.WhenAll, which I would normally agree with. However, in this case you're using Entity Framework and got this error:
A second operation started on this context before a previous asynchronous operation completed.
This is because EF cannot perform multiple asynchronous calls concurrently within the same db context. There are a couple ways around this; one is to use multiple db contexts (essentially multiple connections) to do the calls concurrently. IMO a simpler way is to make the asynchronous calls sequentially instead of concurrently:
[HttpPost]
public async Task<ActionResult> List(DataSourceRequest command, ProductListModel model)
{
var categories = _productService.GetAllProducts(model.SearchProductName,
command.Page - 1, command.PageSize);
var data = new List<ProductModel>();
foreach (var x in categories)
{
var productModel = x.ToModel();
var manufacturer = await _manufacturerService.GetManufacturerById(x.ManufacturerId);
var category = await _categoryService.GetCategoryById(x.CategoryId);
productModel.Category = category.Name;
productModel.Manufacturer = manufacturer.Name;
data.Add(productModel);
}
var gridModel = new DataSourceResult
{
Data = data,
Total = categories.TotalCount
};
return Json(gridModel);
}
The way to debug is is to pause the debugger during the hang. There you will find that some serializer is blocking on Task.Result or similar.
You fill the Data property with a IEnumerable<Task>. The serializer probably calls Result at some point and that's the classic ASP.NET deadlock. You probably should wrap the Select call with await Task.WhenAll(x.Select(...)).
And even then it might be unsafe to concurrently run those lambdas.
I have an online database from Parse.com. There's a table "Vakantie", that contains 3 objects, so the table data isn't big.
I'm trying to get all the objects from that table so I can display them through my VakantieController in a View.
Parse has an own documentation where sample code is provided. I copied their sample code and edited it so that it fits for my website.
The сode:
VakantieController:
public ActionResult Index()
{
var vakantiesTask = vakantieRepository.FindAll();
IEnumerable<Vakantie> vakanties = vakantiesTask.Result;
return View(vakanties);
}
VakantieRepository (class that contains code to retrieve from en write to the database):
public async Task<IList<Vakantie>> FindAll()
{
var query = from v in ParseObject.GetQuery("Vakantie")
orderby v.Get<string>("titel") ascending
select v;
IEnumerable<ParseObject> objects = await query.FindAsync();
IList<Vakantie> vakanties = new Vakantie[] { };
foreach (ParseObject vakantieObject in objects)
{
Vakantie vakantie = GetVakantie(vakantieObject);
vakanties.Add(vakantie);
}
return vakanties;
}
The method `GetVakantie(vakantieObject() is a method I wrote that converts the ParseObject I get from the query result to a Vakantie object from my domain.
When I run this code, I get until the line:
IEnumerable<ParseObject> objects = await query.FindAsync();
and when Visual Studio actually performs this command, my website just keeps loading and loading. I never get over this line to the next one. It is stuck right there.
Does anyone has a solution for this?
You're running into a classic deadlock scenario that I describe in full on my blog and an MSDN article. In summary, await will capture the current "context" and use that to resume the async method. Furthermore, the ASP.NET context will only allow one thread at a time. So when a request thread is blocked (on Task<T>.Result), the await inside of FindAll cannot resume the async method on that context, and you end up with a deadlock.
To correct it, use async "all the way"; i.e., don't use Task<T>.Result:
public async Task<ActionResult> Index()
{
var vakantiesTask = vakantieRepository.FindAll();
IEnumerable vakanties = await vakantiesTask;
return View(vakanties);
}
I am trying to speed up some code of mine,
I make 2 or 3 reads to a slow database, and I want to make these calls run in paralle.
FSKWebInterfaceEntities dbSrc = new FSKWebInterfaceEntities();
public void main()
{
var TaskUsr = GetUserAsync(dev);
var TaskOldCompany = GetOldCompanyAsync(dev);
await Task.WhenAll();
var usr = TaskUsr.Result;
var oldCompanyName = TaskOldCompany.Result;
.....
use my two variables to insert a new entry into my localdb
}
private async Task<ut_User> GetUserAsync(ut_UserAPNdevices dev)
{
return dbSrc.ut_User.FirstOrDefault(x => x.UserID == dev.UserID);
}
private async Task<String> GetOldCompanyAsync(ut_UserAPNdevices dev)
{
return dbSrc.ut_User.FirstOrDefault(x => x.UserID == dev.UserID).Company;
}
On my two helper methods its is underlined green and said that there is no await, and therefore will run synchronously. However I cant return return await dbSrc.ut_User.FirstOrDefault(x => x.UserID == dev.UserID);
How should I modify my code to make the two reads in parallel?
You can await that line because it is not an async method. You can't really await database calls as they are not thread safe. See this thread for an example of a way to do it, and a better explanation as to why not. You can consider using SqlCommands that have async methods as well.
You cannot use the same EF DbContext concurrently. From the EF Team's statement about thread safety:
For the moment, EF will detect if the developer attempts to execute
two async operations at one time and throw.
You still can call them asynchronously, but sequentially:
public async Task main()
{
var sr = await GetUserAsync(dev);
var oldCompany = await GetOldCompanyAsync(dev);
.....
use my two variables to insert a new entry into my localdb
}
Updated to address the comment:
Could you possible post a simple example of how I would change my
above code to use the multiple dbcontext, As I don't really know the
correct way to do it, I would be coding in the dark.
Something like below. I'm not an expert in EF to assure you this will actually improve the processing time. There may be significant overhead of opening the database connection for each context.
public async Task main()
{
var TaskUsr = GetUserAsync(dev);
var TaskOldCompany = GetOldCompanyAsync(dev);
await Task.WhenAll(TaskUsr, TaskOldCompany);
var usr = TaskUsr.Result;
var oldCompanyName = TaskOldCompany.Result;
.....
use my two variables to insert a new entry into my localdb
}
private async Task<ut_User> GetUserAsync(ut_UserAPNdevices dev)
{
using (var dbSrc = new FSKWebInterfaceEntities())
return dbSrc.ut_User.FirstOrDefault(x => x.UserID == dev.UserID);
}
private async Task<String> GetOldCompanyAsync(ut_UserAPNdevices dev)
{
using (var dbSrc = new FSKWebInterfaceEntities())
return dbSrc.ut_User.FirstOrDefault(x => x.UserID == dev.UserID).Company;
}