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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With