Running async methods from contructor or propertys - c#

I am using MVVM and WPF, now I am calling async process from my viewmodel class's constructor like this:
Task.Run(() => this.MyMetho(someParam)).Wait();
The problem with this is that screen freeze it until the task ends.
Another way is to create a LoadDataMethod in the ViewModel and call it from the view in the event handler for UserControl_Loaded something like this
private async void UserControl_Loaded(object sender, RoutedEventArgs e)
{
VMRep vm = (VMRep)this.DataContext;
await vm.LoadDataMethod();
}
that way works better, but I guess there is a better way to do the load of async data for a View.
Thanks for your comments

You can create async factory method, and make your constructor private or protected
public static async bool Create() {
var control = new UserControl();
return await LoadDataMethod();
}

I guess there is a better way to do the load of async data for a View
The key is to realize that all ViewModels must initialize immediately. If you think about it, it doesn't make sense to initialize them asynchronously, because when WPF constructs your VM, it has to show it right away. Not 10 seconds from now whenever the download completes.
So, shift your thinking a bit, and the answer will become clearer. Your VM needs to initialize immediately, but you don't have the data to display yet. So the appropriate thing to do is to immediately initialize to a kind of "loading..." state and start the download of the data. When the data arrives, then you update the VM to show the data.
You can do it the way you have it:
private async void UserControl_Loaded(object sender, RoutedEventArgs e)
{
VMRep vm = (VMRep)this.DataContext;
await vm.LoadDataMethod();
}
but this doesn't provide a clean way for your View to detect whether the data is loading or has completed loading. I wrote a data-bindable Task<T> wrapper (updated version here) that helps with this kind of situation. It can be used like this:
public MyViewModel()
{
MyData = NotifyTask.Create(LoadDataAsync());
}
public NotifyTask<TMyData> MyData { get; }
and then your View can data-bind to MyData.Result to get to the data, as well as other properties such as MyData.IsNotCompleted to show/hide "loading" indicators.

Well, since I don't know exactly what you're trying to accomplish I'm trying my best to give you a brief explanation about Lazy
private Lazy<Task<string>> getInfo;
In this case I'm holding a field with Lazy<Task<string>> in your case it would be Lazy<Task<VMRep>>. I'm using this field, so that you can call this lazy initialzier inside your class.
public Laziness()
{
this.getInfo = new Lazy<Task<string>>(async () => await this.GetInfo());
}
In the constructor I'm assigning the value to the lazy field. In this case with the method GetInfo()
public string GetLazyInfo
{
get
{
return this.getInfo.Value.Result;
}
}
Exposing the getInfo field with a public property. And returning the result from the task inside the lazy.
private async Task<string> GetInfo()
{
await Task.Run(async () => await Task.Delay(5000));
return await Task.Run(() => "test");
}
And finally the method, where your magic can happen.

Related

What is the best way to populate a data-bound property from an async method?

I have a simple requirement I am trying to achieve. Basically I have a view that gets populates with a list of businesses. The property for the list of businesses is embodied in my viewmodel class which in turn is bound to the view. This is a simple MVC application with a list of business.
However, the issue I have is that I derive the list of business for another class which is a dependency to the view model, and its basically similar to a repository which I call BusinessService. Busy service is comprised of async methods and this is the dilemma I have, when the call is made from the Ctor of the viewModel or the getter of the property, my application hangs. The call is to a EF database asynchrounous too within the businessservice and am not sure what is the correct approach for this. Please see code below:
ViewModel:
#region Ctor
public BusinessListViewModel(IBusinessService businessService, IStringBuilder builder)
{
_businessService = businessService;
_builder = builder;
InitBusinesses().Wait(); //OPTION 1
}
#endregion
#region Properties
public IEnumerable<BusinessViewModel> _businesses;
public IEnumerable<BusinessViewModel> Businesses
{
get
{
if (_businesses == null)
{
InitBusinesses().Wait(); //OPTION 2
}
return _businesses;
}
set => _businesses = value;
}
private async Task InitBusinesses()
{
var response = await _businessService.Get();
Businesses = response.IsSuccessful
? response.Data.Select(p => new BusinessViewModel(_builder, p))
: new List<BusinessViewModel>();
}
BUSINESS SERVICE:
#region Service Methods
public async Task<Response<IEnumerable<Models.Business>>> Get()
{
var data = await Db.Businesses.ToListAsync();
return new Response<IEnumerable<Models.Business>>
{
IsSuccessful = true,
Message = "Successful",
Data = Mapper.Map<List<Models.Business>>(data)
};
}
Please may you advise the best pattern and the correct way to do this, I already know this is wrong> Thank you
I wrote an article on the subject.
When the UI framework asks your code to display something, it must be displayed immediately (synchronously). ViewModel constructors and data-bound properties should be synchronous and immediate. Doing network I/O is simply not an option; even if you got it working (which is possible), all that would do is block your UI thread, degrading your user experience.
A more proper solution is to synchronously initialize into a loading state ("Loading..." message, spinner, whatever) and also start the asynchronous operation. Then, when the operation completes, update the UI with the actual data.
You should consider using a factory method that returns a Task
private BusinessListViewModel(IBusinessService businessService, IStringBuilder builder)
{
_businessService = businessService;
_builder = builder;
}
public static async Task<BusinessListViewModel> Create(IBusinessService businessService, IStringBuilder builder)
{
var instance = new BusinessListViewModel(businessService, builder)
await InitBusiness();
return instance;
}

