I've got MainLayout.razor
protected override async Task OnInitializedAsync()
{
var authState = await authenticationStateTask;
var user = authState.User;
if (user.Identity.IsAuthenticated)
{
var authToken = await _localStorage.GetItemAsync<string>("authToken");
ApiClient.SetAuthorization(authToken);
Console.WriteLine($"User {user.Identity.Name} is authenticated. {authToken}");
}
else
{
NavigationManager.NavigateTo("Login");
Console.WriteLine("User is NOT authenticated.");
}
}
And site Index.razor
protected override async Task OnInitializedAsync()
{
await base.OnInitializedAsync();
this.Data = await ApiClient.GetData();
}
Unfortunately ApiClient.GetData is run first before I even set the Bearer token. How can I fix it?
A quick answer to this.
Do not load the parent control in async, rather:
protected override void OnInitialized()
And you can also place the child load in this form:
protected override async Task OnAfterRenderAsync(bool firstRender)
I believe I also had a cleaner solution for this a while ago, but I will need to dig through my code to find it for you.
Related
I've developed a Unity App that uses Firebase as a BaaS and Firestore as a Database.
Firebase has a Client SDK to make calls that are usually called from client to server by an URL endpoint.
My concern is how my methods should be implemented to correctly work on client without blocking the user experience, cause if I made a heavy request, my Unity App is blocked, and no interaction is allowed to the user.
This is the code of my client DatabaseManager with the methods to retrieve a User from Firestore:
public class DatabaseManager
{
public DatabaseManager(FirebaseFirestore db)
{
this.db = db;
}
public async Task<User> GetUserByUIDAsync(string uid)
{
string documentID = uid;
return await AsyncGetDocumentFromCollection<User, User_FirestoreData>(COL_ID_USERS, documentID);
}
public async Task<PlainData> AsyncGetDocumentFromCollection<PlainData, FirestoreData>(string collectionID, string documentID) where PlainData : IConvertToFirestore<FirestoreData> where FirestoreData : IConvertToPlainData<PlainData>
{
try
{
DocumentReference docRef = db.Collection(collectionID).Document(documentID);
DocumentSnapshot documentSnapshot = await docRef.GetSnapshotAsync();
if (documentSnapshot.Exists)
{
Debug.Log("Get Document data for document:" + documentSnapshot.Id);
FirestoreData firestoreData = documentSnapshot.ConvertTo<FirestoreData>();
return firestoreData.ToPlainData();
}
else
{
Debug.Log($"Document {documentSnapshot.Id} does not exist!");
}
}
catch (Exception e)
{
Debug.Log(e);
}
return default(PlainData);
}
}
This is a simple call and when it's called from any MonoBehaviouryou couldn't notice the load difference when you call it like:
using UnityEngine.UI;
public class MyMono : MonoBehaviour
{
private void DatabaseManager db;
[SerializedField] private Button button = null;
private void Awake()
{
button.onClick.AddListener(async ()=> await CustomAwakeAsync(db));
}
private async Task CustomAwakeAsync(DatabaseManager db)
{
//if this Async method is heavier, this will block the main UI thread when the button is pressed
await db.GetUserByUIDAsync("xdfipñfajrfiñar");
}
}
But if instead of GetUserByUIDAsync I make a heavy call, or multiple recursive calls my application UI will freeze until it's finished...which is bad.
How should I build my code to avoid these case?
Note:
My easy way to test if it's blocking UI thread is having this class attached to a GameObject with Image component:
using UnityEngine;
public class InfiniteRotate : MonoBehaviour
{
public float speed = 1;
// Update is called once per frame
private void Update()
{
this.gameObject.transform.Rotate(0, 0, 1 * Time.deltaTime * speed);
}
}
If the image stop spinning, means that async/await is blocking the UI thread.
Your code as shown:
private void CustomAwake(DatabaseManager db)
{
await db.GetUserByUIDAsync("xdfipñfajrfiñar");
}
...should be producing the following error:
error CS4033: The 'await' operator can only be used within an async method. Consider marking this method with the 'async' modifier and changing its return type to 'Task'.
Even if somehow you managed to silence this error the method signature private void CustomAwake(DatabaseManager db) clearly indicates that this method is synchronous to the caller.
OP:
But if instead of GetUserByUIDAsync I make a heavy call, my application UI will freeze until it's finished...which is bad.
So if you are calling this from the same thread as Update, FixedUpdate etc (which by the looks of it you are) then you are going to block Unity and thus slow down your game.
If you are going to use async/await then you need to do so all the way back to the original caller.
Make it asynchronous
Change the method to:
private async Task<User> CustomAwake(DatabaseManager db) // Note the async Task
{
return await db.GetUserByUIDAsync("xdfipñfajrfiñar");
}
...and ensure that whatever calls it uses await in order to get the User.
I have no previous Blazor and very little asynchronous programming experience. I'm exploring using Blazor for a google maps project and trying to add a geojson point to the map using the google maps javascript api. I wasn't sure of the best way to do this, so my plan was to update the inner html of a hidden div in a blazor component and reference that value in my google map api call.
When I call AddMapData (which gets a geojson string from my db) to set the html of the inner div in both OnInitializedAsync and OnAfterRenderAsync everything works great:
#page "/map"
#inject IJSRuntime JSRuntime
#using DataAccessLibrary
#inject IGeoData _db
<div id="info-box"></div>
<div hidden id="mapData">#mapData </div>
<h3>Map</h3>
<div id="map" ></div>
#code {
private string mapData;
private List<string> data;
async Task<bool> AddMapData()
{
data = await _db.GetGeoData();
mapData = data.First<string>();
mapData = mapData.Substring(1, mapData.Length - 2);
return true;
}
protected override async Task OnInitializedAsync()
{
await AddMapData();
}
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
await AddMapData();
await JSRuntime.InvokeVoidAsync("initMap", null);
}
}
}
Why do I have to call AddMapData in both? My first thought was to only call it in the OnAfterRenderAsync override, but the div.innerhtml value is "". Any thoughts or input is appreciated.
I fixed this by adding StateHasChanged() after AddMapData() in OnAfterRenderAsync. I no longer override OnInitializedAsync.
protected override async Task OnAfterRenderAsync(bool firstRender)
{
await AddMapData();
StateHasChanged();
if (firstRender)
{
await JSRuntime.InvokeVoidAsync("initMap", null);
}
}
I am making a network call inside code behind constructor. I have to do this because I am passing parameter to constructor, then using those initiate the network call. Because of I am doing this inside the constructor I have to use Task.Run option which is not a good solution. I tried factory method also but I am unable to use it since passing parameters are done by click event. Can anyone suggest me a better solution. Thanks
Class Home{
private async void HomeLandingListView_ItemTapped(object sender, ItemTappedEventArgs e)
{
var item = (HomeLanding)e.Item;
await Navigation.PushAsync(new PolicyDetailsMotor(item.id, p_end_date));
}
}
Network call class
public partial class PolicyDetailsMotor : ContentPage
{
public PolicyDetailsMotor(string id, string p_end_date)
{
InitializeComponent();
this.id = id;
this.p_end_date = p_end_date;
Title = "Motor Policy Details";
Task.Run(async () =>
{
var result = await api_class.getMotorPolicyDetails(id, p_end_date);
});
}
}
For each Method i want create Sync and Async, But without duplicating the code. I f I call an Async Method with other Async Methods inside, is it a correct code?.
public void MethodA(){//1
MethodAAsync().GetAwaiter();
}
public void MethodA(){//2 is it a correct code
MethodB();
MethodC();
...code
...code
...code
MethodD();
MethodE();
}
public async Task MethodAAsync(){
await MethodBAsync(cancellationToken);
await MethodCAsync(cancellationToken);
...code
...code
...code
await MethodDAsync(cancellationToken);
await MethodEAsync(cancellationToken);
}
//1 or 2
Synchronous wrappers for asynchronous methods is an antipattern. First, I recommend that you only support an asynchronous API. But sometimes this isn't possible, e.g., for backwards compatibility reasons.
In that case, I recommend the boolean argument hack, which looks something like this:
public void MethodA() {
MethodACore(sync: true).GetAwaiter().GetResult();
}
public Task MethodAAsync() {
return MethodACore(sync: false);
}
private async Task MethodACore(bool sync) {
if (sync) MethodB(); else await MethodBAsync(cancellationToken);
if (sync) MethodC(); else await MethodCAsync(cancellationToken);
...code
...code
...code
if (sync) MethodD(); else await MethodDAsync(cancellationToken);
if (sync) MethodE(); else await MethodEAsync(cancellationToken);
}
I have a ServiceBusClient class that creates a QueueClient which is used to listen for messages on a bus. I have looked at the following articles to set this up:
Background tasks (Microsoft)
Hosted services (Microsoft)
Async and Await
My ServiceBusClient class that handles the QueueClient looks like this:
public class ServiceBusClient : IServiceBusClient
{
public ServiceBusClient(IEventService eventService, ServiceBusClientOptions options)
{
...
queueClient = new QueueClient(options.ConnectionString, options.QueueName);
}
public void Run()
{
RegisterOnMessageHandler();
}
private void RegisterOnMessageHandler()
{
...
queueClient.RegisterMessageHandler(ProcessMessagesAsync, messageHandlerOptions);
}
private async Task ProcessMessagesAsync(Message message, CancellationToken token)
{
var eventMessage = EventMessage.FromMessage(message);
await eventService.Write(eventMessage);
if (!token.IsCancellationRequested)
{
await queueClient.CompleteAsync(message.SystemProperties.LockToken);
}
}
private Task ExceptionReceivedHandler(ExceptionReceivedEventArgs exceptionReceivedEventArgs)
{
// log errors
...
return Task.CompletedTask;
}
}
I was hoping to launch from an IHostedService or even by extending the BackgroundService. In the examples I find, work is constantly being executed in a while loop which does not fit my scenario since I am only trying to run a single command.
So I created a super simple implementation like this:
protected override async Task ExecuteAsync(CancellationToken cancellationToken)
{
serviceBusClient.Run();
while (!cancellationToken.IsCancellationRequested)
{
// empty loop to keep running for lifetime of pod
}
}
If removing the async I obviously need to return something. I tried Task.CompletedTask but that required me to change the return type to Task<Task>.
If I have the async in place, I will need to await something, but I am not sure what.
This does not feel right. I would assume I would need to change something in the ServiceBusClient, but I am unsure what, since the ProcessMessagesAsync is async and does the heavy lifting in the background from my understanding.
All I want is for my web app to start listening for messages until it dies. How can I do that?
I gave up on using BackgroundService and implemented IHostedService instead.
public class MessageListenerService : IHostedService
{
private readonly IServiceBusClient client;
private readonly ITelemetryClient applicationInsights;
public MessageListenerService(IServiceProvider serviceProvider)
{
client = serviceProvider.GetService<IServiceBusClient>();
applicationInsights = serviceProvider.GetService<ITelemetryClient>();
}
public Task StartAsync(CancellationToken cancellationToken)
{
applicationInsights.TrackTrace(new TraceTelemetry("MessageListenerService is starting"));
client.Run();
return Task.CompletedTask;
}
public Task StopAsync(CancellationToken cancellationToken)
{
applicationInsights.TrackTrace(new TraceTelemetry("MessageListenerService is stopping"));
return client.Stop();
}
}
If you find issues with this code please let me know in the comments and I'll update as appropriate.
In the end we created a console app for it anyway.