[英]Why should I use IoC Container (Autofac, Ninject, Unity etc) for Dependency Injection in ASP.Net Applications?
[英]C# ASP.NET Dependency Injection with IoC Container Complications
很抱歉,我對此表示歉意,但我知道有一些答案,但是我進行了很多搜索,但沒有找到正確的解決方案,因此請耐心等待。
我正在嘗試為遺留應用程序創建一個框架,以在ASP.NET Web表單中使用DI。 我可能會使用溫莎城堡作為框架。
這些遺留應用程序將在某些地方部分使用MVP模式。
演示者將如下所示:
class Presenter1
{
public Presenter1(IView1 view,
IRepository<User> userRepository)
{
}
}
現在,ASP.NET頁面將如下所示:
public partial class MyPage1 : System.Web.UI.Page, IView1
{
private Presenter1 _presenter;
}
在使用DI之前,我將在頁面的OnInit中實例化Presenter:
protected override void OnInit(EventArgs e)
{
base.OnInit(e);
_presenter = new Presenter1(this, new UserRepository(new SqlDataContext()));
}
所以現在我要使用DI。
首先,我必須創建一個處理程序工廠以覆蓋頁面的構造。 我發現這確實是一個很好的答案,可以幫助您: 如何在ASP.NET Web窗體中使用依賴注入
現在,正如Mark Seeman建議使用Global.asax一樣,我可以在我的合成根目錄中輕松地設置容器(這意味着盡管創建了一個靜態容器,該容器必須是線程安全的並且密封的,才能添加更多的注冊信息)
現在我可以在頁面上聲明構造函數注入
public MyPage1() : base()
{
}
public MyPage1(Presenter1 presenter) : this()
{
this._presenter = presenter;
}
現在我們遇到第一個問題,我有一個循環依賴性。 Presenter1取決於IView1,但是頁面取決於Presenter。
我知道現在有些人會說,當您具有循環依賴項時,該設計可能不正確。 首先,我不認為Presenter設計是不正確的,因為它將構造函數中的依賴關系帶給了View,我可以通過查看大量MVP實現來說明這一點。
有些人可能建議將Page更改為Presenter1成為屬性的設計,然后使用屬性注入
public partial class MyPage1 : System.Web.UI.Page, IView1
{
[Dependency]
public Presenter1 Presenter
{
get; set;
}
}
有些人甚至建議完全刪除對演示者的依賴,然后通過一系列事件簡單地進行布線。但這不是我想要的設計,坦白地說,我不明白為什么我需要進行此更改以適應它。
無論如何建議,仍然存在另一個問題:
當處理程序工廠獲得頁面請求時,只有一種類型可用(沒有視圖接口):
Type pageType = page.GetType().BaseType;
現在使用此類型,您可以通過IoC及其依賴項來解析Page:
container.Resolve(pageType)
然后,它將知道存在一個名為Presenter1的屬性並能夠注入它。 但是Presenter1需要IView1,但我們從未通過容器解析IView1,因此容器將不知道提供處理程序工廠剛創建的具體實例,因為它是在容器外部創建的。
因此,我們需要修改處理程序工廠並替換視圖接口:處理程序工廠解析頁面的位置:
private void InjectDependencies(object page)
{
Type pageType = page.GetType().BaseType;
// hack
foreach (var intf in pageType.GetInterfaces())
{
if (typeof(IView).IsAssignableFrom(intf))
{
_container.Bind(intf, () => page);
}
}
// injectDependencies to page...
}
這帶來了另一個問題,大多數城堡(如Castle Windsor)將不允許您將此接口重新注冊到它現在指向的實例。 同樣,在Global.asax中注冊了容器的情況下,這樣做也不是線程安全的,因為此時只能讀取該容器。
另一個解決方案是創建一個函數,以在每個Web請求上重建容器,然后檢查容器(如果未設置實例)是否包含組件IView。 但這似乎很浪費,並且與建議的用法背道而馳。
另一種解決方案是創建一個名為IPresenterFactory的特殊工廠,並將依賴項放入頁面構造函數中:
public MyPage1(IPresenter1Factory factory) : this()
{
this._presenter = factory.Create(this);
}
問題是您現在需要為每個演示者創建一個工廠,然后調用該容器以解決其他依賴項:
class Presenter1Factory : IPresenter1Factory
{
public Presenter1Factory(Container container)
{
this._container = container;
}
public Presenter1 Create(IView1 view)
{
return new Presenter1(view, _container.Resolve<IUserRepository>,...)
}
}
這種設計似乎也很繁瑣,是否有人對更優雅的解決方案有想法?
也許我誤解了您的問題,但是解決方案對我來說似乎很簡單:將IView
提升為Presenter1
上的一個屬性:
class Presenter1
{
public Presenter1(IRepository<User> userRepository)
{
}
public IView1 View { get; set; }
}
這樣,您可以像這樣在視圖上設置演示者:
public Presenter1 Presenter { get; set; }
public MyPage1()
{
ObjectFactory.BuildUp(this);
this.Presenter.View = this;
}
或不使用屬性注入,可以按照以下步驟進行操作:
private Presenter1 _presenter;
public MyPage1()
{
this._presenter = ObjectFactory.Resolve<Presenter1>();
this._presenter.View = this;
}
Page
類和用戶控件中的構造方法注入將永遠無法真正起作用。 您可以使它在完全信任下工作( 如本文所示 ),但是在部分信任下它將失敗。 因此,您必須為此調用容器。
所有DI容器都是線程安全的,只要您沒有在初始化階段之后自己手動添加注冊,甚至不使用線程安全的一些容器 ( 某些容器甚至在初始化后禁止注冊類型)。 永遠不需要這樣做(大多數容器支持的未注冊類型解析除外)。 但是,對於Castle,您需要預先注冊所有具體類型,這意味着在解決它之前,它需要了解Presenter1
。 注冊,更改此行為或移至默認情況下允許解析具體類型的容器。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.