Error with Self-Hosted Web API

Jan 23, 2013 at 3:03 PM
Edited Jan 23, 2013 at 3:06 PM

HI,

I'm getting the following exception when using Simple Injector in a Self-Hosted Web API project:

"It is not safe to use a LifetimeScope instance across threads. Make sure the complete operation that the lifetime scope surrounds gets executed within the same thread and make sure that the LifetimeScope instance gets disposed on the same thread as it gets created. Dispose was called on thread with ManagedThreadId 13, but was created on thread with id 12."

 

This happens when I call a action of a Controller. I'm using the IDependencyResolver from this link: http://simpleinjector.codeplex.com/wikipage?title=Self%20Hosted%20Web%20API%20Integration

My configuration:

 

 

            var config = new HttpSelfHostConfiguration("http://localhost:8686");

            // Add a route
            config.Routes.MapHttpRoute(
                "API Default",
                "{controller}/{id}",
                new { id = RouteParameter.Optional });

            var container = new Container();

            container.RegisterLifetimeScope<IRequestNode<SmsMoRequest, SmsMoResponse>, SmsMoRequestNode>();
            config.Formatters.Insert(0, new TangramXmlMediaFormatter());
            config.DependencyResolver = new SelfHostedSimpleInjectorWebApiDependencyResolver(container);

            _server = new HttpSelfHostServer(config);     
            _server.OpenAsync().Wait();            

 

My controller requires a IRequestNode<SmsMoRequest, SmsMoResponse> instance in the constructor.

 

.NET 4

SimpleInjector 1.6.0.12319

SimpleInjector.Extensions.LifetimeScoping 1.6.1.0

 

Anyone is having a similar issue?

Coordinator
Jan 23, 2013 at 3:26 PM

Your Controller is probably doing work asynchronously. This is a scenario the Lifetime Scope is not designed for. Make sure the controllers do all their work synchronously (this would be as easy as not returning Task<T> from any action method and not calling those methods XXXAsync).

Jan 23, 2013 at 3:57 PM

Actually I'm not using async actions on my controller:

 

    public class MoMessagesController : ApiController
    {
        private IRequestNode<SmsMoRequest, SmsMoResponse> _smsMoRequestNode;

        public MoMessagesController(IRequestNode<SmsMoRequest, SmsMoResponse> smsMoRequestNode)
        {
            _smsMoRequestNode = smsMoRequestNode;
        }

        [HttpPost]        
        public SmsMoResponse ReceiveMo(SmsMoRequest smsMoRequest)
        {
            return _smsMoRequestNode.StoreRequest(smsMoRequest);
        }
    }

 

The problem happens always after the controller have processed the request. The problem occurs even if  I make a HTTP GET to the controller root address (there's no GET action defined)...

Coordinator
Jan 24, 2013 at 12:37 PM
Edited Jan 24, 2013 at 12:41 PM

I've looked in the source code of the HttpSelfHostServer class and did some googling, but I can't find out why the self-hosted server is finishing the controllers on a different thread and how to prevent this. The lifetime scope is thread-specific and exception is thrown to indicate that there might be a problem. I say 'might', because disposing the scope on a different thread would be safe. Typically however, what would happen is that half of the request is moved to a different thread. If at that point the container is queried for a Lifetime-Scoped type that was already requested during that request, a new instance would be returned, sine the scope runs on a different thread. Or even more painfully, a cached instance is returned that belongs to a different thread. Both are not good things. The design of the LifetimeScope doesn't allow detecting such invalid calls halfway the request. It can only detect whether it started at the same request as where it was disposed (which would normally be a pretty good indication).

So to be honest, I haven't a good solution for this at a Web API level, but....

Your design has much resemblance with the command/handler design and query/handler designs described here and here. These designs allow you to add cross-cutting concerns to handlers (your IRequestNode<TRequest, TResult> interface) with decorators. Simple Injector has great support for decorators.

So what you might be able to do is use a decorator that sits between your controller and request node (and implements IRequestNode) that will start a new transaction scope for you. Such decorator might look as follows:

public sealed class LifetimeRequestNodeProxy<TRequest, TResponse>
    : IRequestNode<TRequest, TResponse>
{
    // Since this type is part of the composition root,
    // we are allowed to inject the container into it.
    private Container container;
    private Func<IRequestNode<TRequest, TResponse>> decorateeCreator;

    public LifetimeRequestNodeProxy(
         Container container,
         Func<IRequestNode<TRequest, TResponse>> decorateeCreator)
    {
        // Simple Injector understands the Func<T> delegate
        // and will automatically inject a delegate for the
        // creation of decorated instance.

this.decorateeCreator = decorateeCreator; this.container = container; } public TResponse StoreRequest(TRequest request) { using (this.container.BeginLifetimeScope()) { // must be called inside the lifetime scope var requestNode = this.decorateeCreator(); return requestNode.StoreRequest(command); } } }

and it can be registered as follows:

// it can be a singleton because it depends on Func<IRequestNode<TRequest, TResponse>>
container.RegisterSingleDecorator(typeof(IRequestNode<,>), typeof(LifetimeRequestNodeProxy<,>));

Simple Injector will now automatically wrap and requested IRequestNode<TRequest, TResult> instance with a LifetimeRequestNodeProxy<TRequest, TResult>.

This SO question/answer talks more about this. This might be of interest to you.

The use of this decorator allows you to keep the service layer (web api in your case) to stay unaffected when applying these types of changes.

Marked as answer by dot_NET_Junkie on 11/4/2013 at 2:05 AM
Jan 24, 2013 at 2:27 PM

Nice, thanks for the answer! I like this aproach, something like aspects-oriented programming. I'll try here!