简体   繁体   English

向简单注入器注册IAuthenticationManager

[英]Register IAuthenticationManager with Simple Injector

I am having a configuration setup for Simple Injector where I have moved all of my registrations to OWIN pipeline. 我正在为Simple Injector进行配置设置,已将所有注册信息移至OWIN管道。

Now the problem is I have a controller AccountController which actually takes parameters as 现在的问题是我有一个控制器AccountController ,它实际上将参数作为

public AccountController(
    AngularAppUserManager userManager, 
    AngularAppSignInManager signinManager, 
    IAuthenticationManager authenticationManager)
{
    this._userManager = userManager;
    this._signInManager = signinManager;
    this._authenticationManager = authenticationManager;
}

Now my Owin Pipeline configurations looks something like this 现在我的Owin Pipeline配置看起来像这样

public void Configure(IAppBuilder app)
{
    _container = new Container();
    ConfigureOwinSecurity(app);
    ConfigureWebApi(app);
    ConfigureSimpleinjector(_container);

    app.Use(async (context, next) =>
    {
        _container.Register<IOwinContext>(() => context);
        await next();
    });

    _container.Register<IAuthenticationManager>(
        () => _container.GetInstance<IOwinContext>().Authentication);

    _container.Register<SignInManager<Users, Guid>, AngularAppSignInManager>();
}

private static void ConfigureOwinSecurity(IAppBuilder app)
{
    app.UseCookieAuthentication(new CookieAuthenticationOptions
    {
        AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
        CookieName = "AppNgCookie",
        //LoginPath = new PathString("/Account/Login")
    });
}

private static void ConfigureWebApi(IAppBuilder app)
{
    HttpConfiguration config = new HttpConfiguration();
    WebApiConfig.Register(config);
    app.UseWebApi(config);
}

private static void ConfigureSimpleinjector(Container container)
{
    SimpleInjectorInitializer.Initialize(container);
}

And Simple Injector Initializer looks something like this 而且简单注入器初始化器看起来像这样

private static void InitializeContainer(Container container)
{
    container.Register<DbContext, AngularAppContext>();

    container.Register<IUserStore<Users, Guid>, AngularAppUserStore>();
    container.Register<IRoleStore<Roles, Guid>, AngularAppRoleStore>();

    container.Register<UserManager<Users, Guid>, AngularAppUserManager>();
    container.Register<RoleManager<Roles, Guid>, AngularAppRoleManager>();
    //container.RegisterPerWebRequest<SignInManager<Users, Guid>, AngularAppSignInManager>();

    container.Register<IdentityFactoryOptions<AngularAppUserManager>, IdentityFactoryOptions<AngularAppUserManager>>();
    //container.Register<IAuthenticationManager>(() => HttpContext.Current.GetOwinContext().Authentication);

    //container.Register<SignInManager<Users, Guid>, AngularAppSignInManager>();
    // For instance:
    // container.Register<IUserRepository, SqlUserRepository>();
}

Now the problem is The controller is not able to register IAuthenticationManager . 现在的问题是,控制器无法注册IAuthenticationManager I tried using 我尝试使用

container.Register<IAuthenticationManager>(
    () => HttpContext.Current.GetOwinContext().Authentication);

But that Leaves me with Exception as: 但这给我留下了如下异常:

System.InvalidOperationException: No owin.Environment item was found in the context. System.InvalidOperationException:在上下文中找不到owin.Environment项目。

In this line 在这条线

container.Register<IAuthenticationManager>(
    () => HttpContext.Current.GetOwinContext().Authentication);

I also tried instead of using HttpContext.Current.GetOwinContext().Authentication with the configuration mentioned above in public void Configure(app) method to register using app.Use() . 我也尝试使用HttpContext.Current.GetOwinContext().Authentication而不是在public void Configure(app)方法中使用上述配置进行HttpContext.Current.GetOwinContext().Authentication ,以使用app.Use()进行注册。 And then later resolve it via container to get the IAuthenticationManager . 然后通过容器解析它,以获取IAuthenticationManager But every possibilities have left me failed. 但是种种可能性使我失败了。

What am I missing here? 我在这里想念什么? Why HttpContext.Current.GetOwinContext().Authentcation is failing to resolve authentcation from OwinContext? 为什么HttpContext.Current.GetOwinContext().Authentcation无法从OwinContext解析身份验证?

And if thats not, why is the same configuration via app.Use also not working? 如果不是这样,为什么通过app.Use使用相同的配置也不起作用?

As TrailMax already mentioned, the exception you got probably got raised during the call to container.Verify() . 正如TrailMax已经提到的那样,在调用container.Verify()可能会引发异常。 At application start-up time there is no HttpContext , hence the exception. 在应用程序启动时,没有HttpContext ,因此是例外。