wpf mvvm awaiting method blocks UI while simulating with await Task Delay doesn't

When I do following my UI doesn't freeze:
xaml binding:
<Button Command="{Binding LongRunningCommand}" />
viewmodel:
ctor
{
LongRunningCommand = new DelegateCommand<object>(ProcessLongRunningCommand);
}
public DelegateCommand LongRunningCommand { get; private set; }
private async void ProcessLongRunningCommand(object e)
{
await Task.Delay(5000);
}
But if I replace the Task.Delay with a real method then my UI freezes. This is so weird. Look below for an example:
xaml binding:
<Button Command="{Binding LongRunningCommand}" />
viewmodel:
private readonly IDbAccess _dbAccess;
ctor(IDbAccess dbAccess)
{
_dbAccess = dbAccess
LongRunningCommand = new DelegateCommand<object>(ProcessLongRunningCommand);
}
public DelegateCommand LongRunningCommand { get; private set; }
private async void ProcessLongRunningCommand(object e)
{
var callResult = await _dbAccess.LongRunningMethod();
//adjust viewmodel observablecollection property which binds to a ListBox with callResult
}
DbAccess impl:
public Task LongRunningMethod(object e)
{
return Task.Run(() =>
{
...
}
}
Can someone see what I'm doing wrong please, I lost a lot of time on this...
And btw I know that async void not a best practice but I couldn't find a solution to make DelegateCommand asynchronous.
EDIT
After more research we concluded that this is a 'render' issue. the LongRunningCommand is asynchronous but the rendering and the binding is taking time and it blocks the UI. I have no idea how to solve this render issue.
This task awaiting deadlock caught me out for a long time too, until I found the solution.
Replace
var callResult = await _dbAccess.LongRunningMethod();
with
var callResult = await _dbAccess.LongRunningMethod().ConfigureAwait(false);
See this blog post by Stephen Cleary for the details why.
Note that the continuation after the await will now be on a different dispatcher context, so anything that directly updates the UI (such as populating an observable collection) must be marshalled back to the UI dispatcher. Many MVVM frameworks contain utilities to help with this - I use the DispatcherHelper class from MVVM Light.
If you're using Resharper, there's a plugin you can install to check that ConfigureAwait() is added to every await statement.

Call async method with callback from sync UI thread [duplicate]

This question already has answers here:
The calling thread cannot access this object because a different thread owns it.WPF [duplicate]
(6 answers)
Closed 6 years ago.
Im really stuck here... I have a XAML Page UI and want to call an async function everytime the user interacts with the UI.
I use SignalR for networking:
public static class ProtocolClient
{
private static HubConnection hubConnection;
private static IHubProxy protocolHubProxy;
public static async void connect(string server)
{
hubConnection = new HubConnection(server);
protocolHubProxy = hubConnection.CreateHubProxy("ProtocolHub");
protocolHubProxy.On<Body>("BodiesChanged", body =>
//call a callback to return body
);
await hubConnection.Start(); //wait for connection
}
public static async void sendTouch(Touch touch)
{
Body body = await protocolHubProxy.Invoke<Body>("GetBodyForTouch", touch);
//call a callback to return body
}
}
UI:
public sealed partial class MainPage : Page
{
[...]
private void Canvas_PointerPressed(object sender, PointerRoutedEventArgs e)
{
[...]
switch (ptrPt.PointerDevice.PointerDeviceType)
{
case Windows.Devices.Input.PointerDeviceType.Mouse:
if (ptrPt.Properties.IsLeftButtonPressed)
{
//call sendTouch
}
break;
default:
break;
}
[...]
}
}
I need a callback which can modify the UI. How can I call connect and sendTouch out of the UI and pass them a callback?
You don't need a callback. Just add the code after the await hubConnection.Start(); statement. Your method is 'cut in multiple methods' and will 'continue' after the await comes back. The await works like a blocking statement, but will not freeze the gui.
public static async void connect(string server)
{
hubConnection = new HubConnection(server);
protocolHubProxy = hubConnection.CreateHubProxy("ProtocolHub");
protocolHubProxy.On<Body>("BodiesChanged", body =>
//call a callback to return body
);
await hubConnection.Start(); //wait for connection
// add code here.
}
When handling commands async (from gui events), don't forget to disable controls to prevent executing the command more than ones.
Don't use async void methods. If you don't need to return a value, use async Task - if you do, use async Task<SomeType>.
Then, when you need to call an async method (and by convention, these should be named like ConnectAsync and SendTouchAsync), await it:
await SendTouchAsync(...);
When the asynchronous workflow ends, your continuation will be marshalled back to the UI thread (because you awaited from within a synchronization context), and you can manipulate the UI easily.
await kind of appears to work when you use async void, but the problem is that the caller has no way of tracking the asynchronous workflow - as far as the caller is concerned, the method just ended right then and now, and the code in the caller continues as usual.
Make sure to mark Canvas_PointerPressed as async too - sadly, in this case, it must be async void. Make sure to never call the event handler directly - the UI thread can handle the callbacks correctly, your code can't. If you need the same logic from other methods, just separate it into a proper async Task method and await that from the event handler.

Delaying API actions

I'm programming an API for my software, which has a lot of interfaces and my software just inherits them.
I want the API users to have the possibility to do something after X milliseconds, something like this :
public void PerformAction(Action action, int delay)
{
Task.Run(async delegate
{
await Task.Delay(delai);
Form.BeginInvoke(action);
// I invoke on the Form because I think its better that the action executes in my main thread, which is the same as my form's thread
});
}
Now i know that the Task is like a new Thread, I just want to know, is this bad for my software? Is there any other possible better way?
The method will get executed a lot, so I don't know whether this approach is good or bad
You should not be creating a new Task for this, you can instead make the method a Task, something like this:
public async Task PerformAction(Action action, int delay)
{
await Task.Delay(delay);
action(); //this way you don't have to invoke the UI thread since you are already on it
}
And then simply use it like this:
public async void Butto1_Click(object sender, EventArgs e)
{
await PerformAction(() => MessageBox.Show("Hello world"), 500);
}
public async Task PerformAction(Action action, int delay)
{
await Task.Delay(delay);
action();
}

How to return data using await function in C# windows phone 8?

I'm new to C#. I
I've a problem related to async methods and await function in C# windows phone 8.0.
I've this http request and can get response. This is working fine and There is no issue...
public async static Task<List<MyAccountData>> GetBalance()
{
HttpClient = new HttpClient();
string response = await client.GetStringAsync("http://xxxx/xxx/xxx");
List<MyAccountData> data = JsonConvert.DeserializeObject<List<MyAccountData>>(response);
return data;
}
I've another class call MainModel
public class MainModel
{
public void LoadData()
{
}
}
So My problem is, I want to call that GetBalance method with in MainModel class and parse data to LoadData method(simply want 2 access Data with in LoadData method). LoadData method can't change return type or can't use async. So how is this possible?
If you want a responsive UI - i.e., one that has a chance of being accepted in the store - then blocking on the async operation is not an option.
Instead, you have to think a bit about how you want your UI to look while the operation is in progress. And while you're thinking about that, also think about how you would want your UI to respond if the operation errors out.
Then you can code up a solution. It's possible to do this with async void, if you catch all exceptions and handle them cleanly:
public async void LoadData()
{
try
{
... // Set up "loading" UI state.
var balance = await GetBalanceAsync();
... // Set up "normal" UI state.
Balance = balance;
}
catch
{
... // Set up "error" UI state.
}
}
However, I prefer to use a type I created called NotifyTaskCompletion, which is a data-bindable wrapper for Task<T> (described in my MSDN article). Using NotifyTaskCompletion, the LoadData becomes much simpler:
public void LoadData()
{
GetBalanceOperation = new NotifyTaskCompletion<Balance>(GetBalanceAsync());
}
public NotifyTaskCompletion<Balance> GetBalanceOperation // Raises INotifyPropertyChanged when set
Then your UI can data-bind to properties on NotifyTaskCompletion<T>, such as IsNotCompleted (for the "loading" state), IsSuccessfullyCompleted and Result (for the "normal" state), and IsFaulted and ErrorMessage (for the "error" state).
There is no difference to use async await in Windows Phone 8 dev:
public class MainModel
{
public async void LoadData()
{
var data = await Foo.GetBalance();
}
}
Depends on whether you want LoadData to be synchronous (not returning until all the data has been streamed in over HTTP, and locking up the UI until then), or to begin the process and return immediately. If you can't change LoadData to async, then those are your only two options.
If you want LoadData to be synchronous:
public void LoadData() {
var task = GetBalance();
var result = task.Result; // will lock up the UI until the HTTP request returns
// now do something with result
}
If you want it to start a background process and return immediately, but for some reason don't want to mark LoadData as async:
public void LoadData() {
BeginLoadData();
}
private async void BeginLoadData() {
var result = await GetBalance();
// now do something with result
}
Though really, there's no reason not to go ahead and make LoadData async. async void does not force you to change the callers in any way (unlike Async<Task<T>>), and it's assignment-compatible with plain old non-async delegates:
public async void LoadData() {
var result = await GetBalance();
// now do something with result
}
// ...
LoadData(); // works just fine
Action myAction = LoadData; // works just fine
As you are working on asynchronus operations you need to wait until the operation is completed.The return type async/await method is always Task(TResult), to access the result of the async/await you need to use Result Property.The get accessor of Result property ensures that the asynchronous operation is complete before returning.
public void LoadData()
{
var data = GetBalance().Result;
}

Categories