How do I run an Async Task through IsActiveChanged - c#

I have a Task which I'm trying to run through OnIsActiveChanged So that it runs when I open as a view. I'm sure I'm doing something obvious but I'm relatively new to Async code.
I'm getting an error with OnIsActiveChanged telling me
Task SettingsView.OnIsActiveChanged(object?, EventArgs) has the wrong return type.
Here is the method I'm trying to use:
public async Task OnIsActiveChanged(object? sender, EventArgs e)
{
if (IsActive)
{
var streaming = _client.BiStreaming();
var requestStream = streaming.RequestStream;
var responseStream = streaming.ResponseStream;
//Print
Debug.WriteLine($"Duration: {Duration.TotalSeconds} seconds");
Debug.WriteLine("");
await BidirectionalStreamingExample(_client);
}
}
And here is where I am trying to call the method:
public SettingsView(SequenceEditorService.SequenceEditorServiceClient client)
{
InitializeComponent();
_client = client;
IsActiveChanged += OnIsActiveChanged;
}
IsActiveChanged is an event handler that checks if there is a change in the state if the view. It was fine with the normal method i had but once i tried to async it broke.

There are two possible solutions to your question, as we would need to know what kind of return type the IsActiveChanged-event has. Typically events should have no return type (void) according to BCL guidelines, the possibility still exists though.
Possible solutions:
You have to return a Task (very unlikely as stated above)
Set the OnIsActiveChanged's return type to void (likely the solution, since IsActiveChanged is probably a event coded by Microsoft and therefore complies with the BCL guidelines)
When applying the second solution to your code, the OnIsActiveChanged's signature should look something like this:
public async void OnIsActiveChanged(object sender, EventArgs e)

Related

Async & Await in C#: Do I have to change the entire Call Chain Up to a Void Method?

I'm completely new to this whole Async / Await topic on C#. Despite countless questions being answered and tutorials being linked to relentlessly, I still seem to be unable to wrap my head around how to use async/await. All I want to achieve is the wpf application to return to rendering animations on the UI while it waits for an expensive method call to finish - in my following example, that'd be GetLinesAsync(), or more specifically, expensive_function() within:
void Button_Click(object sender, RoutedEventArgs e) {
MessageBox.Show(GetAMessage());
}
string GetAMessage() {
string ret = "";
foreach(string s in GetLinesAsync().Result)
ret += $"{s}\n";
return ret;
}
async Task<List<string>> GetLinesAsync() {
List<string> ret = new List<string>();
string[] ExpensiveResult = null;
if (await Task.Run(() => expensive_function(ref ExpensiveResult)) == expensive_stat.SUCCESS && ExpensiveResult != null)
{
foreach(string s in ExpensiveResult)
ret.Add(s);
}
Written like that, the application freezes completely, even though expensive_function() doesn't take THAT long to perform. Besides that I'm not sure why this happens, here's what I've understood from reading tutorials and explanations on await/async, especially the part saying you can only await methods that return either void or Task:
The line foreach(string s in GetLinesAsync().Result) should actually say foreach(string s in await GetLinesAsync().Result) - but then I'd have to mark GetMessages() async, and then I'd have to replace MessageBox.Show(GetAMessage()) with MessageBox.Show(await GetMessages()) and mark Button_Click() async.
In other words: the awaitability would creep up the call stack until it reaches a void method. Okay, but that can't be it, can it? What if, in some other scenario, there is no senseful void method to creep up to, even?
The reason why you code is blocking, is because you block it here.
foreach(string s in GetLinesAsync().Result)
You are saying UI thread wait until my expensive task has finished
Just let async flow free.
In Windows Forms and WPF, async/await has the handy property of coming back to the UI thread when the asynchronous operation you were awaiting has completed
Support for async on a void method was added specifically to support event handlers. however make sure you handle your exceptions as void will be unobserved
So you could just do this.
Note : this is an extremely simplified and sanitized example based on your code
public async void Button_Click(object sender, RoutedEventArgs e)
{
try
{
MessageBox.Show(await GetAMessage());
}
catch (Exception exception)
{
// log the nelly out of it
// or display message
}
}
public async Task<string> GetAMessage()
{
var list = await GetLinesAsync();
return string.Join("\n", list);
}
public List<string> ExpensiveFunction()
{
// ...
return new List<string>();
}
public async Task<List<string>> GetLinesAsync()
{
var result = Task.Run(() => ExpensiveFunction());
return await result;
}

Error with HttpClient

I have been working on a simple c# application that uses rest and Azure SQL Database. My app has two buttons one for saving new names and the second for showing them. the later doesn't react when i press it and i get the following error related to these parts GetRequest(); PostRequest(jsonPost);
private void Btn_Click(object sender, EventArgs e)
{
GetRequest();
}
private void Btn_Click(object sender, EventArgs e)
{
User user = new User(0, firstnameText.Text);
JavaScriptSerializer java = new JavaScriptSerializer();
String jsonPost = java.Serialize(user);
PostRequest(jsonPost);
}
}
Severity Code Description Project File Line Suppression State Detail Description
Warning CS4014 Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call. PersonRegister C:\Users\User\Downloads\RestSolution\RestSolution\PersonRegister\Form1.cs 86 Active The current method calls an async method that returns a Task or a Task and doesn't apply the await operator to the result. The call to the async method starts an asynchronous task. However, because no await operator is applied, the program continues without waiting for the task to complete. In most cases, that behavior isn't what you expect. Usually other aspects of the calling method depend on the results of the call or, minimally, the called method is expected to complete before you return from the method that contains the call.
An equally important issue is what happens to exceptions that are raised in the called async method. An exception that's raised in a method that returns a Task or Task is stored in the returned task. If you don't await the task or explicitly check for exceptions, the exception is lost. If you await the task, its exception is rethrown.
As a best practice, you should always await the call.
You should consider suppressing the warning only if you're sure that you don't want to wait for the asynchronous call to complete and that the called method won't raise any exceptions. In that case, you can suppress the warning by assigning the task result of the call to a variable.
Although the advice to always return Task from an async method, this is not possible for UI event handlers. As such you should rewrite your handler like this:
private async void Button1_Click(object sender, EventArgs e)
{
await GetRequest();
}
private async void Button2_Click(object sender, EventArgs e)
{
Person p = new Person(0, nameText.Text);
JavaScriptSerializer js = new JavaScriptSerializer();
String jsonPost = js.Serialize(p);
await PostRequest(jsonPost);
}
See here for more info.
Also, as an aside, you should declare one static instance of HttpClient for the entire application (as per MSDN docs).

