2015-01-12 112 views
2

如果我從項目中刪除DI lib,使用owin,webapi,mvc和DI(SimpleInjector)的新asp.net mvc項目運行良好。但是,一旦推出,在註冊DI的OWIN組件時應用程序就會崩潰。該OWIN啓動配置正在熱播無誤運行,但是當談到時間來註冊依賴關係(見下表)我收到以下錯誤:ASP.NET MVC 5 + Owin + SimpleInjector

An exception of type 'System.InvalidOperationException' occurred in Microsoft.Owin.Host.SystemWeb.dll but was not handled in user code

Additional information: No owin.Environment item was found in the context.

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

更新 - 完整的堆棧跟蹤

at System.Web.HttpContextBaseExtensions.GetOwinContext(HttpContextBase context) at WebApplication1.App_Start.SimpleInjectorInitializer.<>c__DisplayClass6.b__2() in b:\temp\WebApplication1\WebApplication1\App_Start\SimpleInjectorInitializer.cs:line 41 at lambda_method(Closure) at SimpleInjector.Scope.CreateAndCacheInstance[TService,TImplementation](ScopedRegistration 2 registration) at SimpleInjector.Scope.GetInstance[TService,TImplementation](ScopedRegistration 2 registration) at SimpleInjector.Scope.GetInstance[TService,TImplementation](ScopedRegistration 2 registration, Scope scope) at SimpleInjector.Advanced.Internal.LazyScopedRegistration 2.GetInstance(Scope scope) at lambda_method(Closure) at SimpleInjector.InstanceProducer.GetInstance()

+0

您可以發佈完整的堆棧跟蹤嗎? – Steven

+1

是否在'container.Verify()'點失敗? – qujck

+0

註冊依賴關係時失敗。這是行:container.RegisterPerWebRequest(()=> container.GetInstance ()。Authentication); – bbqchickenrobot

回答

14

我認爲當你調用Verify()拋出異常。可能在那條線上,但只有當代表被調用時。

簡單注射器允許以任何順序進行註冊,因此不會驗證註冊依賴項的存在和正確性。該驗證是在第一次請求實例時完成的,或者可以通過在註冊過程結束時調用.Verify()來觸發。

我懷疑你註冊OwinContext只是因爲你需要它來獲得IAuthenticationManager

您面臨的問題是OwinContext僅在存在HttpContext時可用。此應用程序在構建根目錄中生成時不可用。您需要的是一個委託,它檢查應用程序的階段並返回與此階段相匹配的組件。你可以通過註冊IAuthenticationManager爲:

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

代表將返回Owin控制IAuthenticationManager當代碼爲「正常運行階段」運行,並有一個HttpContext

但是,在註冊過程結束時明確呼叫Verify()(這是高度advisable做!!)時沒有HttpContext。因此,我們將在驗證容器期間創建新的OwinContext,並從此新創建的OwinContext中返回認證組件。但只有當容器確實正在驗證!

如已在評論中提到的那樣,可以讀取完整和詳細的描述here

+0

不是假的'FakeAuthenticationManager',你可能只是返回一個'new OwinContext();' – janhartmann

+0

不要這麼認爲,返回類型是IAuthenticationManager,所以返回一個新的OwinContext贏得'做這項工作。此外,一個新的OwinContext不會有一個AuthenticationManager關聯(null),因此我們返回一個空的。 –

+0

我的意思是我會返回'新的OwinContext()。Autentication',似乎在我的代碼中工作正常。但是我想我也只是回覆一個假的。 – janhartmann

0

雖然問題不同,但答案與my answer here相同。

問題是,您正在將HttpContextWrapper注入到應用程序中,並嘗試在應用程序初始化期間使用其成員,但在應用程序生命週期中的此時,HttpContext尚不可用。 HttpContext包含運行時狀態,並且在一個特定用戶的上下文中初始化應用程序沒有意義。

要解決此問題,您應該使用一個或多個Abstract Factories在運行時(可用時)訪問HttpContext,而不是在應用程序初始化時,並使用DI將工廠注入到服務中。

使用Ric .Net的答案也可能起作用,但它會在初始化應用程序時每隔發生一個例外

+2

每次應用程序初始化時拋出異常的問題是什麼?初始化只會發生一次。 – Steven

+0

不正確。每當應用程序池過期時,應用程序都會被初始化。此外,根據[例外最佳實踐](http://msdn.microsoft.com/en-us/library/seyhszts%28v=vs.110%29.aspx),您應該*設計類,以便發生異常從未拋出正常使用。* – NightOwl888

+0

如果你確實需要HttpContext,這可能是一個解決方案。但是如果你仔細看看這個問題,那麼需要的不是HttpContext,而是IAuthenticationManager。我使用了一個try catch語句。你也可以對HttpContext進行空檢查。但是這有它自己的一些問題。 –

0

「裏克淨」的答案指出我在正確的方向,但允許更改新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);