Although the removal of the call to container.Verify() will 'solve' the problem, I would advise against doing this and I will suggest a better solution below. 尽管删除对container.Verify()的调用将“解决”问题,但我建议不要这样做,并在下面提出更好的解决方案。

NightOwl888 references an old article of Mark Seemann (which I highly respect for his work on DI). NightOwl888引用了马克·西曼(Mark Seemann)的一篇老文章(我非常尊重他在DI上的工作)。 In that article Mark explains why he thinks that verifying the container is useless. Mark在那篇文章中解释了为什么他认为验证容器是无用的。 This article however seems outdated, and conflicts with newer articles from Mark. 但是,本文似乎过时,并且与Mark的较新文章相冲突。 In a newer article Mark explains that one of the big advantages of using Pure DI (that is Dependency Injection without using a DI container) is that it provides the fastest feedback about correctness that you can get . 马克(Mark)在较新的文章中解释说,使用Pure DI (不使用DI容器的依赖注入)的一大优点是, 它提供了关于正确性的最快反馈 Mark, and the rest of us, obviously values both the compiler's feedback and the feedback from static code analysis tools, as rapid feedback mechanism. Mark和我们其他人显然都将编译器的反馈和静态代码分析工具的反馈视为快速反馈机制。 Both Simple Injector's .Verify() and the Diagnostic Services attempt to bring this fast feedback back. Simple Injector的.Verify()Diagnostic Services都试图将这种快速反馈带回来。 In my view, Simple Injector's .Verify() method takes on the job that the compiler would do for you when doing Pure DI and the Diagnostic Services is in a sense static code analysis tool specialized to your DI configuration. 我认为,Simple Injector的.Verify()方法承担了编译器在执行Pure DI时将为您执行的工作,而Diagnostic Services在某种意义上是专用于DI配置的静态代码分析工具。

While it is indeed not possible for a container to do a 100% verification of its configuration, verifying still proved a valuable practice to me. 尽管确实不可能对容器进行100%的配置验证,但对我而言,验证仍然是一种宝贵的实践。 It would be silly to think that a simple call to .Verify() would result in a completely bug free or even a working application. 认为对.Verify()的简单调用会导致完全无错误甚至是正常的应用程序,这是很愚蠢的。 If somebody may think that this is what 'verifying' your DI configuration means, I understand why they would argue that this functionality is worthless. 如果有人可能认为这就是“验证” DI配置的含义,那么我就会理解为什么他们会认为此功能毫无价值。 Sounds like a statement of truism. 听起来像是在说实话。 There is no container out there, including Simple Injector, which pretends having such a feature. 那里没有容器,包括Simple Injector,它伪装成具有这种功能。

You are off course still responsible for writing integration and/or unit tests for eg detecting if the order of applied decorators is correct or if all implementations of ISomeService<T> are indeed registered in the container. 当然,您仍然有责任编写集成和/或单元测试,例如,检测所应用装饰器的顺序是否正确,或者是否确实在容器中注册了ISomeService<T>所有实现。

I want to mention 2 specific arguments from Mark's blog against verifying the container. 我想在Mark的博客中提到2个反对验证容器的特定论点。

It is easy to get into the situation that the container verifies, but still breaks at runtime. 容易进入容器验证的情况,但在运行时仍会中断。

I agree with that, but I do think that the Simple Injector documentation has got some great guidance on how to approach this here . 我同意这一点,但是我确实认为Simple Injector文档已对如何在此处实现此方法提供了一些很好的指导。

When doing convention over configuration it's easy to get registrations that shouldn't be in the container anyway. 在通过配置进行约定时,很容易获得无论如何都不应该在容器中的注册。

I never had this problem, because I think it is a sane practice to prevent getting in this situation anyway. 我从来没有遇到过这个问题,因为我认为无论如何防止这种情况是一种理智的做法。

Back to the question: 回到问题:

Although one of the tips in the Simple Injector documentation is to use abstract factories, I wouldn't do that in this case. 尽管Simple Injector文档中的技巧之一是使用抽象工厂,但在这种情况下我不会这样做。 Creating a factory for something that already exists, sounds pretty weird to me. 为已经存在的东西创建工厂对我来说听起来很奇怪。 Maybe it is just a problem of correct naming, but why would an AccountController need a AuthenticationFactory or AuthenticationContext ? 也许这只是正确命名的问题,但是AccountController为什么需要AuthenticationFactoryAuthenticationContext In other words, why should the application know anything about us having problem wiring things up because of some design quirks in ASP.NET Identity? 换句话说,由于ASP.NET Identity中的某些设计问题,为什么应用程序应该不知道有关我们在接线方面遇到的问题?

