3

I read these (+ , + , + and +) pages, but I cannot figure out what should I do.

I have this simple interface and concrete type:

public interface IIdentifierGenerator {
    long Generate(Type type);
    long Generate<TType>(TType type);
}
public HiloIdentifierGenerator : IIdentifierGenerator { /* implementation... */ }

I create this DependencyResolver:

public class SelfHostedSimpleInjectorWebApiDependencyResolver : IDependencyResolver {
    private readonly Container _container;
    private readonly LifetimeScope _lifetimeScope;

    public SelfHostedSimpleInjectorWebApiDependencyResolver(
        Container container)
        : this(container, false) {
    }

    private SelfHostedSimpleInjectorWebApiDependencyResolver(
        Container container, bool createScope) {
        _container = container;

        if (createScope)
            _lifetimeScope = container.BeginLifetimeScope();
    }

    public IDependencyScope BeginScope() {
        return new SelfHostedSimpleInjectorWebApiDependencyResolver(
            _container, true);
    }

    public object GetService(Type serviceType) {
        return ((IServiceProvider)_container).GetService(serviceType);
    }

    public IEnumerable<object> GetServices(Type serviceType) {
        return _container.GetAllInstances(serviceType);
    }

    public void Dispose() {
        if (_lifetimeScope != null)
            _lifetimeScope.Dispose();
    }
}

and I configured my server like this:

_config = new HttpSelfHostConfiguration("http://192.168.1.100:20000");
_config.Routes.MapHttpRoute(
    "API Default", "api/{controller}/{id}",
    new { id = RouteParameter.Optional });
_config.DependencyResolver =
    new SelfHostedSimpleInjectorWebApiDependencyResolver(
        IoC.Wrapper.GetService<Container>());
_server = new HttpSelfHostServer(_config);
/* etc. */

And this is my controller:

public class IdentifierController : ApiController {

    private readonly IIdentifierGenerator _identifierGenerator;

    public IdentifierController(IIdentifierGenerator identifierGenerator) {
        _identifierGenerator = identifierGenerator;
    }

    public long Get(string id) {
        var type = Type.GetType(id, false, true);
        return type == null ? -1 : _identifierGenerator.GetIdentifier(type);
    }
}

Now, when I call the action method, I get this error:

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 28, but was created on thread with id 29.

Where am I doing wrong? Can you help please?

Community
  • 1
  • 1
amiry jd
  • 27,021
  • 30
  • 116
  • 215

1 Answers1

2

The Lifetime Scope lifestyle in Simple Injector is designed to work on a single thread. Running it over multiple threads (even calling Dispose) is not thread-safe. Web API self-hosting however disposes the created scope on a different thread and could even schedule the controller to be run on a different thread than where the lifetime scope is created. Since this is not safe, Simple Injector throws an exception in this case.

As I explained in this discussion, I don't know of a good solution at the Web API level, but when moving to a message based architecture, the problem goes away completely, because you can inject an ICommandHandler<TCommand> in the controller and decorate that controller with a decorator that adds Lifetime scope behavior to the command handler. This way you should replace your SelfHostedSimpleInjectorWebApiDependencyResolver with a simpler resolver that does not create a lifetime scope. I explained it in the discussion and referred other SO answers and blog post. I know you already read this, since you already referred to the SO answer, but this is the best I can give you.

Steven
  • 166,672
  • 24
  • 332
  • 435
  • Hey dear @Steven. Thanks to answer. But I think it is not a good solution to move every thing to a message based architecture, some times it's not just possible. However, I created some simple implementations of `IDependencyResolver` and `IDependencyScope` [here](https://gist.github.com/kavand/5610025). Would you take a look at them and say what is wrong with them? I test theme, it seems they work. But, as I am really a new Web API coder, I cannot be sure that my work is correct. I'm waiting for your opinion. Thanks a lot for simple injector. It's lovely. – amiry jd May 20 '13 at 02:18
  • @Javad_Amiry: Your implementation will not work. Do not create a Lifetime scope for the lifetime of the application (globally) as you do now in the `SiWebApiDependencyResolver` ctor. Do not manually dispose objects when disposing the `SiWebApiDependencyScope`. Lifetime scope already does this and you might be calling Dispose many times on the same object. Don't implement a finalizer on classes unless they require unmanaged resources. – Steven May 20 '13 at 09:30
  • If a message based architecture is not suited for you and using IIS hosted Web API is not an option, using Simple Injector is probably not the best strategy for you at the moment. – Steven May 20 '13 at 09:33
  • Yes, after a while, I also came to a similar conclusion as you. In this project I just cannot use Simple Injector. Also my implementation has too many problems :D However, thanks to your advise. Regards. – amiry jd May 20 '13 at 11:32