سؤال

The below code:

    [RequiresSTA]
    [Test]
    public async Task TestSta()
    {
        Console.WriteLine(Thread.CurrentThread.ManagedThreadId+" - "+Thread.CurrentThread.GetApartmentState());
        // *** await something here ***
        Console.WriteLine(Thread.CurrentThread.ManagedThreadId+" - "+Thread.CurrentThread.GetApartmentState());
        new FrameworkElement();
    }

Yields the following output:

9 - STA

12 - MTA

And then, throws an InvalidOperationException on new FrameworkElement().

NUnit supports STA thread creation, now supports async tests, but it seems that it doesnt mix both modes by creating a MTA SynchronizationContext.

How do I get this working? Any workaround ?

هل كانت مفيدة؟

المحلول

You can use the AsyncContext from my AsyncEx library, which was originally written to support async unit tests before the unit test libraries supported them.

[RequiresSTA]
[Test]
public Task TestSta()
{
  AsyncContext.Run(async () =>
  {
    Console.WriteLine(Thread.CurrentThread.ManagedThreadId+" - "+Thread.CurrentThread.GetApartmentState());
    // *** await something here ***
    Console.WriteLine(Thread.CurrentThread.ManagedThreadId+" - "+Thread.CurrentThread.GetApartmentState());
    new FrameworkElement();
  });
}

نصائح أخرى

I managed to fix this. However, I'm sure that a post on stackoverflow would have avoided me quite a headache :) see my answer below.

A really good article about SynchronizationContext (click here) just gave me the code (and the knowledge) I required.

However, I had to tweak it a bit to avoid a deadlock on StaSynchronizationContext disposal, and to propagate the synchronization context inside the worker thread.

My test now looks like below:

    [Test]
    [RequiresSTA]
    public async Task DoSomeUITest()
    {
        using (new StaSynchronizationContext())
        {
            Console.WriteLine(Thread.CurrentThread.ManagedThreadId + " - " + Thread.CurrentThread.GetApartmentState());
            // *** await something here ***
            Console.WriteLine(Thread.CurrentThread.ManagedThreadId + " - " + Thread.CurrentThread.GetApartmentState());
            new FrameworkElement();
        }
    }

Now outputs:

9 - STA

12 - STA

... problem solved !

Download tweaked code here

* edit and disclaimer * Before having awaited something, your code will be running on the STA thread created by NUnit. (thread 9) After the first await, code will be running on the thread created by StaSynchronizationContext (thread 12): even if they're both STA they are not the same thread

Beware instanciating controls before await, and using them after. Just needs a bit more tweaking to switch directly to main thread (we could imagine a "using(await StaSynchronizationContext.Create())" which would make us switch on thread 12 at begining)

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top