Instead, by adjusting the registration for the IAuthenticationManager we can return an Authentication component from a newly created OwinContext at startup/verify time and return the 'normal' or configured AuthenticationManager at runtime. 相反,通过调整IAuthenticationManager的注册,我们可以在启动/验证时从新创建的OwinContext返回Authentication组件,并在运行时返回“正常”或已配置的AuthenticationManager This will remove the need for a factory and moves the responsibility to the Composition Root where it should be. 这将消除对工厂的需求,并将责任移到应有的组成根目录。 And lets you inject the IAuthenticationManager everywhere you need it, while still being able to make a call to .Verify() . 并允许您将IAuthenticationManager注入到需要的任何位置,同时仍然可以调用.Verify()

The code looks like: 代码如下:

container.RegisterPerWebRequest<IAuthenticationManager>(() => 
    AdvancedExtensions.IsVerifying(container) 
        ? new OwinContext(new Dictionary<string, object>()).Authentication 
        : HttpContext.Current.GetOwinContext().Authentication); 

An even more SOLID solution however would be to not depend on the IAuthenticationManager at all, because depending on this interface causes us to violate the Interface Segregation Principle, making it hard to create a proxy implementation for it that delays the creation. 然而,一个甚至更多的SOLID解决方案将是完全不依赖IAuthenticationManager ,因为依赖此接口会导致我们违反接口隔离原则,从而使其难以创建代理实现,从而延迟创建。

You could do this by defining an abstraction which fits your needs and only your needs. 您可以通过定义仅适合您的需求的抽象来实现。 Looking at the identity template calls to the IAuthenticationManager this abstraction would need nothing more than the .SignIn() and .SignOut() methods. 查看对IAuthenticationManager的身份模板调用,此抽象只需要.SignIn().SignOut()方法。 This however would force you to completely refactor the crappy AccountController that you got 'for free' by the Visual Studio template, which can be quite an undertaking. 但是,这将迫使您完全重构由Visual Studio模板“免费”获得的糟糕的AccountController ,这可能是一项艰巨的任务。

What you are doing with IAuthenticationManager registration worked for me with no issues. 您对IAuthenticationManager注册所做的一切对我来说都没有问题。 At some point I was getting the same exception as you were getting, but that was caused by line with 在某个时候,我得到的异常与您得到的相同,但这是由于

container.Verify();

just after the container configuration. 在容器配置之后。 It was trying to create all instances of registered objects, but there was no HttpContext.Current present, hence the exception. 它试图创建注册对象的所有实例,但是不存在HttpContext.Current ,因此是例外。

Are you not getting any instances out of container before any HTTP request is available? 在没有任何HTTP请求可用之前,您是否没有从容器中取出任何实例? If you really need them, then the only way to work around this is use Factory, as suggested by NightOwl888. 如果您确实需要它们,那么解决此问题的唯一方法是使用NightOwl888建议的Factory。 If you don't need container before the HTTP request, then refactor, so it is not use outwith HTTP request. 如果您在HTTP请求之前不需要容器,请进行重构,因此不要与HTTP请求一起使用。

See my answer here . 在这里查看我的答案。

Although you are accessing a different type, the problem is the same. 尽管您访问的是其他类型,但问题是相同的。 You cannot rely on properties of HttpContext during application startup because the application is initialized outside of the user's context. 在应用程序启动期间,您不能依赖HttpContext的属性,因为该应用程序是在用户上下文之外初始化的。 The solution is to make an abstract factory to read the values at runtime rather than at object creation and inject the factory rather than the IAuthenticationManager type into your controller. 解决方案是使抽象工厂在运行时(而不是在对象创建时)读取值,并将工厂而不是IAuthenticationManager类型注入到控制器中。

public class AccountController
{
    private readonly AngularAppUserManager _userManager;
    private readonly AngularAppSignInManager _signInManager;
    private readonly IAuthenticationManagerFactory _authenticationManagerFactory;

    public AccountController(AngularAppUserManager userManager
      , AngularAppSignInManager signinManager
      , IAuthenticationManagerFactory authenticationManagerFactory)
    {
        this._userManager = userManager;
        this._signInManager = signinManager;
        this._authenticationManagerFactory = authenticationManagerFactory;
    }

    private IAuthenticationManager AuthenticationManager
    {
        get { return this._authenticationManagerFactory.Create(); }
    }

    private void DoSomething()
    {
        // Now it is safe to call into HTTP context
        var manager = this.AuthenticationManger;
    }
}

public interface IAuthenticationMangerFactory
{
    IAuthenticationManger Create();
}

public class AuthenticationMangerFactory
{
    public IAuthenticationManger Create()
    {
        HttpContext.Current.GetOwinContext().Authentication;
    }
}

// And register your factory...
container.Register<IAuthenticationManagerFactory, AuthenticationMangerFactory>();

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM