[英]MVC5 Ninject binding and HttpContext
我正在嘗試建立一個新項目,並且我添加了一個新類MembershipService ,它需要在它的構造函數中傳遞HttpContext 。
在之前的項目中,我使用了代碼
private static void RegisterServices(IKernel kernel)
{
kernel.Bind<IMembershipService>()
.To<MembershipService>()
.InRequestScope()
.WithConstructorArgument("context", HttpContext.Current);
....
}
然而,在新項目中,我正在使用Ninject Modules,在對StackOverflow和Google進行一些搜索之后,我提出了以下代碼:public class ServiceHandlerModule:NinjectModule {
public override void Load()
{
Bind<IMembershipService>()
.To<MembershipService>()
.WithConstructorArgument("context", ninjectContext=> HttpContext.Current);
this.Kernel.Bind(x =>
{
x.FromAssemblyContaining(typeof(NinjectWebCommon))
.SelectAllClasses()
.Where(t => t != typeof(MembershipService))
.BindDefaultInterface();
});
this.Kernel.Bind(x =>
{
x.FromAssemblyContaining<BrandServiceHandler>()
.SelectAllClasses()
.Where(t => t != typeof(MembershipService))
.BindDefaultInterface();
});
}
}
但是,我收到以下錯誤:
描述:執行當前Web請求期間發生未處理的異常。 請查看堆棧跟蹤以獲取有關錯誤及其源自代碼的位置的更多信息。
異常詳細信息:Ninject.ActivationException:激活字符串時出錯沒有匹配的綁定可用,並且該類型不可自我綁定。 激活路徑:
5)將依賴字符串注入到HttpRequest類型的構造函數的參數filename中
4)將依賴關系HttpRequest注入到HttpContext類型的構造函數的參數請求中
3)將依賴關系HttpContext注入到MembershipService類型的構造函數的參數httpContext中
2)將依賴關系IMembershipService注入到HomeController類型的構造函數的參數membership service中
1)請求HomeController
誰能指出我哪里出錯了?
謝謝,約翰
Steven認為HttpContext
是一個運行時值。 在編寫應用程序時,它的值甚至不會填充。
如果您考慮它,這是有道理的,因為應該在任何單個用戶上下文之外初始化應用程序。
但是,Steven的解決方案只將問題轉移到了另一項服務上。 畢竟,實現IUserContext
的類仍然需要將HttpContext
作為依賴項。
解決方案是使用抽象工廠來允許在運行時訪問HttpContext
實例,而不是在工廠連接時訪問。
重要說明: HttpContext不是抽象,因此無法交換或模擬。 為了確保我們處理抽象,Microsoft提供了HttpContextBase抽象類和默認的具體類型HttpContextWrapper。 HttpContextBase與HttpContext具有完全相同的接口。 您應始終使用HttpContextBase作為服務中的抽象引用類型,而不是HttpContext。
考慮到這兩件事,您可以為HttpContext
創建一個工廠,如下所示:
public interface IHttpContextFactory
{
HttpContextBase Create();
}
public class HttpContextFactory
: IHttpContextFactory
{
public HttpContextBase Create()
{
return new HttpContextWrapper(HttpContext.Current);
}
}
然后可以修改您的MembershipService
以在其構造函數中接受IHttpContextFactory
:
public class MembershipService : IMembershipService
{
private readonly IHttpContextFactory httpContextFactory;
// This is called at application startup, but note that it
// does nothing except get our service(s) ready for runtime.
// It does not actually use the service.
public MembershipService(IHttpContextFactory httpContextFactory)
{
if (httpContextFactory == null)
throw new ArgumentNullException("httpContextFactory");
this.httpContextFactory = httpContextFactory;
}
// Make sure this is not called from any service constructor
// that is called at application startup.
public void DoSomething()
{
HttpContextBase httpContext = this.httpContextFactory.Create();
// Do something with HttpContext (at runtime)
}
}
而且你只需要在組合時注入HttpContextFactory
。
kernel.Bind<IHttpContextFactory>()
.To<HttpContextFactory>();
kernel.Bind<IMembershipService>()
.To<MembershipService>();
但是,僅此一點可能無法解決整個問題。 您需要確保應用程序的其余部分在准備好之前不嘗試使用HttpContext
。 就DI而言,這意味着您不能在任何由應用程序啟動組成的類型的構造函數或其中一個構造函數調用的服務成員中使用HttpContext
。 要解決這個問題,您可能需要創建其他抽象工廠,以確保在HttpContext
准備好之前,這些服務不會調用IMembershipService
成員。
Steven的解決方案還需要圍繞HttpContext
創建Facade 。 雖然這並沒有真正幫助解決手頭的問題,但我同意如果您的MembershipService
(可能還有其他服務)只使用少量的HttpContext
成員,這可能是一個好主意。 通常,此模式是使復雜對象更易於使用(例如將其展平為可能嵌套在其層次結構內的少數成員)。 但是你真的需要權衡添加另一種類型的額外維護與在應用程序中使用HttpContext
的復雜性(或交換其中一部分的值)來做出決定。
我添加了一個新類MembershipService,它需要在它的構造函數中傳遞HttpContext。
這是你出錯的地方。 HttpContext是一個運行時值,但您的對象圖應該只包含編譯時或配置時依賴性。 任何其他的,運行時值,應該通過方法調用傳遞,或者應該作為注入的服務的屬性公開。
不遵循本指南,將使撰寫和測試對象圖變得更加困難。 測試組合根是一個很好的例子,因為在測試框架內運行時HttpContext.Current
不可用。
因此,請阻止此MembershipService
對HttpContext
采用構造函數依賴項。 相反,注入一個將HttpContext
公開為屬性的服務,因為這允許您在對象圖是構造函數之后請求此上下文。
但也許更好的是將HttpContext
隱藏在特定於應用程序的抽象背后。 HttpContext
不是一個抽象; 它是一個龐大而丑陋的API,使您的代碼更難以測試,更難以理解。 相反,創建非常狹窄/專注的界面,例如這樣的界面:
public interface IUserContext
{
User CurrentUser { get; }
}
現在,您的MembershipService
可以依賴於通過屬性公開User
對象的IUserContext
。 現在,您可以在調用CurrentUser
屬性時創建在內部使用HttpContext.Current
的AspNetUserContext
實現。 這樣可以生成更清晰,更易維護的代碼。
這是一個可能的實現:
public class AspNetUserContext : IUserContext
{
public User CurrentUser
{
// Do not inject HttpContext in the ctor, but use it
// here in this property
get { return new User(HttpContext.Current.User); }
}
}
我同意史蒂文,但你也可以:
kernel.Bind<HttpContext>().ToMethod(c => HttpContext.Current);
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.