Async and populating data for WPF application - c#

I have a WPF application that initializes the state of the UI via methods in the constructor. However, it's never returning from the Wait(); in the constructor.
Here's what I am currently doing via a fairly-contrived sample:
public class SomeViewModel
{
public ICommand AddDataCommand { get { return RelayCommand(AddDataExecute); } }
public ObservableCollection<int> UIData { /* Property with INotifyPropertyChanged */}
public SomeViewModel()
{
//Load synch. here
LoadData().Wait();
}
public async Task LoadData()
{
UIData = await Task.Run(() => SomeService.SelectAllUIData());
}
public async void AddDataExecute()
{
//Add new data item to database on separate thread to keep UI responsive
await Task.Run(() => SomeService.AddNewData(0));
//Reload data from database and update UI, has to happen after AddNewData completes
await LoadData();
}
}
I believe it's hanging because I am never actually returning a Task. However, I don't know of a different way to assign UIData asynchronously that works both in the constructor and the Commands that call it.

You're seeing the classic deadlock situation that I describe on my blog.
To solve it, use async all the way, as I describe in my MSDN article on async best practices.
This can be difficult in some scenarios. I have a blog post describing a few approaches for async constructors. However, since you're talking about a ViewModel and data for the UI, you'll probably find my blog post on async properties more helpful - in particular, the section on data binding.

Don't construct the object through a constructor, if you require construction to be asynchronous. Use a static factory method:
public class SomeViewModel
{
private SomeViewModel()
{ }
public static async Task<SomeViewModel> Create()
{
SomeViewModel model = new SomeViewModel();
await model.LoadData();
return model;
}
public async Task LoadData()
{
UIData = await Task.Run(() => SomeService.SelectAllUIData());
}
//...
}
As for why your code isn't working, you're getting the standard await deadlock in which you're blocking on an asynchronous operation, that blocking is preventing continuations from being called on the UI thread, and with two different operations each waiting on the other to continue, you get a deadlock. This is why it's important to "async all the way up" rather than synchronously blocking on an asynchronous operation.

Related

c# Task.WhenAll to block while awaiting completion

I have a simple Winforms application. I would like to background TCP connections/print requests and check the output of all tasks at a set point in my code.
I would expect ReportOnTasks to block until WaitAll is complete. Please could someone explain why this is not the case? I'm also worried I haven't structured this correctly.
Edit, to clarify my intentions:
I would like to send the print jobs as soon as I receive the data. Then continue with some other DB operations. Once all the print operations are complete, I would like to update the UI to state the result.
I've attempted to simplify the code as much as I can. Maybe too much. HomeController just inits some stuff. There are buttons on the form and file watchers that trigger the main functionality.
public class HomeController
{
public HomeController(){
MessageBox.Show("1");
oPrintController.PrintAsync("192.168.2.213", Encoding.ASCII.GetBytes("string to print"));
MessageBox.Show("2");
// Block here untill tasks are complete
ReportOnTasks();
MessageBox.Show("Report on tasks complete");
}
public async void ReportOnTasks()
{
await Task.WhenAll(oPrintController.Tasks);
foreach(Task<PrintController.PrintResult> PR in oPrintController.Tasks)
{
// do something with the result of task
}
}
}
and the PrintController
public class PrintController
{
public List<Task<PrintResult>> Tasks = new List<Task<PrintResult>>();
public async void PrintAsync(string sIP, List<byte[]> lsToPrint, int iPort = 9100)
{
var s = await Task.Run(() => PrintAsync1(sIP, lsToPrint));
}
public async System.Threading.Tasks.Task<PrintResult> PrintAsync1(string sIP, List<byte[]> lsToPrint, int iPort = 9100)
{
using (TcpClient tc = new TcpClient())
{
await tc.ConnectAsync(sIP, iPort);
using (var ns = tc.GetStream())
{
foreach (byte[] btLabel in lsToPrint)
{
await ns.WriteAsync(btLabel, 0, btLabel.Length);
}
}
}
Thread.Sleep(10000);
return new PrintResult();
}
}
public class PrintResult
{
bool bSuccess = false;
}
You are not awaiting the call to ReportOnTasks()
Moreover, you can't await within a ctor, because they can't be async.
Depending on how your HomeController is used, you could use a static async method which returns an instance of HomeController, created by a private ctor instead:
Something like this:
public class HomeController
{
//notice private - you can't new up a HomeController - you have to use `CreateInstance`
private HomeController(){
MessageBox.Show("1");
//not clear from your code where oPrintController comes from??
oPrintController.PrintAsync("192.168.2.213", Encoding.ASCII.GetBytes("string to print"));
MessageBox.Show("2");
MessageBox.Show("Report on tasks complete");
}
public static async Task<HomeController> CreateInstance() {
var homeController = new HomeController();
await homeController.ReportOnTasks();
return homeController;
}
//don't use async void! Change to Task
public async Task ReportOnTasks()
{
//not clear from your code where oPrintController comes from??
await Task.WhenAll(oPrintController.Tasks);
foreach(Task<PrintController.PrintResult> PR in oPrintController.Tasks)
{
// do something with the result of task
}
}
}
Usage:
var homeControllerInstance = await HomeController.CreateInstance();
It's generally not recommended to perform heavy operations in class constructors, but I suppose you won't change that part, so in order to wait for ReportOnTasks to finish, you need to make it synchronous.
Take into account, that constructor itself doesn't support async/await, it's not possible to mark it async.
Having said that, you won't have real performance enhancement marking void ReportOnTasks as async. In addition, it is not recommended to mark void methods as async due to issues with exceptions handling, which is usually not possible.
So, you can either postpone ReportOnTasks like Alex showed you, or you can synchronously wait until all tasks are finished (which is possible inside ctor).
public void ReportOnTasks()
{
Task.WhenAll(oPrintController.Tasks).GetAwaiter().GetResult(); //synchronously wait
foreach(Task<PrintController.PrintResult> PR in oPrintController.Tasks)
{
// do something with the result of task
}
}
However, I wouldn't suggest this approach, because instance creation will take a while and most importantly block UI thread - and that's usually signal something is really fishy

