Search code examples
c#multithreadingwinformsasync-awaitappdomain

No SynchronizationContext when calling Await in a another AppDomain


I have successfully built a plugin mechanism where I can create UI controls in a separate AppDomain and display them as part of a Form in the main AppDomain.

These UI controls do their own data loading so when I open a form about 10 different plugins get created and each needs to load its data.

Again this all works fine if I do it synchronously but I would like to use the async/await pattern in each plugin. My refresh method looks like this:

protected async void RefreshData()
{ 
    _data = await LoadAsync(_taskId);  <= UI Thread     :)
    OnDataChanged();                   <= Worker Thread :( 
}

Now here starts the problem. When I enter this method I am on the main UI thread. But when the await is over I am on a worker thread so I get a cross thread exception in the OnDataChanged() method which updates the controls.

await should by default use the SynchronizationContext.Current for its continuation but since I am in an other AppDomain this happens to be NULL.

So my question is. How can I configure await to continue on the current thread, that is the UI thread?

I know I can grab a control and do Invoke() but I am also using the MVVM pattern and this is in the View Model so I don't have access to any controls there and all View Model -> View communications are done through data bindings.


Solution

  • I finally figured out how to get back to the UI-Thread from within a separate AppDomain, without having a handle to a control.

    Since my view model is always instantiated on the UI thread, I simply grab the current dispatcher:

    _dispatcher = System.Windows.Threading.Dispatcher.CurrentDispatcher
    

    Now in my RefreshData method all I have to do is dispatch the action I want to perform after the await.

    protected async void RefreshData()
    { 
        _data = await LoadAsync(_taskId);            <= UI Thread :)
        _dispatcher.Invoke(() => OnDataChanged());   <= UI Thread :)
    }
    

    This can of course be made more fancy, by encapsulating the dispatcher, etc.

    The idea for this actually came from the: MVVM Light Toolkit