2

After adding Simple Injector to my project to create an instance of my UserAppManager that will exist through the whole lifetime of a session I started to recieve errors:

Error with no parameterless constructor:

An error occurred when trying to create a controller of type 'AuthController'. Make sure that the controller has a parameterless public constructor.

Error with a parameterless constructor:

For the container to be able to create AuthController it should have only one public constructor: it has 2.

I followed a guide (https://simpleinjector.codeplex.com/discussions/564822) to avoid getting the UserManager from the Request.GetOwinContext() and doing it using the constructor but without luck. What am I doing wrong?

Startup:

public class Startup
{
    public void Configuration(IAppBuilder app)
    {
        ...
        app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
        app.UseWebApi(httpConfig);

        DependencyConfig.InjectDependencies(app);
    }
 }

AuthController:

[RoutePrefix("api/auth")]
public class AuthController : ApiController
{
    readonly private AppUserManager _appUserManager;

    public AuthController(AppUserManager appUserManager)
    {
        _appUserManager = appUserManager;
    }
}

DependencyConfig:

public class DependencyConfig
{
    public static void InjectDependencies(IAppBuilder app)
    {
        var container = new Container();
        container.Options.DefaultScopedLifestyle = new WebApiRequestLifestyle();

        container.Register<AppDbContext>(Lifestyle.Scoped);
        container.Register<IUserStore<AppUser>>(() => new UserStore<AppUser>(container.GetInstance<AppDbContext>()), Lifestyle.Scoped);
        container.Register<AppUserManager>(Lifestyle.Scoped);

        // other service injections

        container.RegisterWebApiControllers(GlobalConfiguration.Configuration);
        container.Verify();
        GlobalConfiguration.Configuration.DependencyResolver = new SimpleInjectorWebApiDependencyResolver(container);

        app.CreatePerOwinContext(() => container.GetInstance<AppUserManager>());
    }
}
Steven
  • 166,672
  • 24
  • 332
  • 435
Sam
  • 1,303
  • 3
  • 23
  • 41
  • Your AuthController has 2 constructors. You should remove one of them. – Steven May 06 '16 at 11:55
  • When I have 1 constructor (with injection usermanager) it complains about not having a parameterless constructor. So I add a parameterless constructor under the constructor with injection but than it shows the error about having 2 public constructors when it only should have 1. – Sam May 06 '16 at 12:19
  • 1
    Don't look at the parameterless condtructor message. It is misleading and incorrect. This is a design error in Web API. Always look at the inner exception. That exception is given by Simple Injector giving the real cause. – Steven May 06 '16 at 13:02

1 Answers1

4

Looking at your code:

Startup.cs

app.UseWebApi(httpConfig);

DependencyConfig

container.RegisterWebApiControllers(GlobalConfiguration.Configuration);
container.Verify();
GlobalConfiguration.Configuration.DependencyResolver = new SimpleInjectorWebApiDependencyResolver(container);

Seems clear to me that you are not using the same HttpConfiguration instance for both the Web Api configuration and SimpleInjector registration.

Please note that GlobalConfiguration.Configuration != httpConfig (except if you have manually assigned var httpConfig = GlobalConfiguration.Configuration).

This way your Web API middleware has no knowledge of SimpleInjector, and will use its default implementation. Because of this the creation of your controller will fail.

Please use the same HttpConfiguration for both Web Api and SimpleInjector registration:

public class Startup
{
    public void Configuration(IAppBuilder app)
    {
        //...
        app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
        app.UseWebApi(httpConfig);

        DependencyConfig.InjectDependencies(app, httpConfig);
    }
 }

public class DependencyConfig
{
    public static void InjectDependencies(IAppBuilder app, HttpConfiguration config)
    {
        // ...
        container.RegisterWebApiControllers(config);
        container.Verify();
        config.DependencyResolver = new SimpleInjectorWebApiDependencyResolver(container);
        // ...
    }
}

Also, you need to provide just a single constructor for your controller, with every parameter you want to be injected inside it (remove the parameterless constructor).


Regarding app.CreatePerOwinContext

I would be very careful in using this line:

app.CreatePerOwinContext(() => container.GetInstance<AppUserManager>());

You are requesting an instance of a LifeStyle.Scoped type inside Owin, but you declared the default scope as WebApiRequestLifestyle. This life-style inherits from ExecutionContextScopeLifestyle but the scope itself begins only on the start of a Web Api request, and thus I believe it will not be present in any middleware executed outside of Web API (maybe Steven could help us clarify this matter).

You may consider using WebRequestLifestyle, but be aware of the possible issues with async/await.

Federico Dipuma
  • 17,655
  • 4
  • 39
  • 56
  • 3
    Simple Injector will never resolve a scoped instance for you when there is no active scope; it will throw an exception instead. So if this CreatePerOwinContext doesn't throw, I think it's okay. But using the CreatePerOwinContext basically means that OWIN is managing lifetimes as well. You should prefer having just one tool that manages this, not two. This can lead to a lot of confusion. – Steven May 06 '16 at 13:09
  • It is looking promesing but I need to do some code-refacturing because some providers/services still use the "context.OwinContext.GetUserManager...". After that I will let you know if it worked, thanks! – Sam May 06 '16 at 13:48
  • 4
    @Steven "But using the CreatePerOwinContext basically means that OWIN is managing lifetimes as well". Although the name suggests that Owin will manage this instance, this is not true. Making this call will let Owin store an instance of the `UserManager` in its dictionary. And this is actually critical to do because without an instance of the `UserManager` Owin will be unable to invalidate the security cookie which will keep session open even after a logout has been made! – Ric .Net May 06 '16 at 20:53
  • 4
    See [this article](http://tech.trailmax.info/2014/08/cookieauthenticationprovider-and-user-session-invalidation-on-change-of-securitystamp/) for an in detail explanation. – Ric .Net May 06 '16 at 20:54
  • 2
    Thanks @Ric.Net, you are absolutely right. This is indeed a critical issue. Thanks for commenting this. – Steven May 06 '16 at 21:16
  • app.CreatePerOwinContext(() => contai.. indeed was giving trouble when used inside the ConfigureOAuth method and changing the default scope to WebRequestLifestyle made it work. Thanks for this beautiful explained answer and all the contributing answers. – Sam May 07 '16 at 13:56