await Task.Delay(2000) is not completed In Winform [duplicate]

I have the following four tests and the last one hangs when I run it. Why does this happen:
[Test]
public void CheckOnceResultTest()
{
Assert.IsTrue(CheckStatus().Result);
}
[Test]
public async void CheckOnceAwaitTest()
{
Assert.IsTrue(await CheckStatus());
}
[Test]
public async void CheckStatusTwiceAwaitTest()
{
Assert.IsTrue(await CheckStatus());
Assert.IsTrue(await CheckStatus());
}
[Test]
public async void CheckStatusTwiceResultTest()
{
Assert.IsTrue(CheckStatus().Result); // This hangs
Assert.IsTrue(await CheckStatus());
}
private async Task<bool> CheckStatus()
{
var restClient = new RestClient(#"https://api.test.nordnet.se/next/1");
Task<IRestResponse<DummyServiceStatus>> restResponse = restClient.ExecuteTaskAsync<DummyServiceStatus>(new RestRequest(Method.GET));
IRestResponse<DummyServiceStatus> response = await restResponse;
return response.Data.SystemRunning;
}
I use this extension method for restsharp RestClient:
public static class RestClientExt
{
public static Task<IRestResponse<T>> ExecuteTaskAsync<T>(this RestClient client, IRestRequest request) where T : new()
{
var tcs = new TaskCompletionSource<IRestResponse<T>>();
RestRequestAsyncHandle asyncHandle = client.ExecuteAsync<T>(request, tcs.SetResult);
return tcs.Task;
}
}
public class DummyServiceStatus
{
public string Message { get; set; }
public bool ValidVersion { get; set; }
public bool SystemRunning { get; set; }
public bool SkipPhrase { get; set; }
public long Timestamp { get; set; }
}
Why does the last test hang?
Acquiring a value via an async method:
var result = Task.Run(() => asyncGetValue()).Result;
Syncronously calling an async method
Task.Run( () => asyncMethod()).Wait();
No deadlock issues will occur due to the use of Task.Run.
You're running into the standard deadlock situation that I describe on my blog and in an MSDN article: the async method is attempting to schedule its continuation onto a thread that is being blocked by the call to Result.
In this case, your SynchronizationContext is the one used by NUnit to execute async void test methods. I would try using async Task test methods instead.
You can avoid deadlock adding ConfigureAwait(false) to this line:
IRestResponse<DummyServiceStatus> response = await restResponse;
=>
IRestResponse<DummyServiceStatus> response = await restResponse.ConfigureAwait(false);
I've described this pitfall in my blog post Pitfalls of async/await
You are blocking the UI by using Task.Result property.
In MSDN Documentation they have clearly mentioned that,
"The Result property is a blocking property. If you try to access it
before its task is finished, the thread that's currently active is
blocked until the task completes and the value is available. In most
cases, you should access the value by using Await or await instead of
accessing the property directly."
The best solution for this scenario would be to remove both await & async from methods & use only Task where you're returning result. It won't mess your execution sequence.
An addition to the answer given by #HermanSchoenfeld. Unfortunately the quote below is not true:
No deadlock issues will occur due to the use of Task.Run.
public String GetSqlConnString(RubrikkUser user, RubrikkDb db)
{
// deadlock if called from threadpool,
// works fine on UI thread, works fine from console main
return Task.Run(() =>
GetSqlConnStringAsync(user, db)).Result;
}
The execution is wrapped inside a Task.Run, this will schedule the task on the threadpool the block the calling thread. This is okay, as long as the calling thread is not a threadpool thread. If the calling thread is from the threadpool then the following disaster happens: A new task is queued to the end of the queue, and the threadpool thread which would eventually execute the Task is blocked until the Task is executed.
In library code there is no easy solution as you cannot assume under what context your code is called. The best solution is to only call async code from async code, blocking sync APIs from sync methods, don’t mix them.
Source:
https://medium.com/rubrikkgroup/understanding-async-avoiding-deadlocks-e41f8f2c6f5d
If you don't get any callbacks or the control hangs up, after calling the service/API async function, you have to configure Context to return a result on the same called context.
Use TestAsync().ConfigureAwait(continueOnCapturedContext: false);
You will be facing this issue only in web applications, but not in static void main.

Deadlock using FileOpenPicler when unit testing UWP [duplicate]

I have the following four tests and the last one hangs when I run it. Why does this happen:
[Test]
public void CheckOnceResultTest()
{
Assert.IsTrue(CheckStatus().Result);
}
[Test]
public async void CheckOnceAwaitTest()
{
Assert.IsTrue(await CheckStatus());
}
[Test]
public async void CheckStatusTwiceAwaitTest()
{
Assert.IsTrue(await CheckStatus());
Assert.IsTrue(await CheckStatus());
}
[Test]
public async void CheckStatusTwiceResultTest()
{
Assert.IsTrue(CheckStatus().Result); // This hangs
Assert.IsTrue(await CheckStatus());
}
private async Task<bool> CheckStatus()
{
var restClient = new RestClient(#"https://api.test.nordnet.se/next/1");
Task<IRestResponse<DummyServiceStatus>> restResponse = restClient.ExecuteTaskAsync<DummyServiceStatus>(new RestRequest(Method.GET));
IRestResponse<DummyServiceStatus> response = await restResponse;
return response.Data.SystemRunning;
}
I use this extension method for restsharp RestClient:
public static class RestClientExt
{
public static Task<IRestResponse<T>> ExecuteTaskAsync<T>(this RestClient client, IRestRequest request) where T : new()
{
var tcs = new TaskCompletionSource<IRestResponse<T>>();
RestRequestAsyncHandle asyncHandle = client.ExecuteAsync<T>(request, tcs.SetResult);
return tcs.Task;
}
}
public class DummyServiceStatus
{
public string Message { get; set; }
public bool ValidVersion { get; set; }
public bool SystemRunning { get; set; }
public bool SkipPhrase { get; set; }
public long Timestamp { get; set; }
}
Why does the last test hang?
Acquiring a value via an async method:
var result = Task.Run(() => asyncGetValue()).Result;
Syncronously calling an async method
Task.Run( () => asyncMethod()).Wait();
No deadlock issues will occur due to the use of Task.Run.
You're running into the standard deadlock situation that I describe on my blog and in an MSDN article: the async method is attempting to schedule its continuation onto a thread that is being blocked by the call to Result.
In this case, your SynchronizationContext is the one used by NUnit to execute async void test methods. I would try using async Task test methods instead.
You can avoid deadlock adding ConfigureAwait(false) to this line:
IRestResponse<DummyServiceStatus> response = await restResponse;
=>
IRestResponse<DummyServiceStatus> response = await restResponse.ConfigureAwait(false);
I've described this pitfall in my blog post Pitfalls of async/await
You are blocking the UI by using Task.Result property.
In MSDN Documentation they have clearly mentioned that,
"The Result property is a blocking property. If you try to access it
before its task is finished, the thread that's currently active is
blocked until the task completes and the value is available. In most
cases, you should access the value by using Await or await instead of
accessing the property directly."
The best solution for this scenario would be to remove both await & async from methods & use only Task where you're returning result. It won't mess your execution sequence.
An addition to the answer given by #HermanSchoenfeld. Unfortunately the quote below is not true:
No deadlock issues will occur due to the use of Task.Run.
public String GetSqlConnString(RubrikkUser user, RubrikkDb db)
{
// deadlock if called from threadpool,
// works fine on UI thread, works fine from console main
return Task.Run(() =>
GetSqlConnStringAsync(user, db)).Result;
}
The execution is wrapped inside a Task.Run, this will schedule the task on the threadpool the block the calling thread. This is okay, as long as the calling thread is not a threadpool thread. If the calling thread is from the threadpool then the following disaster happens: A new task is queued to the end of the queue, and the threadpool thread which would eventually execute the Task is blocked until the Task is executed.
In library code there is no easy solution as you cannot assume under what context your code is called. The best solution is to only call async code from async code, blocking sync APIs from sync methods, don’t mix them.
Source:
https://medium.com/rubrikkgroup/understanding-async-avoiding-deadlocks-e41f8f2c6f5d
If you don't get any callbacks or the control hangs up, after calling the service/API async function, you have to configure Context to return a result on the same called context.
Use TestAsync().ConfigureAwait(continueOnCapturedContext: false);
You will be facing this issue only in web applications, but not in static void main.

Load data (async/await)

I'm not sure about this state. I need to get data from database asynchrony.
I have class DB
public class Db{
public async Task<ObservableCollection<Person>> GetAllPerson()
{
using (var context = new Db())
{
// get data and return ObservableCollection<Person>
}
}
}
In the ViewModel I call LoadData function.
public class VM{
public ObservableCollection<Person> Person { get; set; }
private readonly DB sqlRepository;
public VM()
{
sqlRepository=new DB();
LoadData();
}
private async void LoadData()
{
Person= await sqlRepository.GetAllPerson();
}
}
I got warning: Warning CS1998 This async method lacks 'await' operators and will run synchronously.
How can I run my function asynchronously?
Should I use ?
Person=await Task.Run(()=>this.sqlRepository.GetAllPerson());
How can I run my function asynchronously?
You're approaching your problem from the wrong direction. You're trying to go "outside in" - your ViewModel wants to load the database data asynchronously. And that's a fine way of describing the problem, but it's the wrong way to solve it.
To solve it more easily, start at the other end. Whatever methods are actually calling into the database (e.g., Entity Framework calls) should be made asynchronous first, and then let async grow out from there. Eventually you'll end up with something like:
public async Task<ObservableCollection<Person>> GetAllPersonAsync()
{
using (var context = new Db())
{
// This code wasn't shown in the question.
// But from the compiler warning, it was probably using something like
// var people = People.ToList();
// return new ObservableCollection<Person>(people);
// And the async version should be:
var people = await People.ToListAsync();
return new ObservableCollection<Person>(people);
}
}
Which you could consume as:
private async void LoadData()
{
Person = await sqlRepository.GetAllPersonAsync();
}
But I recommend consuming it via NotifyTask as described in my MVVM async data binding article. That approach would give you the ability to data-bind busy spinners and whatnot.
Should I use [Task.Run]?
No. That's "fake asynchrony" - where your code acts like it's asynchronous but it's really just synchronously running on a background thread.

ViewModel creation blocking UI

In my XAML UI I have a listview which contains a list of complex objects. These complex objects have an async initialization method which loads in the data (downloads an image, formats the text, etc).
Here's the setup (pseudo code):
public class PageViewModel
{
public ObservableCollection<ItemViewModel> Items;
public async Task InitializeAsync()
{
var models = await GetModelsAsync();
List<Task> initTasks = new List<Task>();
foreach(var model in models)
{
var vm = new ItemViewModel(model)
initTasks.Add(vm.InitializeAsync());
Items.Add(vm);
}
await Task.WhenAll(initTasks);
}
}
The issue i'm seeing is that it seems like the UI thread is being blocked and unresponsive until all the tasks have completed which is confusing me. All my async complex logic is in an awaitable task.
This lead me to experiment with the only other logic here, the creation of the view model. The issue seems to dissapear when I wrap the following code in a Task.Run:
var vm = Task.Run(() => new ItemViewModel(item))
This surprised me because there's very little to no logic in the ViewModel constructor which is why I was fine initially putting it on the UI thread.
Does anyone have thoughts on why I would see the UI thread block here? Do you have any code suggestions?
I can't be sure where you are calling your init() but you should be (unless you use an intelligent framework that automatically calls it for you) callingit from your Page OnNavTo override, like this:
public sealed partial class MainPage : Page
{
public MainPage() { InitializeComponent(); }
MainPageViewModel ViewModel => DataContext as MainPage;
protected override async void OnNavigatedTo(NavigationEventArgs e)
{
DataContext = new MainPageViewModel();
await DataContext.InitAsync();
}
}
public class MainPageViewModel
{
public Task InitAsync()
{
// TODO
}
}
As an aside, I don't recommend you load your models in parallel, but instead in series because of performance on low-powered devices. Make sense? I should not make a noticeable difference to your user, bur your app will not lock if the generation is costly.
Best of luck!

Categories

Resources