Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Will caliburn.micro do the right thing with async method on ViewModel?

As mentioned elsewhere, the new .NET async/await model propagates through layers of software like a virus. A recent async change has now bubbled up to my view model, and I am wondering if it is safe change declaration from public void DoStuff() to public async Task DoStuff() ?

Thanks!

like image 588
Michael Teper Avatar asked Mar 14 '13 18:03

Michael Teper


4 Answers

The support of asynchronous programming model in Caliburn.Micro is pretty good now.

Few things you can do:

  • Use async/await in Action method. Be careful, as action methods are technically event handlers, you shoud do async void rather than async Task.
  • Asynchronous event handlers for Screen's events, like Activated, ViewLoaded and other.
  • Asynchronous overrides for Screen's methods: OnInitialize, OnActivate, ... You can override then as protected override async void OnInitialize(){} and inside you can await another task.
  • Convert Coroutines to Tasks. Use ExecuteAsync() extension method. Coroutines still have some advantages in some scenarios, like execution context.
  • IHandleWithTask<TMessage> - pretty handy...

There's a blog post desribing some use cases with few code snippets. And a GitHub repository with sample project I've used to play with async/await in Caliburn.

like image 82
Valeriu Caraulean Avatar answered Oct 20 '22 22:10

Valeriu Caraulean


The answer is 'yes', starting with Caliburn.Micro 1.5.

See release announcement.

like image 21
Michael Teper Avatar answered Oct 20 '22 21:10

Michael Teper


It's safe, but will break your existing global exception handling. After I did the refactoring, I didn't see any error dialogues anymore, to fix that, I had to subscribe to the Coroutine.Completed event:

Coroutine.Completed += (s, a) =>
{
    //Do something here ...
};

You can do that in your App.xaml.cs file.

Example from my code on how I handle all possible errors raised in my app:

protected override void OnStartup(StartupEventArgs e)
{
    SetupExceptionHandlers();
    base.OnStartup(e);
}

private void SetupExceptionHandlers()
{
    AppDomain.CurrentDomain.UnhandledException += (s, a) =>
    {
        HandleException((Exception)a.ExceptionObject, "AppDomain.CurrentDomain.UnhandledException");
    };

    Current.DispatcherUnhandledException += (s, a) =>
    {
        HandleException(a.Exception, "Application.Current.DispatcherUnhandledException");
        a.Handled = true;
    };

    TaskScheduler.UnobservedTaskException += (s, a) =>
    {
        Dispatcher.InvokeAsync(() => HandleException(a.Exception, "TaskScheduler.UnobservedTaskException"));
        a.SetObserved();
    };

    Coroutine.Completed += (s, a) =>
    {
        if (a.Error != null)
        {
            HandleException(a.Error, "Coroutine.Completed");
        }
    };
}

private void HandleException(Exception exception, string source)
{
    logger.Error(exception, "Unhandled exception occured (Source: {0})", source);

    var msg = new ShowErrorDialogEvent(exception, exception.GetBaseException().Message);
    eventAggregator.PublishOnUIThread(msg);
}

In-case you're wondering, the logger and eventAggregator variables are instantiated from the bootstrapper class in the OnStartup method before calling DisplayRootViewFor.

like image 2
Shahin Dohan Avatar answered Oct 20 '22 22:10

Shahin Dohan


Marco Amendola, a project manager in the Caliburn.Micro project wrote an article that has this title: Coroutines are dead. Long live Coroutines. and he titled it this way because of the emergence of the async/wait programming model and if you read the article you will see that async/wait bring to life what Coroutines did in the past so i assume you could use them safely where you have used Coroutines before. i advise you to read the article.

like image 1
Ibrahim Najjar Avatar answered Oct 20 '22 22:10

Ibrahim Najjar