Use of Async/Await for EventHandlers

Posted on

Problem

I have a MVC WinForms application. I am using Dependency Injection with Ninject as the IoC container.

public class SqlObjectExplorerController : ToolController, ISqlObjectExplorerController
{
    private ISqlObjectExplorerView view = null;
    private SqlServerStructureProvider structureProvider;
    private IProgress<IProgressInfo> progress;

    public SqlObjectExplorerController(ISqlObjectExplorerView view)
    {
        if (view == null)
            throw new ArgumentNullException("view");

        this.view = view;
        InitializeEventHandlers();
        ...
    }

    private void InitializeEventHandlers()
    {
        (view as ToolView).Initialize += new EventHandler(async (s, e) => await RefreshObjectExplorerAsync());
        view.OnRefreshObjectExplorerClicked += new EventHandler(async (s, e) => await RefreshObjectExplorerAsync());
        view.OnAddServerInstanceClicked += new EventHandler(async (s, e) => await AddServerInstanceAsync());
        view.OnNewSqlQueryClicked += new EventHandler<NewSqlQueryRequestEventArgs>((s, e) => OpenNewSqlQueryDocument(e));
        view.OnExpandRequested += new EventHandler<BeforeExpandEventArgs>((s, e) => BuildSubStructureForDatabaseNode(e));
    }

    private async Task RefreshObjectExplorerAsync()
    {
        await InitializeObjectExplorerAsync();
        view.InitializeObjectExplorer(ServerCache);
    }

    ... // Lots more code.

My question concerns the use of async/await in setting up the likes of += new EventHandler(async (s, e) => await RefreshObjectExplorerAsync());. There are some tasks I need to do on a background thread, so I am using async/await, my questions are about my understanding:

  1. In doing async (s, e) => await SomeMethodAsync() I am merely setting up an event handler equivalent to private async void SomeMethodAsync(object s, EventArgs e) { ... }. Which I think is fine in this case of the fire-and-forget Task I want to undertake, is it?

  2. I don’t think there are any problems in setting this up these async handlers in the ctor of the controller, am I right?

The code seems to work fine, but is there something I am not seeing?

Solution

The things to consider when it comes to async event handlers are:

  • Exceptions thrown for the handler might be rethrown on the UI SynchronizationContext, which usually crashes the application.
  • After you raise the event, the handlers won’t be completed yet. The execution of a handler might be interleaved with the execution of the code after the raising and execution of multiple handlers can also be interleaved with each other.

Assuming those caveats are acceptable for you, code like this should be fine.


Also:

view.OnRefreshObjectExplorerClicked += new EventHandler(async (s, e) => await RefreshObjectExplorerAsync());

You should be able to simplify this to just:

view.OnRefreshObjectExplorerClicked += async (s, e) => await RefreshObjectExplorerAsync();

It seems you never unsubscribe the event handlers, are you sure that’s okay?

Leave a Reply

Your email address will not be published. Required fields are marked *