Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

async/await Model View Presenter winforms

I have a winforms app that is using the MVP pattern as described in this post http://www.codeproject.com/Articles/563809/UIplusDesignplusUsingplusModel-View-Presenter I am converting my app to async/await but am having issues.

Here is an example to illustrate my issue

public interface IView
{
    event Action DoSomething;
}

public partial class MyForm : Form, IView
{
    public event Action DoSomething;

    public MyForm() 
    {
        InitializeComponent();
    }

    private async void OnSomeButtonClick(object sender, EventArgs e)
    {
        if (DoSomething!= null)
        {
            try
            {
                await DoSomething();
                SomeOtherMethod();
            }
            catch(Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }   
    }
}

public class MyPresenter
{
    private readonly IView _view;
    private ClassWithAwaitbleMethods _foo;

    public MyPresenter(IView view)
    {
        _view = view;

        _view.DoSomething += OnDoSomething;
        _foo = new ClassWithAwaitableMethods();
    }

    public async void OnDoSomething()
    {
        //this may throw an exception
        await _foo.SomeAwaitableWork1();
    }
}

public class MySecondPresenter
{
    private readonly IView _view;
    private ClassWithAwaitbleMethods _foo;

    public MySecondPresenter(IView view)
    {
        _view = view;

        _view.DoSomething += OnDoSomething;
        _foo = new AnotherClassWithAwaitableMethods();
    }

    public async void OnDoSomething()
    {
        //this may throw an exception
        await _foo.SomeAwaitableWork2();
    }
}

This code is not awaiting properly and when an exception is thrown it is not caught. This is because of async void. When this code was not async/await exceptions were caught fine.

I know async void is a no no except for top level events but the way my app is designed I can't really get around that. When I only had one subscriber I changed the IView interface to

public interface IView
{
    Func<Task> DoSomething {get; set;};
}

and wired things up like this

public MyPresenter(IView view)
{
        _view = view;
        _view.DoSomething = OnDoSomething;
        _foo = new ClassWithAwaitableMethods();
}

Which is hacky but awaits things properly and catches exceptions. Any help or insight would be greatly appreciated.

like image 834
jeffreyk Avatar asked Aug 20 '26 12:08

jeffreyk


1 Answers

The core problem is that the code is using events as a strategy pattern rather than an observer pattern. There isn't much you can do with this code as it currently stands; a proper refactoring would require callback interfaces rather than events. E.g.:

// An instance of this is passed into the concrete view.
public interface IViewImplementation
{
  void DoSomething();
  // Or the async equivalent:
  //   Task DoSomethingAsync();
}

However, there are some workarounds you can apply, if that level of refactoring is unsavory. I cover such "async events" on my blog. There are a few approaches; it is possible (though awkward) to define a Task-returning event. My favorite approach, though, is Deferrals, mainly because deferrals are a concept already familiar to WinRT developers.

like image 96
Stephen Cleary Avatar answered Aug 23 '26 00:08

Stephen Cleary



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!