jameskind
jameskind

Reputation: 1107

Correctly set Scope when using WebApi, SimpleInjector and MediatR

Controller

public class LocationsController : ApiController
{
    private readonly IMediator _mediator;

    public LocationsController(IMediator mediator)
    {
        _mediator = mediator;
    }

    public IEnumerable<Place> Get()
    {
        return _mediator.Send(new GetLatestMapData<Place>());
    }
}

On first request of Get() action, the Handler is instantiated by SimpleInjector and executed correctly.

On the second request (F5 in browser for e.g.), it fails with :

Handler was not found for request of type ....

Container or service locator not configured properly or handlers not registered with your container.

and inner exception of:

Cannot access a disposed object.

Object name: 'The ThreadLocal object has been disposed.'

OWIN Startup

public class Startup
{
    public void Configuration(IAppBuilder app)
    {
        // SimpleInjector
        var container = CompositionRoot.CreateContainer();

        var config = GlobalConfiguration.Configuration;

        config.DependencyResolver = new SimpleInjectorWebApiDependencyResolver(container);

        // Routing
        config.MapHttpAttributeRoutes();

        config.Routes.MapHttpRoute("DefaultApi", "api/{controller}/{id}",
            new { id = RouteParameter.Optional });

        config.EnsureInitialized();

        app.UseWebApi(config);
    }
}

SimpleInjector IPackage for WebAPI project

public class Installer : IPackage
{
    public void RegisterServices(Container c)
    {
        c.Options.DefaultScopedLifestyle = new WebApiRequestLifestyle();

        c.RegisterWebApiControllers(GlobalConfiguration.Configuration);
    }
}

I think what's happening is the Handler is correctly created, and then disposed after the first request. Now, I don't know why, but on subsequent requests, the Handler isn't re-created. I know this because if I change the WebApiRequestLifestyle to 'not dispose when scope ends', it works for every request:

c.Options.DefaultScopedLifestyle = new WebApiRequestLifestyle(false
/*disposeInstanceWhenScopeEnds*/);

Questions

  1. Should I keep the disposeInstanceWhenScopeEnds parameter set to false?
  2. If not, what is the correct solution?
  3. I see this has been solved before by creating a LifetimeScopeDecorator... however, surely this functionality is already provided by the SimpleInjector WebApi integration library? What am I missing?

(And thank you for reading)

Upvotes: 0

Views: 1621

Answers (2)

Sercan Timo&#231;in
Sercan Timo&#231;in

Reputation: 658

You need to arrange your Lifetime scoped

Code:

container.Options.DefaultScopedLifestyle = new WebApiRequestLifestyle();

container.Options.LifestyleSelectionBehavior = new WebApiInjectionLifestyle();


internal class WebApiInjectionLifestyle : ILifestyleSelectionBehavior
{
    public Lifestyle SelectLifestyle(Type serviceType, Type implementationType)
    {
        return Lifestyle.Scoped;
    }
}

More Detail

https://simpleinjector.readthedocs.io/en/latest/lifetimes.html

Upvotes: 0

Nabster
Nabster

Reputation: 93

This link provides good guidance on dependency resolution and using the IDependencyResolver / IDependencyScope Interfaces.

Immediately you will see that they touch on life spans which tend to get a little tricky.

This section in particular is interesting:

Dependenecy Scope and Controller Lifetime

Controllers are created per request. To manage object lifetimes, IDependencyResolver uses the concept of a scope.

The dependency resolver attached to the HttpConfiguration object has global scope. When Web API creates a controller, it calls BeginScope. This method returns an IDependencyScope that represents a child scope.

Web API then calls GetService on the child scope to create the controller. When request is complete, Web API calls Dispose on the child scope. Use the Dispose method to dispose of the controller’s dependencies.

Conventionally bootstrapping a service would occur once during the app start-up and as you know resolve any dependencies at that time. Only when the worker process was shutting down (no activity, for example) would this then invoke dispose.

Ordinarily I think it is quite normal for resolved instances to remain for the life cycle unless it is imperative that they are destroyed after use. But the example given explains that we must correctly dispose once the request is complete. So I would recommend that you dispose of your instances correctly using the examples provided as guidance.

This helped me when working with IoC and WebApi. I hope this helps!

Upvotes: 0

Related Questions