【问题标题】:ASP.NET MVC 5 + Owin + SimpleInjectorASP.NET MVC 5 + Owin + SimpleInjector
【发布时间】:2023-03-23 07:15:01
【问题描述】:

如果我从项目中删除 DI 库,则使用 owin、webapi、mvc 和 DI (SimpleInjector) 的新 asp.net mvc 项目运行良好。但是,一旦引入,应用程序在为 DI 注册 OWIN 组件时就会崩溃。 OWIN 启动配置正在被命中并正常运行,但是在注册依赖项时(如下所列)我收到以下错误:

Microsoft.Owin.Host.SystemWeb.dll 中出现“System.InvalidOperationException”类型的异常,但未在用户代码中处理

附加信息:在上下文中找不到 owin.Environment 项。

SimpleInjector 注册码:

container.RegisterPerWebRequest<IUserStore<ApplicationUser>>(() => new UserStore<ApplicationUser>());
container.RegisterPerWebRequest<HttpContextBase>(() => new HttpContextWrapper(HttpContext.Current));
// app fails on call to line below...
container.RegisterPerWebRequest(() => container.GetInstance<HttpContextBase>().GetOwinContext());
container.RegisterPerWebRequest(() => container.GetInstance<IOwinContext>().Authentication);
container.RegisterPerWebRequest<DbContext, ApplicationDbContext>();

更新 - 全栈跟踪

在 System.Web.HttpContextBaseExtensions.GetOwinContext(HttpContextBase 上下文)在 WebApplication1.App_Start.SimpleInjectorInitializer.c__DisplayClass6.b__2() 在 b:\temp\WebApplication1\WebApplication1\App_Start\SimpleInjectorInitializer.cs:line 41 at lambda_method(Closure) at SimpleInjector.Scope.CreateAndCacheInstance[TService,TImplementation](ScopedRegistration2 registration) at SimpleInjector.Scope.GetInstance[TService,TImplementation](ScopedRegistration2 注册)在 SimpleInjector.Scope.GetInstance[TService,TImplementation](ScopedRegistration2 registration, Scope scope) at SimpleInjector.Advanced.Internal.LazyScopedRegistration2.GetInstance(Scope 范围)在 lambda_method(闭包)在 SimpleInjector.InstanceProducer.GetInstance()

【问题讨论】:

  • 你能发布完整的堆栈跟踪吗?
  • 它在container.Verify()这一点上失败了吗?
  • 依赖注册失败。这是行:container.RegisterPerWebRequest(() => container.GetInstance().Authentication);
  • 你见过thisthis吗?
  • 我认为不需要注册 IOwinContext。您在哪里依赖 OwinContext 注入另一个类。如果可能的话,不要直接注入 OwinContext。如果你真的需要它,注入一个工厂类。

标签: c# asp.net-mvc dependency-injection owin simple-injector


【解决方案1】:

我认为当您调用Verify() 时会引发异常。可能在该行,但仅在调用委托时。

Simple Injector 允许以任何顺序进行注册,因此不会验证注册依赖项的存在和正确性。此验证在第一次请求实例时完成,或者可以通过在注册过程结束时调用.Verify() 来触发。

我怀疑您注册OwinContext 只是因为您需要它来获取IAuthenticationManager

您面临的问题是OwinContext 仅在有HttpContext 时可用。在组合根中构建应用程序时,此上下文不可用。您需要的是一个委托,它检查应用程序的阶段并返回与该阶段匹配的组件。您可以通过将IAuthenticationManager 注册为:

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

当代码在“正常运行时阶段”运行并且存在 HttpContext 时,委托将返回 Owin 控制的 IAuthenticationManager

但是,在注册过程结束时,当显式调用Verify()(这非常适合advisable!)时,没有HttpContext。因此,我们将在验证容器的过程中创建一个新的OwinContext,并从这个新创建的OwinContext 返回身份验证组件。但前提是容器确实在验证!

如 cmets 中已经提到的,可以阅读here 的完整详细说明。

【讨论】:

  • 你可能只返回一个new OwinContext();,而不是一个假的FakeAuthenticationManager
  • 不要这么认为,返回类型是 IAuthenticationManager,所以返回一个新的 OwinContext 就不行了。此外,新的 OwinContext 不会有关联的 AuthenticationManager(null),因此我们返回一个空的。
  • 我的意思是我会返回new OwinContext().Autentication,在我的代码中似乎工作正常。但我想我也只是退回一个假的,可以肯定的。
  • @janhartmann,看来您毕竟是对的,它简化了代码。查看我的编辑。
【解决方案2】:

虽然问题不同,但答案与my answer here相同。

问题是您将 HttpContextWrapper 注入到您的应用程序中并尝试在应用程序初始化期间使用其成员,但在应用程序生命周期的那个时刻,HttpContext 尚不可用。 HttpContext 包含运行时状态,在特定用户的上下文中初始化应用程序是没有意义的。

要解决此问题,您应该使用一个或多个Abstract Factories 在运行时(如果可用)而不是在应用程序初始化时访问 HttpContext,并使用 DI 将工厂注入到您的服务中。

使用 Ric .Net 的答案也可能有效,但它会在应用程序初始化时每次抛出异常。

【讨论】:

  • 每次应用初始化时抛出异常有什么问题?初始化只会发生一次。
  • 不正确。每次应用程序池过期时都会初始化应用程序。此外,根据Best Practices for Exceptions,您应该设计类,以便在正常使用中永远不会引发异常。
  • 如果你真的需要 HttpContext 这可能是一个解决方案。但是,如果您仔细研究这个问题,则需要的不是 HttpContext,而是 IAuthenticationManager。我使用了 try catch 语句。您还可以对 HttpContext 进行空检查。但这有它自己的一些问题。
  • @Ric.Net - 无论所需的确切类型如何,抽象工厂都是创建它的好方法。 IAuthenticationManager 可以从在其构造函数中采用 IHttpContextFactory 的工厂返回。
  • 一个经常使用的网站/webservice会每天过期一次,具体取决于你的配置。工厂很快就会变成一种代码味道,所以如果你能避免它们,就不要使用它们。
【解决方案3】:

“Ric .Net”的答案为我指明了正确的方向,但要允许更改新的 SimpleInjector,必须更改以下代码(因为 RegisterPerWebRequest 已过时):

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

另外,必须在容器中添加以下两个注册,以允许 'container.Verify()' 正常工作:

        container.Register<ApplicationUserManager>(Lifestyle.Scoped);
        container.Register<ApplicationSignInManager>(Lifestyle.Scoped);

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-10-18
    • 2015-06-09
    • 2014-01-02
    • 1970-01-01
    • 2016-04-26
    • 2014-03-06
    • 1970-01-01
    • 2016-09-10
    相关资源
    最近更新 更多