Marshalling data back to UI Thread from within Async Action - c#

I've got an ICommand that needs to set data to a property on the UI Thread.
public override async void Execute(object parameter)
{
var vm = (MyVm)parameter;
var data = await _myDataService.GetData();
vm.MyData = data; // must be set on UI Thread due to binding.
}
Now I want to wrap my call in an event logger (I originally wanted to do AOP and decorate the method with a logging attribute, but I couldn't figure it out in a PCL). So I moved onto wrapping my call like this.
public override void Execute(object parameter)
{
EventLogger.LogEvent(this,
EventLogEntryType.Command,
EventLogErrorSeverity.Warning,
Errors.GetServiceAreaCommand_ErrorMessage,
async () =>
{
var vm = (MyVm)parameter;
var data = await _myDataService.GetData();
vm.MyData = data; // must be set on UI Thread due to binding.
});
}
Here's the LogEvent method.
public static void LogEvent(object sender,
EventLogEntryType entryType,
EventLogErrorSeverity eventLogErrorSeverity,
string friendlyErrorMessage,
Action action)
{
var name = sender.GetType().Name.SplitCamelCase();
var startEntry = new EventLogEntry(entryType);
LogEvent(string.Format("Start: {0}", name), startEntry);
try
{
action.Invoke();
}
catch (Exception ex)
{
var exEntry = new EventLogEntry(EventLogEntryType.Error, friendlyErrorMessage, false, ex)
{
ErrorSeverity = eventLogErrorSeverity
};
LogEvent(string.Format("Error: {0}", name), exEntry);
if (eventLogErrorSeverity == EventLogErrorSeverity.Critical)
{
throw;
}
}
var endEntry = new EventLogEntry(entryType);
LogEvent(string.Format("Finish: {0}", name), endEntry);
}
The problem is that it appears as though I'm STILL setting the property on a background thread instead of the Main thread (IllegalStateException in Android).
What is the cleanest way to set the data as is being done in the first example, while still wrapping the Action in a logging method?
I also had success creating a base class for ICommand, but it A) changed the method signatures for CanExecute and Execute, and B) it also (obviously) doesn't extend it's capabilities beyond Commands.
I'm looking for a clean way to log methods (BeforeExecute, AfterExecute, OnError) no matter what they do.
As an aside, the ideal logging mechanism would be to use an Interceptor, but I'm just not strong enough in my C# chops to implement it.
[Log(EventLogEntryType.Command, EventLogErrorSeverity.Warning, "Some Friendly Message")]
public override async void Execute(object parameter)
{
var vm = (MyVm)parameter;
var data = await _myDataService.GetData();
vm.MyData = data; // must be set on UI Thread due to binding.
}

If you have (caveat below) access to the Activity object in your code then you can probably do;
Activity.RunOnUiThread(() => {
//Execute my code on UIThread here
});
But it's an if, because I note you're using a PCL, or have referenced using one, so I suspect that a shared library is not going to know anything about the Activity (unless you pass that too). Very much depends on your app structure and where this code is, but within the main Xamarin.Android project where your views are the above should work

Related

Is there a simple way to execute a method on the UI thread during an asynchronous task?