Calling a WebAPI asynchronously from a Xamarin.Android event handler deadlocks

I've been struggling with this issue for a while and it's now time for me to ask my question here. So, here's the situation:
I've got a WebAPI, a Xamarin.Android application and an IdentityServer4 implementation. The Xamarin.Android app needs to call the WebAPI to get a list of users based on a search. This is done via an async call in a TextChanged event handler. What happens is that when the call to the WebAPI is awaited, it never returns and at some point the task gets cancelled. I've tried using ConfigureAwait(false), I've made the event handler method async void, I've read through several other questions on here about similar issues - none of the options work for me. But here's some code for you to get an idea of what I've doing:
The TextChanged handler:
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
SetContentView(Resource.Layout.SearchPeopleLayout);
this.searchUsersElement = FindViewById<EditText>(Resource.Id.searchUsersField);
this.usersListElement = FindViewById<ListView>(Resource.Id.usersSearchResult);
using (var scope = App.Container.BeginLifetimeScope())
{
this.apiClient = App.Container.Resolve<IUsersApiClient>();
}
this.searchUsersElement.TextChanged += SearchUsersElement_TextChanged;
}
public async void SearchUsersElement_TextChanged(object sender, Android.Text.TextChangedEventArgs e)
{
var length = e.Start + e.AfterCount;
if (length <= MinUsernameLength)
{
return;
}
var searchName = string.Join(string.Empty, e.Text);
var users = await this.apiClient.GetUsers(searchName, Persistor.ApiClient);
RunOnUiThread(() =>
{
var usersAdapter = new UsersAdapter(this, users.ToList());
this.usersListElement.Adapter = usersAdapter;
});
}
Here's the UsersApiClient implementation:
public async Task<IEnumerable<UserModel>> GetUsers(string username, HttpClient client)
{
try
{
var content = await client.GetStringAsync($"{apiUrl}/users?name={username}").ConfigureAwait(false);
var result = JsonConvert.DeserializeObject<IEnumerable<UserModel>>(content);
return result;
}
catch (Exception ex)
{
throw ex;
}
}
The deadlock happens on the GetStringAsync() call. I've tested the call that it makes via Postman and it is giving me the correct response. Only things I haven't tried yet are implementing the ITextWatcher interface instead of adding an event handler, and changing the code so I don't use a separate method but rather an anonymous delegate - I read in some places that that could prove to be an issue.
I'm really hoping some of you can help me in resolving this issue and making me understand how to do such asynchronous calls correctly.
Best regards.
Okay, so I'm not really sure what fixed the issue, but here's what I did - there was another StackOverflow question with a similar problem where the problem was solved with uninstalling the app from the emulator manually before redeploying it. I also removed the ConfigureAwait(false) call. And suddenly, everything's working. Not sure how it happened, but here it is - uninstalling the app manually and calling the API without the ConfigureAwait(false) did it.

