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.
Related
I'm currently trying to write a simple C#-WPF-Application that functions as a simple universal 'launcher'. For different applications we program.
It's purpose is to check the current software version of the 'real' software and if a new one is available it starts to copy the installer from a network share and runs the installer afterwards.
Then it starts the 'real' application and thats it.
The user Interface mainly consists of a startup window which shows the user the currently executed action (version check, copy, installation, startup, ...).
Now I create my view and my viewModel in the overridden StartUp method in App.cs
public override OnStartup(string[] args)
{
var viewModel = new StartViewModel();
var view = new StartView();
view.DataContext = viewModel;
view.Show();
// HERE the logic for the Launch starts
Task.Run(() => Launch.Run(args));
}
The problem is that if I don't go async here the Main Thread is blocked and I cannot update the UI. Therefore I got it working by using the Task.Run(...). This solves my problem of blocking the UI thread, but I have some problems/questions with this:
I cannot await the task, because that would block the UI again. Where to await it?
Is my concept of starting this workflow here ok in the first place?
Some update to clarify: After I show the UI to the user my logic starts to do heavy IO stuff. The possible calls I came up with are the following 3 variants:
view.Show();
// v1: completely blocks the UI, exceptions are caught
DoHeavyIOWork();
// v2: doesn't block the UI, but exceptions aren't caught
Task.Run(() => DoHeavyIOWork());
// v3: doesn't block the UI, exceptions are caught
await Task.Run(() => DoHeavyIOWork());
Currently I'm not at my work PC so i apologies for not giving you the original code. This is an on the fly created version.
I guess v1 and v2 are bad because of exceptions and the UI blocking.
I thought v3 didn't work when I tried it in my office. Now it seems to work in my local example. But I'm really not sure about v3. Because I'm using async void StartUp(...) there. Is it okay here?
I cannot await the task, because that would block the UI again. Where to await it?
await doesn't block the UI. Using await here is fine.
Is my concept of starting this workflow here ok in the first place?
I usually recommend that some UI is shown immediately when doing any asynchronous operation. Then when the async operation is complete, you can update/replace the UI.
Thanks for all the replys.
After reading all your comments and combining some of your answers I came up with the following example. It is working under all circumstances I tested.
Hopefully there is not to much wrong in your opinion.
Code behind from App.xaml.cs
public partial class App : Application
{
readonly StartViewModel viewModel = new StartViewModel();
protected override async void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
var view = new StartWindow
{
DataContext = viewModel
};
view.Show(); // alternative: Run(view);
// alternative: instead of calling it here it is possible
// to move these calls as callback into the Loaded of the view.
await Task.Run(() => DoHeavyIOWork());
}
private string GenerateContent()
{
var content = new StringBuilder(1024 * 1024 * 100); // Just an example.
for (var i = 0; i < 1024 * 1024 * 2; i++)
content.Append("01234567890123456789012345678901234567890123456789");
return content.ToString();
}
private void DoHeavyIOWork()
{
var file = Path.GetTempFileName();
for (var i = 0; i < 20; i++)
{
File.WriteAllText(file, GenerateContent());
File.Delete(file);
Dispatcher.Invoke(() => viewModel.Info = $"Executed {i} times.");
}
}
}
Code in StartViewModel.cs
class StartViewModel : INotifyPropertyChanged
{
private string info;
public event PropertyChangedEventHandler PropertyChanged;
public string Info
{
get => info;
set
{
info = value;
OnPropertyChanged();
}
}
private void OnPropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
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)
Maybe I did not search correctly here in the forum because I did not find a similar problem.
Well, my problem is when I try to execute an async method inside a thread.
When I run the method (Register) without the thread it works perfectly!
Below is an example of the scenario.
private SyncProcess _sync = new SyncProcess();
private static HttpClient _httpClient = new HttpClient();
private Thread _thread;
public class SyncProcess : ISyncProcess
{
public event CompleteHandler OnComplete = delegate { };
// another properties ...
public void Run()
{
// import rules
// ...
OnComplete();
}
}
public void TestImport()
{
Register(idsync, "start"); // here register works fine
_sync.OnComplete += ImportComplete;
_thread = new Thread(() =>
{
try
{
_sync.Run();
}
catch (Exception ex)
{
// not fall here
}
});
//
_thread.Start();
}
private void ImportComplete()
{
// other end-of-import rules
// ...
Register(idsync, "complete"); // here register not works
}
public async Task<string> Register(int idsync, string type)
{
string url = "myurl";
var stringContent = new FormUrlEncodedContent(new[] { new KeyValuePair<string, string>("json", "myjson") });
var response = await _httpClient.PostAsync(url + type, stringContent);
if (response.IsSuccessStatusCode)
{
// do something
}
return "";
}
The problem occurs when I call the method (Register) inside the thread, another thing is that is that it does not generate error does not fall into the try, the debugging simply terminates. I've tried adding try code everywhere but never crashed in catch.
Debug always aborts on the following line:
var response = await _httpClient.PostAsync(url + type, stringContent);
What should I do in this case?
Updated the code returning string in the Register method, but the same error remains.
Thanks any suggestions!
Your problem is due to the use of async void, which should be avoided. One of its problems is that you can't catch exceptions using try/catch.
Event handlers in C# are a "fire-and-forget" kind of language feature. In particular, asynchronous event handlers must use async void, and this means the event-publishing code cannot see those exceptions. If you want to allow async event handlers and handle exceptions (or other results) from them, you can use a "deferral" solution or make your event handler delegate return Task instead.
Async void will not allow you to catch any exceptions and will terminate your application when one is thrown. Exceptions are only observed and handled as normal exceptions when using task instead of void.
You can read all about it here link
I can not answer your first question on why it works without the thread without more information. I can guarantee you thought that it has nothing to do with multi threading as far as I know since the main thread is also just a thread like any other.
I have the following:
public async Task<bool> SearchForUpdatesAsync()
{
return await TaskEx.Run(() =>
{
if (!ConnectionChecker.IsConnectionAvailable())
return false;
// Check for SSL and ignore it
ServicePointManager.ServerCertificateValidationCallback += delegate { return (true); };
var configurations = UpdateConfiguration.Download(UpdateConfigurationFileUri, Proxy);
var result = new UpdateResult(configurations, CurrentVersion,
IncludeAlpha, IncludeBeta);
if (!result.UpdatesFound)
return false;
_updateConfigurations = result.NewestConfigurations;
double updatePackageSize = 0;
foreach (var updateConfiguration in _updateConfigurations)
{
var newPackageSize = GetUpdatePackageSize(updateConfiguration.UpdatePackageUri);
if (newPackageSize == null)
throw new SizeCalculationException(_lp.PackageSizeCalculationExceptionText);
updatePackageSize += newPackageSize.Value;
_packageOperations.Add(new UpdateVersion(updateConfiguration.LiteralVersion),
updateConfiguration.Operations);
}
TotalSize = updatePackageSize;
return true;
});
}
As you can see I'm using Microsoft.Bcl.
Now in my other class I wrote this code in a normal void:
TaskEx.Run(async delegate
{
// ...
_updateAvailable = await _updateManager.SearchForUpdatesAsync();
MessageBox.Show("Test");
});
The problem I have is that it executes _updateAvailable = await _updateManager.SearchForUpdatesAsync(); and then it doesn't continue the thread, it just stops as if there is nothing after that call. Visual Studio also tells me this after a while: Thread ... exited with code 259, so something seems to be still alive.
I debugged through it to search for any exceptions that could maybe be swallowed, but nothing, everything works fine and it executes the return-statement.
And that is what I don't understand, I never see the MessageBox and/or no code beyond this line's being executed.
After I talked to some friends, they confirmed that this shouldn't be. Did I make a horrible mistake when implementing async-await?
Thanks in advance, that's actually all I can say about that, I got no more information, I appreciate any tips and help as far as it's possible.
The main issue that you're having is that you're unnecessarily wrapping your method in TaskEx.Run() and you are probably experiencing deadlock somewhere.
Your method signature is currently:
public async Task<bool> SearchForUpdatesAsync()
This means the following:
async --> Doesn't actually do anything, but provides a "heads up" that await might be called within this method and that this method can be used as a runnable Task.
Task --> This method returns a runnable task that can be run asynchronously on the threadpool
<bool> --> This method actually returns bool when awaited.
The await TaskEx.Run() is unnecessarily since this says run this method and then don't return until after a value is available. This is most likely causing a synchronization problem. Removing this construct will make your method work properly, however you'll notice that now you have no reason to even include the async operator or the Task<T> portion since the method is actually synchronous anyway. Usually you're only going to use async identifier on the method signature if you have methods that you are going to call await on them.
Instead you have two options.
Whenever you want to call SearchForUpdates() you can wrap this in a Task<bool>.Run() to run it asynchronously (or the Bcl equivalent)
Since you are using WinForms you might be better off using a BackgroundWorker and just calling this method within it.
Regarding using the async-await pattern I think that this is a great article to use to make sure you're following best practices: https://msdn.microsoft.com/en-us/magazine/jj991977.aspx
The best practice is to have async all the way through your layers, and then call await or less desirably .Wait() / .Result at the final use site.
Also, try to keep your UI calls separate from the backend work, since you can run into synchronicity/thread-context issue.
public class WinFormsCode
{
private async Task WinsFormCodeBehindMethodAsync()
{
var updatesAvailable = await _updateManager.SearchForUpdatesAsync();
MessageBox.Show("Updates Available: " + updatesAvailable.ToString());
}
private void WinsFormCodeBehindMethodSync()
{
var updatesAvailable = _updateManager.SearchForUpdatesAsync().Result;
MessageBox.Show("Updates Available: " + updatesAvailable.ToString());
}
}
public class UpdateManager
{
public async Task<bool> SearchForUpdatesAsync()
{
return true;
}
}
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.