During the lengthy execution of an asynchronous method, launched with Task.Run() I might need to get back to the user for additional input or confirmation, e.g. a message box or a file dialog which has to be executed on the UI thread.
Is there a simple way to do this?
private void buttonApply_Click (object sender, EventArgs e) {
try {
// ...
await executeAsync (_cts.Token, progress);
} catch (OperationCanceledException) { }
// ...
}
private async Task executeAsync (CancellationToken cancellationToken, IProgress<string> progress) {
// ...
await Task.Run (() => execute (path, cancellationToken, progress), cancellationToken);
}
private void execute (string path, CancellationToken cancellationToken, IProgress<string> progress) {
// do some work, report progress, check for cancellation
// --> depending on initial work, request additional input via UI thread, how?
// do more work, based on initial work and requested input
}
Thanks for your comments, they pointed me to the right direction. I looked into the implementation of System.Progress<T> and I think, I will start from there.
System.Progress<T> captures the SynchronizationContext and Report(T value) invokes the asynchronous Post() method of the sync context.
Since I need feedback from my invocation, I will go for the synchronous Send() method instead and presumably base this on a new interface, ICallbackQuery<T, TResult> or something like that, modeled after IProgress<T> and Progress<T>
There is a framework called ReactiveUI - it uses ReactiveExtensions and observables to make MVVM life easier.
You don't have deep dive and use the whole framework, but it has a neat concept of Interaction:
public class ViewModel : ReactiveObject
{
private readonly Interaction<string, bool> confirm;
public ViewModel()
{
this.confirm = new Interaction<string, bool>();
}
public Interaction<string, bool> Confirm => this.confirm;
public async Task DeleteFileAsync()
{
var fileName = ...;
// this will throw an exception if nothing handles the interaction
var delete = await this.confirm.Handle(fileName);
if (delete)
{
// delete the file
}
}
}
public class View
{
public View()
{
this.WhenActivated(
d =>
{
d(this
.ViewModel
.Confirm
.RegisterHandler(
async interaction =>
{
var deleteIt = await this.DisplayAlert(
"Confirm Delete",
$"Are you sure you want to delete '{interaction.Input}'?",
"YES",
"NO");
interaction.SetOutput(deleteIt);
}));
});
}
}
You can just focus on Interactions.
I would also recommend ReactiveCommand to avoid problems with buttons and async actions.

Asynchronous data loading and subsequent error handling

I have an application that involves a database. Previously, upon opening a window, I would query the database and use this to populate aspects of my view model. This worked reasonably well, but could create noticeable pauses when the data access took longer than expected.
The natural solution, of course, is to run the database query asynchronously and then populate the view model when that query completes. This isn't too hard, but it raises some interesting questions regarding error handling.
Previously, if something went wrong with the database query (a pretty big problem, granted), I would propagate the exception through the view model constructor, ultimately making it back up to the caller that wanted to open the window. It could then display an appropriate error and not actually open the window.
Now, however, the window opens right away, then populates later as the query completes. The question, now, is at what point should I check for an error in the background task? The window is already open, so the behavior needs to be different somehow, but what is a clean way to indicate the failure to the user and allow for graceful recovery/shutdown?
For reference, here is a snippet demonstrating the basic pattern:
public ViewModel()
{
_initTask = InitAsync();
//Now where do I check on the status of the init task?
}
private async Task InitAsync()
{
//Do stuff...
}
//....
public void ShowWindow()
{
var vm = new ViewModel(); //Previously this could throw an exception that would prevent window from being shown
_windowServices.Show(vm);
}
One option I've considered is use an asynchronous factory method for constructing the ViewModel, allowing the entire thing to be constructed and initialized before attempting to display the window. This preserves the old approach of reporting errors before the window is ever opened. However, it gives up some of the UI responsiveness gained by this approach, which allows initial loading of the window to occur in parallel with the query and also allows me (in some cases) to update the UI in increments as each query completes, rather than having the UI compose itself all at once. It avoids locking up the UI thread, but it doesn't reduce the time before the user actually sees the window and can start interacting with it.
Maybe use some kind of messaging/mediator between your viewmodel and underlying service?
Semi-pseudo code using MVVMLight
public ViewModel()
{
Messenger.Default.Register<NotificationMessage<Exception>>(this, message =>
{
// Handle here
});
Task.Factory.StartNew(() => FetchData());
}
public async Task FetchData()
{
// Some magic happens here
try
{
Thread.Sleep(2000);
throw new ArgumentException();
}
catch (Exception e)
{
Messenger.Default.Send(new NotificationMessage<Exception>(this, e, "Aw snap!"));
}
}
I dealt with a similar problem here. I found it'd be best for me to raise an error event from inside the task, like this:
// ViewModel
public class TaskFailedEventArgs: EventArgs
{
public Exception Exception { get; private set; }
public bool Handled { get; set; }
public TaskFailedEventArgs(Exception ex) { this.Exception = ex; }
}
public event EventHandler<TaskFailedEventArgs> TaskFailed = delegate { };
public ViewModel()
{
this.TaskFailed += (s, e) =>
{
// handle it, e.g.: retry, report or set a property
MessageBox.Show(e.Exception.Message);
e.Handled = true;
};
_initTask = InitAsync();
//Now where do I check on the status of the init task?
}
private async Task InitAsync()
{
try
{
// do the async work
}
catch (Exception ex)
{
var args = new TaskFailedEventArgs(ex);
this.TaskFailed(this, args);
if (!args.Handled)
throw;
}
}
// application
public void ShowWindow()
{
var vm = new ViewModel(); //Previously this could throw an exception that would prevent window from being shown
_windowServices.Show(vm);
}
The window still shows up, but it should be displaying some kind of progress notifications (e.g. using IProgress<T> pattern), until the end of the operation (and the error info in case it failed).
Inside the error event handler, you may give the user an option to retry or exit the app gracefully, depending on your business logic.
Stephen Cleary has a series of posts on his blog about Async OOP. In particular, about constructors.

