MVC4 Task<IEnumerable> not awaitable - c#

I'm looking at examples, just trying to understand the await keyword in a MVC AsyncController. I feel like the following should work as I am just trying to return a list asynchronously. This was just an example for understanding the async keyword:
public async Task<ActionResult> Index()
{
var s = await SelectAsync();
return View(s);
}
private async Task<IEnumerable<Student>> SelectAsync()
{
var ctx = new Test.MVC4.Repository.StudentDataContext;
return await ctx.Students.ToList();
}
I get that Task<IEnumerable<Student>> is not awaitable. I was under the impression that Tasks are awaitable.
Updated: What about something like this (assuming the EF code has been abstracted to the .Select method?
public async Task<ActionResult> Index()
{
var s = await SelectAsync();
return View(s);
}
private async Task<IEnumerable<Student>> SelectAsync()
{
return _repo.Select();
}
Or do I also need to use Task.Run inside the SelectAsync method as well? I'm used to doing this sort of thing in client-side so I appreciate the help here with these methods...

You are calling awaiton ctx.Students.ToList(). This method (ToList()) does not return a Task and therefor is not awaitable.

Related

Calling non-async methods

Have a class library that makes use of a DbContext to return results from sql.
If I want to build a
Class library method that might take a few seconds. This class is injected into an asp.net core webapp in its Startup
class Util
{
public string DoStuff(string colorVal) {
string ourValue = (from a in ctx.BigTable where a.color == colorVal select a.DoneFlag).FirstOrDefault();
return ourValue;
}
}
Do I need to make this method async also if I intend to use it from code like this
Web project
Util o;
public async Task<IViewComponentResult> InvokeAsync()
{
var item = await GetMatchingColorAsync();
return View(item);
}
private Task<string> GetMatchingColorAsync()
{
string matchingColor = o.DoStuff("red");
return Task.FromResult(matchingColor);
}
Ideally yes. You could even use FirstOrDefaultAsync while you're at it (depending on what your underlying data source is):
public async Task<string> DoStuff(string colorVal) {
string ourValue = await (from a in ctx.BigTable where a.color == colorVal select a.DoneFlag).FirstOrDefaultAsync();
var someColor = await GetMatchingColorAsync();
return ourValue;
}
Microsoft has a series of articles about Asynchronous programming with async and await that are quite well written. They're worth the read.
If you absolutely can't change the calling methods, then you could just synchronously wait:
public string DoStuff(string colorVal) {
string ourValue = (from a in ctx.BigTable where a.color == colorVal select a.DoneFlag).FirstOrDefault();
var someColor = GetMatchingColorAsync().GetAwaiter().GetResult();
return ourValue;
}
Easy right? Except it blocks the thread (you lose the benefit of the asynchronous methods) and you risk deadlocking, as explained in this article: Don't Block on Async Code.
That's Bad™

Non async method, call async method, and need to work with the result

I have a syncronized method and class which is static. I have to call Mandrill and use the result.
My method, looks like this
public static void MyMethod(Association association , Person p)
{
MandrillApi mandrill = new MandrillApi(mandrillAPIKey);
var mail = GetMandrillMessage(fromEmail, clubAdmin.ProfileInfo.Email);
mail.AddGlobalVariable("Key", value);
mail.AddGlobalVariable("Key", value);
mail.AddGlobalVariable("AssociationBaseUrl", SettingsHelper.GetSetting("AssociationBaseUrl"));
mail.AddGlobalVariable("Key", value);
mail.AddGlobalVariable("UserFirstname", clubAdmin.Firstname);
mail.Subject = "Subject goes here";
var messageRequest = new SendMessageTemplateRequest(mail, "template");
var result = mandrill.SendMessageTemplate(messageRequest);
}
I need result.Result. I can't make my method to async.
So is there any way to get result.Result and use it in a new method?
i was thinking to do something like
var messageRequest = new SendMessageTemplateRequest(mail, "template");
var result = mandrill.SendMessageTemplate(messageRequest);
CallMyMethod(result.Result.First().Id)
but this wont work, it will just stop the program. Really need help. Ask for more information. I am really bad at explaining myself.
You are experiencing a deadlock. The correct wait to call asynchronous code from synchronous code is to make your whole call stack (starting at the controller's action method) asynchronous. IMO not doing this is an unreasonable constraint.
Changes:
Make MyMethod return Task instead of void so it can be awaited
Add async to MyMethod
Add await to wait for the result from IMandrilApi.SendMessageTemplate
Rename MyMethod to MyMethodAsync, not required but it follows a commonly accepted naming convention
Make controller method async and return a Task. If the controller is returning something then return Task<T> where T is the current return type.
Controller method awaits the result of MyMethodAsync
public static async Task MyMethodAsync(Association association , Person p)
{
/* your existing code has been removed for brevity as there are no changes in that code */
var result = await mandrill.SendMessageTemplate(messageRequest);
}
Controller method
I had to make an assumption this code but you did state in the comments the call stack is controller → MyMethod. Also note that only the relevant part to the question is shown and I had no other info to go on.
[HttpPost]
public async Task SendMessage()
{
await MyMethodAsync(association, person);
}
You can put your method to a Task and await it
public static object MyMethod(Association association , Person p)
{
MandrillApi mandrill = new MandrillApi(mandrillAPIKey);
var mail = GetMandrillMessage(fromEmail, clubAdmin.ProfileInfo.Email);
mail.AddGlobalVariable("Key", value);
mail.AddGlobalVariable("Key", value);
mail.AddGlobalVariable("AssociationBaseUrl", SettingsHelper.GetSetting("AssociationBaseUrl"));
mail.AddGlobalVariable("Key", value);
mail.AddGlobalVariable("UserFirstname", clubAdmin.Firstname);
mail.Subject = "Subject goes here";
var messageRequest = new SendMessageTemplateRequest(mail, "template");
var result = mandrill.SendMessageTemplate(messageRequest);
return result;
}
[HttpPost]
public async Task<HttpResponseMessage> Post([FromBody]MyObject obj)
{
var task = Task.Run(() => MyMethod(a,p));
var result = await task;
return Request.CreateResponse(...);
}
But async in the controller does not make the user experience async. User will have to wait as long as task finishes. So why do we do async ?

Async timing issue

I am making a call to a repository which wraps EF core, messing about with the contoso example
public async void PopulateFacultySL( object selectedFaculty )
{
var data = await GetUnitOfWork().Faculties.GetAllAsync();
FacultySL = new SelectList( data, "Id", "Name", selectedFaculty );
}
When refreshing the page, sometimes the dropdown list is populated, sometimes it is not. I am late on the tech (Async and await) and trying to learn, I know it is likely to be something silly somewhere and hoping an experienced eye can see what the issue is.
protected DbSet<TEntity> TypedContext => Context.Set<TEntity>();
public virtual Task<List<TEntity>> GetAllAsync()
{
return ReturnAndFilterByInstitutionAsync( TypedContext.AsQueryable() );
}
public Task<List<TEntity>> ReturnAndFilterByInstitutionAsync( IQueryable<TEntity> query )
{
return query.Where( q => q.InstitutionId == InstitutionId ).ToListAsync();
}
Please let me know if you need to see any other class info
Edit:
This is the original calling method from the page
public IActionResult OnGet()
{
PopulateFacultySL(null);
return Page();
}
This then had to change to:
public async Task<IActionResult> OnGet()
{
FacultySL = await GetFacultySL( null );
return Page();
}
to make it accept the await keyword with the revised GetFacultySL from the suggestion below
Avoid async void fire and forget methods.
Reference Async/Await - Best Practices in Asynchronous Programming
Chances are that when PopulateFacultySL is invoked there are times when it is not completing in time to be included in the response before it is sent to the client. Which is why sometimes it is populated and sometimes it is not when the page is refreshed because they are being invoked in parallel.
What you need to do is refactor the method so that it can be awaited.
public async Task PopulateFacultySL(object selectedFaculty = null) {
var data = await GetUnitOfWork().Faculties.GetAllAsync();
FacultySL = SelectList(data, "Id", "Name", selectedFaculty);
}
and await it on page load
public async Task<IActionResult> OnGetAsync() {
await PopulateFacultySL();
return Page();
}
Now the page will wait for the data to be populated before displaying the page.
Reference Introduction to Razor Pages in ASP.NET Core

Is async keyword necessary in the method signature when calling another async method internally?

Here's a C# async code snippet. Is GetAsync the same as GetAsync2? Is GetAsync a right implementation?
public Task<IHttpActionResult> GetAsync()
{
return GetOneAsync();
}
public async Task<IHttpActionResult> GetAsync2()
{
return await GetOneAsync();
}
private async Task<IHttpActionResult> GetOneAsync()
{
using (var httpClient = new HttpClient())
{
await httpClient.GetAsync("http://baidu.com");
}
return Ok();
}
It is not the same. GetAsync does not generate a state machine and does not wait for the result of GetOneAsync, which is the preferred option when the result of the async method is not needed in this method.
The resulting code is more efficient as well as no state machine is generated and no context switch is required.
See Understanding the cost of Async/Await article for more info.

Why is my async function hanging when calling task.run function

I want to make a function async since I'd like to return the results to the UI and don't want to make this hang, but it is anyway.
Can someone tell me why this would be hanging?
public ActionResult Index()
{
return View(FunctionThreeAsync().Result);
}
private async Task<MyType> FunctionThreeAsync()
{
return await FunctionThree();
}
private Task<MyType> FunctionThree()
{
return Task.Run<MyType>(() => { /* code */ });
}
This:
return View(FunctionThreeAsync().Result);
Is deadlocking your code. You shouldn't be blocking on an async method. Instead, mark your method as async, make it return a Task<T> and await the call:
public async Task<ActionResult> DoStuffAsync()
{
return View(await FunctionThreeAsync());
}
Async goes all the way. When you have a method that's async, it will need to be asynchronously waited on, not synchronously blocked on. This means spreading async throughout your code base.

Categories

Resources