Write async method correctly

I've used async methods for a long time, but I don't know the right way to write my own async method. As I get it, first root(I mean most deep) async method begins from sync method wrapped in a Task? Am I right? What is the right pattern to write such methods? Right now I do it like this:
private async Task DoSomethingAsync()
{
var tsk = new Task(OnDo);
tsk.Start();
await tsk.ConfigureAwait(false);
}
private void OnDo()
{
// Do stuff ...
}
The actual answer is too long to post here.
Short answer
No, you should not call async methods from sync methods. The method tree should be asynchronous bottom to top.
For example: Using ASP.NET MVC async methods would be called from async Action methods, see: Using Asynchronous Methods in ASP.NET MVC 4.
public async Task<ActionResult> GizmosAsync()
{
ViewBag.SyncOrAsync = "Asynchronous";
var gizmoService = new GizmoService();
return View("Gizmos", await gizmoService.GetGizmosAsync());
}
Using WPF applications, you should have async void event methods, that call your asynchronous methods.
private async void button1_Click(object sender, RoutedEventArgs e)
{
textBlock1.Text = "Click Started";
await DoWork();
textBlock2.Text = "Click Finished";
}
Either way, the question is too broad.
What is your "to be implemented" method?
why do you want it to be async?
What environment are you using?
In general async is used for I/O operations(databases, network, file read/write) or parallelism.

SOAP Client doesn't finish its job or maybe doesn't return anything?

I'm using SOAP Client in Windows RT, I'm using Windows 8.1 OS and VS 2013. Anyway, what I want to do is just a simple task which returns a some string values.
When I try to do await - async task it doesn't return anything or maybe it just simply loses itself trying to find the client. I couldn't find a problem.
I added service reference , when I look at it in Object Browser there doesn't seem a problem. I'm stuck any idea why it's happening?
Here's my code:
using Namespace.InfoGetter;
private void btn_Click(object sender, RoutedEventArgs e)
{
Info info = GetInfo("en-US");
txtInfo.Text = info.Result.Value;
}
async Task<Info> GetInfo(string culture)
{
InfoSoapClient client = new InfoSoapClient();
Task<InfoResponse> info = client.GetInfoAsync(culture); <<<<<<<<<<<
Info result = await info;
return result;
}
When debug comes to the line (which I put <<<) client.GetInfoAsync(culture) doesn't return anything and the method ends there too. What to do?
As your code example isn't accurate, i assume what is happening is that you have a deadlock, since you block on info.Result while your await in GetInfo is trying to marshal work back to your UI thread.
We're going to add the async keyword to your buttons click event handler and await on GetInfoAsync
Try this:
private async void btn_Click(object sender, RoutedEventArgs e)
{
Info info = await GetInfoAsync("en-US");
textInfo.Text = info.Value
}
private Task<Info> GetInfoAsync(string culture)
{
InfoSoapClient client = new InfoSoapClient();
return client.GetInfoAsync(culture);
}
Note i added the Async suffix to your GetInfo method to follow the TAP convention and removed the async keyword from GetInfoAsync as you don't really need to generate the extra state machine as all you do is return is return the result and not do additional work with it.

Categories