This project is read-only.

Scoping and unpredictable behaviour with async/await

Feb 22, 2014 at 1:36 PM
Here's a simple program which demonstrates the issue. It works half the time without any issue, while half the time throwing the exception below.

My hunch is that when the continuation gets executed synchronously by the runtime, it works. But is this excepted behavior?
public class Program
{
    private static void Main(string[] args)
    {
        Task.Factory.StartNew(async () =>
        {
            var container = new Container();
            container.Register(typeof (ITest), () => new Test(), new LifetimeScopeLifestyle());


            var scope = container.BeginLifetimeScope();
            Console.WriteLine(container.GetInstance<ITest>().Message);

            await Task.Delay(1000);

            try
            {
                Console.WriteLine(container.GetInstance<ITest>().Message);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex);
            }

            scope.Dispose();
        }).Wait();

        Console.ReadKey();
    }

    public interface ITest
    {
        string Message { get; set; }
    }

    public class Test : ITest
    {
        public Test(string message = null)
        {
            Message = message ?? "Hello";
        }

        public string Message { get; set; }
    }
}
Exception:
SimpleInjector.ActivationException: The registered delegate for type Program+ITest threw an exception. The ITest is regi
stered as 'LifetimeScope', but the instance is requested outside the context of a lifetime scope. Make sure you call con
tainer.BeginLifetimeScope() first. ---> SimpleInjector.ActivationException: The ITest is registered as 'LifetimeScope',
but the instance is requested outside the context of a lifetime scope. Make sure you call container.BeginLifetimeScope()
first.
Feb 22, 2014 at 6:31 PM
PS: It always throws the exception in Debug Mode. This happens only in Release mode.
Developer
Feb 22, 2014 at 7:59 PM
Edited Feb 22, 2014 at 8:45 PM
Hi prasannavl,

Great to see that you are trying to use Simple Injector with async/await!

The bad news is: The exception you see is by-design because LifetimeScope was created only for a single-threaded use-case. A LifetimeScope is registered as thread local variable - it is tightly bound to a specific thread. This is not compatible with the async/await flow of execution across threads... Additionally LifetimeScope does not like to be disposed on a different thread - that means even if you changed your code-snippet to (successfully) resolve the ITest instance before the Task.Delay() you would most likely (whenever continuation happened on a different thread) get an exception telling you that you called Dispose() from the "wrong" thread...

The good news is Steven (dot_NET_junkie) and I have almost finished the implementation of a new slightly different type of scope called ExecutionContextScope which will be part of the upcoming Simple Injector 2.5 release. In contrast to LifetimeScope the ExecutionContextScope will not be registered in TLS but in the logical execution context (CallContext.LogicalGetData()) and therefore flows with tasks across threads - this means your code sample will work with ExecutionContextScope instead of LifetimeScope. In addition to flowing with async operations the Scope base class will now be completely thread-safe. This is especially important if the logical program flow branches to executes multiple operations in parallel (fork-join scenarios) - this is fully supported when compiling against the >= .Net 4.5.

@dot_NET_junkie: Steven, can we already make a statement about the expected release date of Simple Injector 2.5?

Andreas
Marked as answer by dot_NET_Junkie on 2/26/2014 at 11:23 AM
Coordinator
Feb 22, 2014 at 8:11 PM
I believe I already leaked a possible release date for v2.5 here :-). There's already a 2.5-beta1 available, but unfortunately there's no ExecutionContextScoping package yet in the beta1. I think it's safe to assume we will be able to release a beta2 within a couple of days that will contain this, and the stable release within 7 days after that.
Feb 23, 2014 at 9:55 AM
Ah. Thanks great to hear! Thanks for the detailed reply.

I'm looking forward to ExecutionContextScoping in the final release. Just taking a look at the SimpleServiceLocator branch.