How to await for an PostAsync method in xamarin? - c#

I am trying to do something like await client.PostAsync() in a xamarin project, but it does not waits. How can I make it wait ? The same code in winforms project does waits.
my code
private bool ConfirmLogin(string user, string password)
{
bool result = false;
AppLoginRequest appLoginRequest = new AppLoginRequest();
AppLoginResponse appLoginResponse = new AppLoginResponse();
// step 1
Task<HttpResponseMessage> task = GetAppLogin(appLoginRequest, appLoginResponse);
// step 4
result = appLoginResponse.Authorized;
return result;
}
and the code for GetAppLogin
private async Task<HttpResponseMessage> GetAppLogin(AppLoginRequest appLoginRequest, AppLoginResponse appLoginResponse)
{
HttpResponseMessage response = null;
string JsonData = JsonConvert.SerializeObject(appLoginRequest);
System.Net.Http.StringContent restContent = new StringContent(JsonData, Encoding.UTF8, "application/json");
HttpClient client = new HttpClient();
try
{
// step 2
response = await client.PostAsync(#"http://x.x.x.x:xxxx/api/XXX/GetAppLogin", restContent);
// step 3
if (response.IsSuccessStatusCode)
{
var stream = await response.Content.ReadAsStringAsync();
AppLoginResponse Result = JsonConvert.DeserializeObject<AppLoginResponse>(stream);
}
else
{
appLoginResponse.Remark = response.ReasonPhrase;
}
}
catch (Exception ex)
{
appLoginResponse.Authorized = false;
appLoginResponse.Remark = ex.Message;
}
return response;
}
What I need is to execute the steps (// step x in the comments of the code) in the correct sequential order.
Step 1, then step 2, then step 3 and finally step 4
But it executes like this
step 1 then step 2 and then step 4 and step 3
This off course messes up the complete logic, is there a way I can force the client.PostAsync to actually really wait ?
I found hundreds of questions on how to run an async task synchronously, but all seem to not work.
Is xamarin different about this ?
I run the exact same code from a testclient written in winforms and there it does waits.

async void ButtonClick(...)
{
bool x = await ConfirmLogin(..., ...);
}
private async Task<bool> ConfirmLogin(string user, string password)
{
bool result = false;
AppLoginRequest appLoginRequest = new AppLoginRequest();
AppLoginResponse appLoginResponse = new AppLoginResponse();
// step 1
await GetAppLogin(appLoginRequest, appLoginResponse);
// step 4
result = appLoginResponse.Authorized;
return result;
}
Do not use async void when you can avoid it. It is a cop-out especially intended for eventhandlers. "To get the async going"

Related

How to get the individual API call status success response in C#

How to get the individual API call status success response in C#.
I am creating a mobile application using Xamarin Forms,
In my application, I need to prefetch certain information when app launches to use the mobile application.
Right now, I am calling the details like this,
public async Task<Response> GetAllVasInformationAsync()
{
var userDetails = GetUserDetailsAsync();
var getWageInfo = GetUserWageInfoAsync();
var getSalaryInfo = GetSalaryInfoAsync();
await Task.WhenAll(userDetails,
getWageInfo,
getSalaryInfo,
);
var resultToReturn = new Response
{
IsuserDetailsSucceeded = userDetails.Result,
IsgetWageInfoSucceeded = getWageInfo.Result,
IsgetSalaryInfoSucceeded = getSalaryInfo.Result,
};
return resultToReturn;
}
In my app I need to update details based on the success response. Something like this (2/5) completed. And the text should be updated whenever we get a new response.
What is the best way to implement this feature? Is it possible to use along with Task.WhenAll. Because I am trying to wrap everything in one method call.
In my app I need to update details based on the success response.
The proper way to do this is IProgress<string>. The calling code should supply a Progress<string> that updates the UI accordingly.
public async Task<Response> GetAllVasInformationAsync(IProgress<string> progress)
{
var userDetails = UpdateWhenComplete(GetUserDetailsAsync(), "user details");
var getWageInfo = UpdateWhenComplete(GetUserWageInfoAsync(), "wage information");
var getSalaryInfo = UpdateWhenComplete(GetSalaryInfoAsync(), "salary information");
await Task.WhenAll(userDetails, getWageInfo, getSalaryInfo);
return new Response
{
IsuserDetailsSucceeded = await userDetails,
IsgetWageInfoSucceeded = await getWageInfo,
IsgetSalaryInfoSucceeded = await getSalaryInfo,
};
async Task<T> UpdateWhenComplete<T>(Task<T> task, string taskName)
{
try { return await task; }
finally { progress?.Report($"Completed {taskName}"); }
}
}
If you also need a count, you can either use IProgress<(int, string)> or change how the report progress string is built to include the count.
So here's what I would do in C# 8 and .NET Standard 2.1:
First, I create the method which will produce the async enumerable:
static async IAsyncEnumerable<bool> TasksToPerform() {
Task[] tasks = new Task[3] { userDetails, getWageInfo, getSalaryInfo };
for (i = 0; i < tasks.Length; i++) {
await tasks[i];
yield return true;
}
}
So now you need to await foreach on this task enumerable. Every time you get a return, you know that a task has been finished.
int numberOfFinishedTasks = 0;
await foreach (var b in TasksToPerform()) {
numberOfFinishedTasks++;
//Update UI here to reflect the finished task number
}
No need to over-complicate this. This code will show how many of your tasks had exceptions. Your await task.whenall just triggers them and waits for them to finish. So after that you can do whatever you want with the tasks :)
var task = Task.Delay(300);
var tasks = new List<Task> { task };
var faultedTasks = 0;
tasks.ForEach(t =>
{
t.ContinueWith(t2 =>
{
//do something with a field / property holding ViewModel state
//that your view is listening to
});
});
await Task.WhenAll(tasks);
//use this to respond with a finished count
tasks.ForEach(_ => { if (_.IsFaulted) faultedTasks++; });
Console.WriteLine($"{tasks.Count() - faultedTasks} / {tasks.Count()} completed.");
.WhenAll() will allow you to determine if /any/ of the tasks failed, they you just count the tasks that have failed.
public async Task<Response> GetAllVasInformationAsync()
{
var userDetails = GetUserDetailsAsync();
var getWageInfo = GetUserWageInfoAsync();
var getSalaryInfo = GetSalaryInfoAsync();
await Task.WhenAll(userDetails, getWaitInfo, getSalaryInfo)
.ContinueWith((task) =>
{
if(task.IsFaulted)
{
int failedCount = 0;
if(userDetails.IsFaulted) failedCount++;
if(getWaitInfo.IsFaulted) failedCount++;
if(getSalaryInfo.IsFaulted) failedCount++;
return $"{failedCount} tasks failed";
}
});
var resultToReturn = new Response
{
IsuserDetailsSucceeded = userDetails.Result,
IsgetWageInfoSucceeded = getWageInfo.Result,
IsgetSalaryInfoSucceeded = getSalaryInfo.Result,
};
return resultToReturn;
}

How to call async method from sync in C#?

I am adding a new web API call to existing functionality. I want to make this API call async but looks like it is causing deadlock. I have to make a lot more changes if I want to make entire code channel async which is not possible.
Questions I have are:
Is it possible to call async method from regular method?
What am I missing here? OR What is the correct approach here?
Code:
// Exisitng Method
public Tuple<RestaurantDeliveryProvider, DeliveryHubResult, Task<DeliveryManagerQuoteResponse>> CreateDeliveryRequest(OrderContextDTO orderContextDto)
{
var provider = RestaurantBl.GetDeliveryProviderInformationByRestaurantId(orderContextDto.RestaurantId ?? 0);
var deliveryHubResult = RestaurantBl.GetDeliveryHubResult(orderContextDto.OrderId ?? 0);;
// New Call which always comes back with "Not Yet Computed" result
Task<DeliveryManagerQuoteResponse> deliveryManagerQuoteResponse = _deliveryManager.CreateQuoteRequestAsync(orderContextDto, orderInfo);
return Tuple.Create(provider, deliveryHubResult, deliveryManagerQuoteResponse);
}
Async Methods:
public async Task<DeliveryManagerQuoteResponse> CreateQuoteRequestAsync(OrderContextDTO orderContextDto, OrderInfoDTO orderInfo)
{
DeliveryManagerQuoteResponse deliveryManagerQuoteResponse = null;
try
{
var restaurantInfo = RestaurantApi.GetRestaurant(orderInfo.RestaurantId);
var quoteRequest = new DeliveryManagerQuoteRequest
{
DeliveryProvider = null,
Country = orderContextDto.DeliveryEstimateRequestDto.RequestedDeliveryAddress.Country,
Concept = "BK",
StoreName = "BK-TEST-US-4",
OrderId = orderInfo.OrderId.ToString(),
AllowCash = false,
PaymentType = OrderPaymentType.Prepaid_Credit,
Note = orderInfo.DeliveryInstructions,
};
deliveryManagerQuoteResponse = await Quote(quoteRequest);
}
catch (Exception ex)
{
Log.ErrorFormat("Get Delivery Manager Quote failed: Error: {0}, OrderId: {1}", ex.Message, orderContextDto.OrderId);
}
return deliveryManagerQuoteResponse;
}
public async Task<DeliveryManagerQuoteResponse> Quote(DeliveryManagerQuoteRequest quoteRequest)
{
DeliveryManagerQuoteResponse deliveryManagerQuoteResponse;
var client = HttpClientFactory.GetClient();
var content = HttpClientFactory.JsonContentFactory.CreateJsonContent(quoteRequest);
var response = await client.PostAsync("https://myUrl", content);
if (response.IsSuccessStatusCode)
{
var data = await response.Content.ReadAsStringAsync();
deliveryManagerQuoteResponse = JsonConvert.DeserializeObject<DeliveryManagerQuoteResponse>(data);
}
else
{
throw new Exception((int)response.StatusCode + "-" + response.StatusCode);
}
return deliveryManagerQuoteResponse;
}
I tried following as well but same result:
public async Task<DeliveryManagerQuoteResponse> Quote(DeliveryManagerQuoteRequest quoteRequest)
{
DeliveryManagerQuoteResponse deliveryManagerQuoteResponse;
using (var client = new HttpClient())
{
var content = HttpClientFactory.JsonContentFactory.CreateJsonContent(quoteRequest);
var response = await client.PostAsync("https://myUrl", content);
if (response.IsSuccessStatusCode)
{
var data = await response.Content.ReadAsStringAsync();
deliveryManagerQuoteResponse = JsonConvert.DeserializeObject<DeliveryManagerQuoteResponse>(data);
}
else
{
throw new Exception((int)response.StatusCode + "-" + response.StatusCode);
}
}
return deliveryManagerQuoteResponse;
}
Output (sorry for the blurry output, if you click on it, you will see clear result):
don't
don't
Basically, there is no good or workable way to call an async method from a sync method and wait for the answer. There's "sync over async", but that's an anti-pattern and should be aggressively avoided.
So either:
rewrite the caller to be async
implement a synchronous version of the API

Run parallel validation of Uri using WebRequest

Let's say I have more Uri's. I need to validate, if they are reachable.
public RelayCommand TestConnectionCommand => new RelayCommand(async () =>
{
var res1 = await ValidateUriAsync(uri);
var res2 = await ValidateUriAsync(uri);
});
private async Task<bool> ValidateUriAsync(Uri uri)
{
try
{
var request = WebRequest.CreateHttp(uri);
var result = await request.GetResponseAsync();
return true;
}
catch (Exception e)
{
return false;
}
}
When the program comes to first await it takes some time to validate the uri, after I get the result, I can show the result on the View. Then program goes next and I validate second uri. I'd like to do that parallel, without awaiting. I was thinking about using Begin/EndGetResponse. I need to show the result for each validation on the View. Validation succeeded/failed.
Many thanks for advice.
When using await you stop the execution until the task returns, instead wait for all task to finish:
var task1 = ValidateUriAsync(uri);
var task2 = ValidateUriAsync(uri);
await Task.WhenAll(task1, task2);
or to wait until the first fault:
var tasks = new List<Task>
{
ValidateUriAsync(), ValidateUriAsync(uri)
};
while (tasks.Any())
{
var t = await Task.WhenAny(tasks);
if (t.IsFaulted)
{
//Faulty
break;
}
tasks.Remove(t);
}

Ping ASync Problems

I have to have a breakpoint on the indicated line below for the following code to work. Otherwise, the program just pauses indefinitely.
async Task<List<PingReply>> PingAsync()
{
var pingTargetHosts = GetIPs();
var pingTasks = pingTargetHosts.Select(host => new Ping().SendPingAsync(host, 2000)).ToList();
var pingResults = await Task.WhenAll(pingTasks); //THIS LINE NEEDS A BREAKPOINT TO WORK
return pingResults.ToList();
}
The code is called like this
List<PingReply> GetReplies()
{
var PingIPs = PingAsync();
MessageBox.Show("Loading:...");
List<PingReply> Results = PingIPs.Result;
return Results;
}
Could anyone tell me how I need to amend my code in order to remove the breakpoint but still have a functional piece of code.
EDIT:
Not tested, but 99% sure this will work.
async Task<List<PingReply>> PingAsync()
{
var pingTargetHosts = GetIPs();
var pingTasks = pingTargetHosts.Select(async host => await new Ping().SendPingAsync(host, 2000)).ToList();
var pingResults = await Task.WhenAll(pingTasks);
return pingResults.ToList();
}
async Task<List<PingReply>> GetReplies()
{
var PingIPs = PingAsync();
MessageBox.Show("Loading:...");
return await PingIPs;
}
async Task BuildDictionary()
{
List<PingReply> Replies = await GetReplies();
//Use this list via foreach
}
async private void button1_Click(object sender, EventArgs e)
{
EthernetCheck checker = new EthernetCheck();
checker.Check();
bool IsEthernetIn = checker.PluggedIn;
if (IsEthernetIn)
{
await BuildDictionary();
//Do Stuff
}
}
Your code is deadlocking because you're blocking on asynchronous code. To fix it, use async all the way:
async Task<List<PingReply>> GetRepliesAsync()
{
var PingIPs = PingAsync();
MessageBox.Show("Loading:...");
return await PingIPs;
}
Usage:
var replies = await GetRepliesAsync();
When use async/await you should remember that .net framework will control the flow of program execution so, I recommend u to make all calls asynchronous to avoid this kind of problem.
async Task<List<PingReply>> PingAsync()
{
var pingTargetHosts = await GetIPs();
var pingTasks = pingTargetHosts.Select(host => await new Ping().SendPingAsync(host, 2000)).ToList();
var pingResults = await Task.WhenAll(pingTasks);
return pingResults.ToList();
}

how can I make the httprequest stop if internet is slow and data is not gather within 10 seconds?

I get crashes when I work with my app with a slow internet when I keep pressing different buttons that gathers data via httprequests. how could i make a function so that if i do not recieve the data within for example 10 seconds, the attempt to gather the data from the httprequest should be cancelled.
This is my code:
static public async Task<JObject> getCategories ()
{
var httpClientRequest = new HttpClient ();
try {
var result = await httpClientRequest.GetAsync ("http://localhost");
var resultString = await result.Content.ReadAsStringAsync ();
var jsonResult = JObject.Parse (resultString);
System.Diagnostics.Debug.WriteLine (resultString);
return jsonResult;
}
catch {
return null;
}
}
async void createCategory (object sender, EventArgs args)
{
var getCategory = await parseAPI.getCategories ();
if (getCategory != null) {
foreach (var currentItem in getItems["results"]) {
id = currentItem ["ID"].ToString ();
objectid = currentItem ["objectid"].ToString ();
//connected
}
}
} else {
//no connection
}
}
You can use CancellationTokenSource which has a method provided in it which is CancelAfter():
var token = new CancellationTokenSource();
token.CancelAfter(10000); // after 10 secs
var result = await httpClientRequest.GetAsync ("http://localhost",token.Token);
var resultString = await result.Content.ReadAsStringAsync ();
Here is a article about using Cancellation with GetAsync which you may be interested to read.
You could also use the timeout on the httpclient.
var httpClientRequest = new HttpClient();
httpClientRequest.Timeout = TimeSpan.FromSeconds(10);
var result = await httpClientRequest.GetAsync("http://localhost");

Categories

Resources