How can I load my viewmodel and still get events?

I'm trying to implement MVVM and in the ViewModel I'm doing some async fetching of data. For that purpose I've tried to loading data in the constructor:
MyModel Model { get; set; }
public MyViewModel()
{
Model = new MyModel();
Model.Foo = await LoadDataFromIsolatedStorage();
But this isnt valid as you cant append async to the contructor. So I tried a public static load function:
MyModel Model { get; set; }
public MyViewModel()
{
Model = new MyModel();
}
async public static void Load()
{
Model.Foo = await LoadDataFromIsolatedStorage();
But here WP8 complains that it Cannot await void. Because you would set up the ViewModel and bind it to the View in the code behind of the view. Boring.
Lastly a fix is making the Load function return a ViewModel, so that you in the code behind of the view can do something like:
protected override void OnNavigatedTo(NavigationEventArgs e)
{
MyViewModel viewModel = await MyViewModel.Load();
with the following code to load:
MyModel Model { get; set; }
public MyViewModel()
{
Model = new MyModel();
}
async public static Task<MyViewModel> Load()
{
MyViewModel viewModel = new MyViewModel();
viewModel.Model.Foo = await LoadDataFromIsolatedStorage();
return viewModel;
NOW, the problem at hand is that I have no control if the data loaded should force the application to navigate to another page. Lets say MyViewModel loads a variable from isolated storage, that should then make the app navigate to another page?
I've set up eventlistener to MyViewModel to make the app navigate, but I cant do this when I initiate it.
Does not work with events:
protected override void OnNavigatedTo(NavigationEventArgs e)
{
MyViewModel viewModel = await MyViewModel.Load();
viewModel.NavigationAction += viewmodel_NavigationAction;
}
void viewmodel_NavigationAction(sender, args)
{
NavigationService.Navigate(...)
}
Would work but I "cannot await void":
async protected override void OnNavigatedTo(NavigationEventArgs e)
{
MyViewModel viewModel = new MyViewModel();
viewModel.NavigationAction += viewmodel_NavigationAction;
await viewModel.Load(); // given the Load only is a async void and not Task<T>
}
void viewmodel_NavigationAction(sender, args)
{
NavigationService.Navigate(...)
}
I assume this code block "doesn't work" because the event is set too late, after the data is loaded:
MyViewModel viewModel = await MyViewModel.Load();
viewModel.NavigationAction += viewmodel_NavigationAction;
In that case, fixing your last code block is simple enough: have Load return Task and it will work. Task is the async equivalent of void; you should never use async void unless you're writing an event handler. See my best practices article for more information.
However, even when you get this working, you'll end up with an empty view until the VM loads; this may be a poor user experience if your load could take a long time (or errors out, say, if there's no network connectivity).
You may want to consider using NotifyTaskCompletion from my AsyncEx library, which I describe on my blog. That pattern allows you to (immediately) create a VM in a "loading" state, which will transition (via INotifyPropertyChanged) to a "loaded" or a "loading error" state. That pattern provides a better UX, IMO.
What roliu wrote makes pretty much sense since the whole concept of async/await pattern is based on Tasks. When writing an asynchronous method you ALWAYS need to return a task to make sure the following await will work. Usually the compiler will make some special replacements in the background, so you don't have to care about it.
You will never get a bool as a result of an asynchronous operation - but a generic bool typed Task! Make sure you understood the pattern like described at MSDN.
Then you can create something like this. It is not a Win8 App. For simplicity I chose it to be a console application.
class Program
{
static void Main(string[] args)
{
DoWork();
}
static async void DoWork()
{
await YourVoidMethod();
}
static Task YourVoidMethod()
{
Task task = Task.Run(() =>
{
// Your payload code
}
);
return task;
}
}
Just as a hint: When working with GUIs you also need to work with the dispatcher. When changing data of the UI thread you otherwise could generate cross thread exceptions.

Where to Break the Chain with Task, ContinueWith, Lock

I have MVP application C#, .NET 4, WinForms. It uses Bridge class which communicate with third party app via NamedPipe.
The command flow is like this: View → Presenter → Manager → Bridge → Client
And back in the reverse order. View is prepared for multitasking. I split reverse chain in Manager by rising event with the result, but it doesn't help.
// View class
public void AccountInfo_Clicked() { presenter.RequestAccountInfo(); }
public void UpdateAccountInfo(AccountInfo info)
{
if (pnlInfo.InvokeRequired)
pnlInfo.BeginInvoke(new InfoDelegate(UpdateAccountInfo), new object[] {info});
else
pnlInfo.Update(info);
}
// Presenter class
public void RequestAccountInfo() { manager.RequestAccountInfo(); }
private void Manager_AccountInfoUpdated(object sender, AccountInfoEventArgs e)
{
view.UpdateAccountInfo(e.AccountInfo);
}
// Manager class
public void RequestAccountInfo()
{
AccountInfo accountInfo = bridge.GetAccountInfo();
OnAccountInfoUpdated(new AccountInfoEventArgs(accountInfo));
}
// Bridge class
public AccountInfo GetAccountInfo() { return client.GetAccountInfo(); }
// Client class
public AccountInfo GetAccountInfo()
{
string respond = Command("AccountInfo");
return new AccountInfo(respond);
}
private string Command(string command)
{
var pipe = new ClientPipe(pipeName);
pipe.Connect();
return pipe.Command(command);
}
I want to unfreeze the UI during command processing. There are also other commands that can be executed. Finally all commands reach Command(string command) method in Client.
I tried to break the chain in Manager by using task and ContinueWith but it results to pipe failing to connect. The reason is that client is not thread safe.
// Manager class
public void RequestAccountInfo()
{
var task = Task<AccountInfo>.Factory.StartNew(() => bridge.GetAccountInfo());
task.ContinueWith(t => { OnAccountInfoUpdated(new AccountInfoEventArgs(t.Result)); });
}
My question is: Where to use Task, ContinueWith and where to Lock?
I assume I can lock only Command(string command) because it is the ultimate method.
private string Command(string command)
{
lock (pipeLock)
{
var pipe = new ClientPipe(pipeName);
pipe.Connect();
return pipe.Command(command);
}
}
Can I use Task, Wait in Command in Client class?
I think the problem you are having is that bridge.GetAccountInfo() is trying to extract information from the UI itself - hence the UI thread. This code
public void RequestAccountInfo()
{
var task = Task<AccountInfo>.Factory.StartNew(() => bridge.GetAccountInfo());
task.ContinueWith(t => { OnAccountInfoUpdated(new AccountInfoEventArgs(t.Result)); });
}
is attempting to execute the bridge.GetAccountInfo() method (accessing the UI) from a background thread-pool thread.
My first question here would be how expensive is the call to bridge.GetAccountInfo()? If it is not expensive, it makes no sense to put working into multi-threading this aspect. If it is expensive, you will have to think about a way to make this operation thread safe (I can't advise without more information).
Another thing to do would assess the expense of a move to WCF. This handles most synchronisation problems for you... I am sorry I can't be of more help. I wrote the above before I read your last comment.
I hope this is of some use.
Aside: something to be aware of is SynchronizationContext. Using a TaskScheduler you can launch a Task on the UI thread (this is not what you want here as this again will just block the UI - however, this can be good to know when reporting [in .NET 4.0]. To launch your code above on the UI thread you can do
public void RequestAccountInfo()
{
var task = Task<AccountInfo>.Factory.StartNew(() =>
bridge.GetAccountInfo(),
TaskScheduler.FromCurrentSynchronizationContext());
task.ContinueWith(t => { OnAccountInfoUpdated(new AccountInfoEventArgs(t.Result)); });
}
I locked Command in Client class. It appears that it works perfectly in that way. No blocking UI, no pipe errors. I lock on pipeName because each copy of View is using a unique pipe name.
I applied Task<Type>, ContinueWith to all commands in Manager class.
// Manager class
public void RequestSomeInfo()
{
var task = Task<SomeInfo>.Factory.StartNew(() => bridge.GetSomeInfo());
task.ContinueWith(t => { OnInfoUpdated(new InfoEventArgs(t.Result)); });
}
// Client class
private string Command(string command)
{
lock (pipeName)
{
var pipe = new ClientPipe(pipeName);
pipe.Connect();
return pipe.Command(command);
}
}

Decent pattern for testable async operations?

I had trouble finding a simple, flexible pattern to allow me to write code in my ViewModels that in runtime would run asynchronously but during test-time run synchronously. This is what I came up with - does anyone have any suggestions? Is this a good path to go down? Are there better existing patterns out there?
LongRunningCall definition:
public class LongRunningCall
{
public Action ExecuteAction { get; set; }
public Action PostExecuteAction { get; set; }
public LongRunningCall(Action executeAction = null, Action postExecuteAction = null)
{
ExecuteAction = executeAction;
PostExecuteAction = postExecuteAction;
}
public void Execute(Action<Exception> onError)
{
try
{
ExecuteAction();
PostExecuteAction();
}
catch (Exception ex)
{
if (onError == null)
throw;
onError(ex);
}
}
public void ExecuteAsync(TaskScheduler scheduler, Action<Exception> onError)
{
var executeTask = Task.Factory.StartNew(ExecuteAction);
var postExecuteTask = executeTask.ContinueWith((t) =>
{
if (t.Exception != null)
throw t.Exception;
PostExecuteAction();
}, scheduler);
if (onError != null)
postExecuteTask.ContinueWith((t) => { onError(t.Exception); });
}
}
Usage:
var continueCall = new LongRunningCall(continueCommand_Execute, continueCommand_PostExecute);
if (svc.IsAsyncRequired)
continueCall.ExecuteAsync(TaskScheduler.FromCurrentSynchronizationContext(), continueCommand_Error);
else
continueCall.Execute(continueCommand_Error);
The only real pre-requisite is that you need to know at runtime if you're supposed to use async/sync. When I run my unit tests I send in a mock that tells my code to run synchronously, when the application actually runs IsAsyncRequired defaults to true;
Feedback?
I would prefer to encapsulate the decision on whether to execute code synchronously or asynchronously in a separate class that can be abstracted behind an interface such as this:
public interface ITaskExecuter
{
void ScheduleTask(
Action executeAction,
Action postExecuteAction,
Action<Exception> onException);
}
An instance of a class implementing ITaskExecuter can be injected where required.
You can inject different instances for testing versus production scenarios.
Usage becomes:
taskExecuter.ScheduleTask(
continueCommand_Execute,
continueCommand_PostExecute,
continueCommand_Error);
with no separate code paths in the calling class for test versus production.
You have the option of writing tests that:
just check the correct actions are passed to the task executer, or
configuring the task executer to execute the action synchronously and
test for the desired result, or
do both.
I did something very simmilar at my current job, but can't get to the code to copy/paste it right now...
Basically what I did was to create an IWorker interface, with a DoWork(Func<>) method.
Then I created 2 derived classes, one 'AsyncWorker' and one 'SyncWorker'. The SyncWorker just executes the passed in Func (synchronously), and the 'AsyncWorker' is a wrapper around a BackgroundWorker that sends the passed in Func off to the BackgroundWorker to be processed asynchronously.
Then, I changed my ViewModel to have an IWorker passed in. This moves the dependency resolution out of the ViewModel, so you can use a Dep. Inj. utility (I use Unity and Constructor injection).
Since I use Unity, in my unit test configuration, I then map IWorker to SyncWorker, and in production I map IWorker to AsyncWorker.
Hope that makes sense... I know it'd be easier if I had the code on hand...
Consider changing ExecuteAsync so that it will return a Task:
public Task ExecuteAsync(TaskScheduler scheduler, Action<Exception> onError)
So in production code, I would just call it as is:
longRunningCall.ExecuteAsync(
TaskScheduler.FromCurrentSynchronizationContext(),
continueCommand_Error);
But in unit tests, I would wait for the task to actually finish:
var task = longRunningCall.ExecuteAsync(
TaskScheduler.FromCurrentSynchronizationContext(),
continueCommand_Error);
task.Wait();

Categories

Resources