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
Related
Is this a proper way of using async/await with EF for an API? If not, can you please show me some options? The code is compiling and shows no errors. The application is working fine, but I want to make sure that it runs asynchronous.
public class AdminController : ControllerBase
{
[HttpGet("Users")]
public async Task<IResult> Users()
{
return await Admin.GetUsers();
}
}
public static class Admin
{
internal static async Task<IResult> GetUsers()
{
using var context = new DataBaseContext();
List<User>? _users = context.users.ToList();
return (_users.Count == 0) ?
Results.NotFound() :
Results.Ok(_users);
}
}
Or should I use this instead?
public class AdminController : ControllerBase
{
[HttpGet("Users")]
public Task<IResult> Users()
{
return Admin.GetUsers();
}
}
public static class Admin
{
internal static async Task<IResult> GetUsers()
{
using var context = new DataBaseContext();
List<User>? _users = await context.users.ToListAsync();
return (_users.Count == 0) ?
Results.NotFound() :
Results.Ok(_users);
}
}
The second one is what you want to use. Your first option lacks an async signature meaning you cannot await your static async method. To understand why you must understand what async and await does at runtime. So you have a bunch of threads running in a thread pool and when you have a request come in, a thread gets used to run your code and if its lacking async and await it would run in a synchronous fashion. This means that the thread will be out of that pool until the end of processing. If that admin function took 10 seconds to process, that thread will be locked to that request for 10 seconds. In that same example if you mark it as async and await, your thread goes back into the pool while you await and a callback gets used to say "Hey I'm done" and completes the execution from that await. It becomes more important as your application gets more requests. Hope I explained it well enough
I'm using PutAsJsonAsync in a Blazor WA app.
I can pass objects from client to Server in a hosted app (Server is basically a Web API app)
public async Task Pause(int id)
{
var x = new PartyList
{
Psid = id
};
var c = await _client.PutAsJsonAsync($"api/party/pause", x);
}
[HttpPut("pause")]
public async Task<IActionResult> Pause(PartyList partyList)
{
await _partySingerRepository.PauseAsync(partyList.Psid);
return Ok();
}
However I'd like to pass an int instead of a full object
public async Task Pause(int id)
{
var c = await _client.PutAsJsonAsync($"api/party/pause", id);
}
[HttpPut("pause")]
public async Task<IActionResult> Pause(int id)
{
await _partySingerRepository.PauseAsync(id);
return Ok();
}
However, the server (Web API) method doesn't get hit like this, or if I make id an anonymous type either.
Is it possible to pass in a single int or do I have to create an object?
I couldn't find anything on Google about this.
Thanks
Just had this same issue myself. Not with one parameter, but with two. It didn't work, not even with an anonymous object, until I created a class to hold the values.
I have a server-side blazor client and I'm trying to modify the MainLayout razor page by having a Login check. I'm currently using Blazored for localstorage saving, and I'm currently using to see if a token is saved to see if user is logged in, however I'm not sure how I translate this in the if statement in razor page because it wants async method.
My login check is pretty simple as shown below.
public async Task<bool> IsLoggedIn()
{
return await m_localStorage.ContainKeyAsync("token").ConfigureAwait(false);
}
In my Razor page I'm doing this statement check - which obvious doesn't work as there's no async modifier
#if (!await AppState.IsLoggedIn()) //Requires async modifier
{
Login
}
I've also tried doing it using the .Result property, but this results in an exception thrown: (System.AggregateException: 'Information: Executed an implicit handler method, returned result Microsoft.AspNetC)' with an inner-exception -> NullReferenceException: Object reference not set to an instance of an object.
But from what I can see AppState is injected correctly and the local storage seems to be injected correctly in AppState.
#if (!AppState.IsLoggedIn().Result)
{
Login
}
So my question is what is the correct way to approach this, is there a way to execute async methods in razor pages?
is there a way to execute async methods in razor pages?
No, there isn't a way to use await in a Razor component. This is because you can't do async work as part of the rendering of the component.
Incidentally, the local storage mechanism provided by the Blazor team supports data protection, and is recommended for use by Steve Sanderson.
Note: The async Lifecycle methods of the component are where async work is done, and thus you can design your code accordingly, as for instance, calling AppState.IsLoggedIn() from OnInitializedAsync, and assigning the returned value to a local variable which can be accessed from your views.
AsyncComponent.razor
#typeparam TResult
#typeparam TInput
#if (Result != null)
{
#DataReadyFragment(Result)
}
else if (DataMissingFragment != null)
{
#DataMissingFragment
}
#code {
[Parameter] public RenderFragment<TResult> DataReadyFragment { get; set; }
[Parameter] public RenderFragment DataMissingFragment { get; set; }
[Parameter] public Func<TInput, Task<TResult>> AsyncOperation { get; set; }
[Parameter] public TInput Input { get; set; }
TResult Result { get; set; }
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if(firstRender)
AsyncOperation.Invoke(Input).ContinueWith(t => { Result = t.Result; InvokeAsync(StateHasChanged); });
}
}
Usage
<AsyncComponent TResult="User" TInput="string" Input="Pa$$W0rd" AsyncOperation="#AsyncMethodName">
<DataReadyFragment Context="result">
#if(result.IsLoggedIn)
{
<h3>Logged-In , Username:#result.Name</h3>
}
else
{
<h3>Wrong Password</h3>
}
</DataReadyFragment>
<DataMissingFragment>
<h3>Please Login :)</h3>
</DataMissingFragment>
</AsyncComponent>
Based on LazZiya example, worked for me. In my case an event was not async as supported by a component, but the call I had required await. Using this example I could return data from the call based on a model.
public string Name => Task.Run(() => Service.GetNameAsync()).GetAwaiter().GetResult();
I ended up with this and it worked like a charm.
Task<AssessmentResponsesModel> task = Task.Run(() => _IAssessmentResponse.CreateAsync(APIPathD, argsItem, token).GetAwaiter().GetResult());
I'm working on school project, and I'm trying to implement asynchronous methods in my repository classes and got this error in my API for the POST-method: Argument 1: cannot convert from 'System.Threading.Tasks.Task' to Project.Models.Booking.
Can someone please help me to solve this error?
This is how my repository-method looks like:
public async Task<Booking>CreateBooking(Booking inBooking) {
Booking booking = new Booking();
booking.BookingId = ApplicationDbContext.Bookings.Count();
booking.UserName = inBooking.UserName;
booking.UserMail = inBooking.UserMail;
booking.UserMobile = inBooking.UserMobile;
booking.Available = false;
booking.StartTime = inBooking.StartTime;
booking.EndTime = inBooking.EndTime;
booking.Date = inBooking.Date;
Add(booking);
return await Task.FromResult(booking);
//return booking;
}
And this how my Post-method looks like:
[Route("api/PostBooking")]
[HttpPost]
public async Task<IHttpActionResult> PostBooking(BookingSystemServiceBookingViewModel inBooking)
{
if (!ModelState.IsValid)
{
return BadRequest("Invalid data");
}
var booking = inBooking.booking;
booking.StartTime = inBooking.startTime;
booking.EndTime = inBooking.startTime.AddMinutes(inBooking.service.Duration);
uw.Services.AddBooking(uw.Bookings.CreateBooking(booking), inBooking.service.ServiceId);
uw.Complete();
return Ok();
}
I get the error message at: uw.Services.AddBooking(uw.Bookings.CreateBooking(booking), inBooking.service.ServiceId);`
trying to implement asynchronous methods in my repository classes
The best way to make things asynchronous is not from the "outside in". That is, you don't want to start at the controller action and say "how can I make this asynchronous?". Instead, you want to start from the lowest-level API and say "this should be asynchronous" (i.e., it's I/O-based), and let asynchrony grow out from there.
If Add actually does the database insert, then that would be the first method to be made asynchronous. Then you await your AddAsync method, and the asynchrony grows from AddAsync to CreateBookingAsync:
public async Task<Booking> CreateBookingAsync(Booking inBooking) {
Booking booking = new Booking();
...
await AddAsync(booking);
return booking;
}
[Route("api/PostBooking")]
[HttpPost]
public async Task<IHttpActionResult> PostBooking(BookingSystemServiceBookingViewModel inBooking)
{
...
uw.Services.AddBooking(await uw.Bookings.CreateBookingAsync(booking), inBooking.service.ServiceId);
uw.Complete();
return Ok();
}
If, on the other hand, it is the Complete method that actually communicates with the database, then that is the one that should be made asynchronous. In this case Add is purely an in-memory operation, and Complete is the one that saves all the database updates. So, after you convert Complete to CompleteAsync, your code would look like:
public Booking CreateBooking(Booking inBooking) {
Booking booking = new Booking();
...
Add(booking);
return booking;
}
[Route("api/PostBooking")]
[HttpPost]
public async Task<IHttpActionResult> PostBooking(BookingSystemServiceBookingViewModel inBooking)
{
...
uw.Services.AddBooking(uw.Bookings.CreateBooking(booking), inBooking.service.ServiceId);
await uw.CompleteAsync();
return Ok();
}
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.