UserManager dependency injection with Owin and Sim

2019-04-14 06:22发布

问题:

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>());
    }
}

回答1:

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.