3

I am trying to integrate Simple Injector (4.0.12) in my .NET (4.6.1) Web API Project, but cannot find a way to register all Web API Controllers with the correct AsyncScopedLifestyle.

When I try injecting an async scoped instance of DatabaseProvider into the controller like so...

public class DatabaseController : ApiController
{
    private readonly IDatabaseProvider databaseProvider;

    public DatabaseController(IDatabaseProvider databaseProvider)
    {
        this.databaseProvider = databaseProvider;
    }

    [HttpGet]
    public bool CheckDatabaseConnection()
    {
        return databaseProvider.IsConnected();
    }
}

... I receive a SimpleInjector.ActivationException with the following error:

The DatabaseProvider is registered as 'Async Scoped' lifestyle, but the instance is requested outside the context of an active (Async Scoped) scope.

But why?


This is how my code for registering the the controllers looks like:

public static Container Initialize()
{
     var container = new Container();
     container.Options.LifestyleSelectionBehavior = new CustomLifestyleSelectionBehavior();
     container.Options.DefaultScopedLifestyle = new AsyncScopedLifestyle();
     container.RegisterWebApiControllers(GlobalConfiguration.Configuration);

     DependencyProvider.SetResolver(new SimpleInjectorDependencyProvider(container));
     GlobalConfiguration.Configuration.DependencyResolver = 
         new SimpleInjectorWebApiDependencyResolver(container);
     RegisterTypes(container);

     //container.Verify();

     return container; 
 }

public class CustomLifestyleSelectionBehavior : ILifestyleSelectionBehavior
{
    public Lifestyle SelectLifestyle(Type implementationType)
    {
        if (implementationType.GetCustomAttribute<ApplicationScoped>(true) != null)
        {
            return Lifestyle.Singleton;
        }

        return new AsyncScopedLifestyle();
    }
}

As you can see the DefaultScopedLifestyle is set to AsyncScopedLifestyle and also my CustomLifestyleSelectionBehavior returns the same lifestyle for controllers.

However all controllers seem to be registered as Transient, because this is the output of container.Verify() for all controllers:

Exception Type: DiagnosticVerificationException
Exception Message: The configuration is invalid.
The following diagnostic warnings were reported:

-[Disposable Transient Component] DatabaseController is registered as transient, but implements IDisposable.

-[Disposable Transient Component] LoginDataController is registered as transient, but implements IDisposable.
...

Does anybody know how to set the lifestyle for WebAPIController-registrations to AsyncScoped so that I can inject async scoped business logic?

Steven
  • 166,672
  • 24
  • 332
  • 435
Alwin S
  • 186
  • 1
  • 10
  • Possible duplicate of [Simple Injector Diagnostic Warning Disposable Transient](https://stackoverflow.com/questions/42591234/simple-injector-diagnostic-warning-disposable-transient) – Milen Dec 19 '17 at 10:15
  • It is somehow the same problem, but my question is **how** can I register controllers with a scoped lifestyle? – Alwin S Dec 19 '17 at 10:18
  • There are a two things I don't understand. 1: Why do you require your controllers to be Scoped? 2. How on earth did you manage to get this "Disposable Transient Component" diagnostic error on your controllers? You should not get this warning, because it is [suppressed](https://github.com/simpleinjector/SimpleInjector/blob/master/src/SimpleInjector.Integration.WebApi/SimpleInjectorWebApiExtensions.cs#L111), because Web API will dispose controllers for you. So what are you doing differently that causes the resurrection of these warnings? – Steven Dec 19 '17 at 10:33
  • Alwin, I tried to reproduce your issue using VS 2017, for .NET 4.6.1, using the default "ASP.NET Web Application (.NET Framework)" template for Web API, after which I added the [Simple Injector Web API WebHost QuickStart 4.0.12](https://www.nuget.org/packages/SimpleInjector.Integration.WebApi.WebHost.QuickStart) NuGet package and copy-pasted your code into the project. Using that setup and the code you provided, there are no diagnostic warnings, so it's unclear to me what's going on. You might try isolating this issue. Could it be that 'something' is overriding the controller registrations? – Steven Dec 19 '17 at 12:34
  • @Steven thx for your reply – Alwin S Dec 19 '17 at 17:27
  • 1. I wanted to make all controllers Scoped, only because I thought this would solve the exception when injecting async scoped instances into the controller. 2. You are right, there should be no diagnostic error! With your hints I found the cause of all the problems: I had updated the Simple Injector NuGet package from 3.1.2 to 4.0.12 in my project but my local assembly was still the old version for some reason. I have cleaned up my assemblies and now everything works fine, no diagnostic error and no exception any more :) – Alwin S Dec 19 '17 at 18:43
  • @Steven on a side note: thx for correcting my code, but the parameter of the method SelectLifestyle(Type xx) is not garanteed to be the implementation type. I noticed that it gets passed the serviceType when using Register with a instanceCreator function. Am I right? – Alwin S Dec 19 '17 at 18:50
  • 2
    That's correct. Simple Injector sends it the most specific type it _knows_ of. When you call `Register(Func)` all it knows is `T`, not what `Func` might return. That would force runtime analysis on _every_ call, which could cause a _severe_ performance penalty. But in general, the type _is_ a implemenetation type, since Simple Injector should in almost all cases be in control of the creation of the type. – Steven Dec 19 '17 at 18:53

1 Answers1

0

In .NET Core add:

// Sets up the basic configuration that for integrating Simple Injector with
// ASP.NET Core by setting the DefaultScopedLifestyle, and setting up auto
// cross wiring.
services.AddSimpleInjector(_container, options =>
{
    // AddAspNetCore() wraps web requests in a Simple Injector scope and
    // allows request-scoped framework services to be resolved.
    options.AddAspNetCore()
        .AddControllerActivation();
});

via https://simpleinjector.readthedocs.io/en/latest/aspnetintegration.html

Saibamen
  • 610
  • 3
  • 17
  • 43