2012-01-20 130 views
13

我正在向現有的WebForms應用程序(使用Castle Windsor)引入依賴注入框架。Webforms和依賴注入

我對DI有很深的經驗,並且傾向於強烈支持構造函數注入而非setter注入。如果您熟悉Webforms,則知道ASP.Net框架處理頁面和控件對象的構建,從而無法真正構建注入。

我目前的解決方案是將容器註冊到Global.asax的Application_Start事件中,並將容器作爲公共靜態變量保存在Global中。然後,我只需在頁面中直接解決我需要的每個服務,或者在需要時直接解決這些服務。因此,在每個頁面的頂部,我結束了這樣的代碼:

private readonly IMyService _exposureManager = Global.IoC.Resolve<IMyService>(); 
private readonly IMyOtherService _tenCustomersExposureManager = Global.IoC.Resolve<IMyOtherService>(); 

顯然,我不喜歡散佈我的應用程序或爲我的網頁/控制依賴是不向容器中的所有這些引用 - 明確的,但我一直沒有找到更好的方法。

是否有更優雅的解決方案使用DI與Webforms?

+0

我發現這篇文章http://www.codemag.com/Article/1210031它具有優秀的示例代碼,以便如何在WPF,MVC和WebForms上實現依賴注入(對於WebForms,使用'PageHandlerFactory') 。似乎認爲,由於代碼隱藏類限制,您將不得不使用setter注入。有趣的是,它還顯示了Microsoft Managed Extensibility Framework(MEF)如何以非常有用且稍微不標準的方式幫助您解決這個類似的DI問題。 –

回答

16

我同意@DarinDimitrov MVP是一個有趣的選項。但是,在處理遺留應用程序時,將現有頁面重寫爲MVP模式是一件難事。在這種情況下,您可能會更好地從Service Locator模式開始(但在您的UI類中只有只有),因爲您已經在使用該模式。但是,改變一件事。不要將選定的DI容器暴露給應用程序,因爲我期望您正在使用Global.IoC屬性。

而是在Global類上創建靜態Resolve<T>方法。這完全隱藏了容器,並允許您交換實現,而無需更改網頁中的任何內容。當你這樣做時,@Wiktor提出的使用通用服務定位器沒有任何優勢。通用服務定位器只是抽象的抽象對象,因爲您已經使用Global.Resolve<T>抽象出了容器。

不幸的是,對於Web表單,沒有任何好的方法可以做到這一點。對於Simple Injector,我寫了一個integration guide for Web Forms,基本上描述了使用Global.Resolve<T>方法,但也顯示了一種測試是否可以創建Page類的方法。該指南也可用於其他DI容器。

順便說一句,請記住,溫莎城堡,你要求的一切都必須明確發佈(Register Resolve Release pattern)。這有點令人討厭(IMO),並且與其他容器的工作方式有所不同,如果不正確執行此操作,可能會導致內存泄漏。

最後一張紙條。 It is possible to do constructor injection with Web Forms。嗯...有點,因爲這將調用Form已使用默認構造函數創建後使用反射的重載構造函數,所以這會導致Temporal Coupling

+0

感謝您的回答。最後一篇文章很有趣,如果不是完全信任的問題,確實會引起很大興趣。 –

+0

@PhilSandler:似乎微軟已經[完全放棄了ASP.NET 4.0的部分信任](https://stackoverflow.com/questions/16849801/is-trying-to-develop-for-medium-trust-a-lost-原因)和超越。 – Steven

+0

如果'Resolve '方法是靜態的,則它無法訪問實例成員容器。不過我可能會誤解你。你是否建議使容器靜態? –

4

是否有一個更優雅的解決方案使用DI與Webforms?

Yeap,MVP pattern允許您在WebForms應用程序中清晰分離關注點。一旦你關注和弱耦合分離,DI很容易。

而在內置的ASP.NET MVC中。

2

ASP.NET MVC有IDependencyResolverstatic manager class,讓您獲取和設置解析器。我不喜歡在Web窗體項目引用System.Web.Mvc的想法,所以我去IServiceLocator,該做的是同一件事:

public static class Bootstrapper 
{ 
    private static readonly IUnityContainer _container = new UnityContainer(); 

    public static void Initialize() 
    { 
     ServiceLocator.SetLocatorProvider(() => new UnityServiceLocator(_container)); 

     _container.RegisterType<IDriverService, DriverService>(); 
    } 

    public static void TearDown() 
    { 
     _container.Dispose(); 
    } 
} 

public class Global : HttpApplication 
{ 
    protected void Application_Start(object sender, EventArgs e) 
    { 
     Bootstrapper.Initialize(); 
    } 

    protected void Application_End(object sender, EventArgs e) 
    { 
     Bootstrapper.TearDown(); 
    } 
} 
在你的頁面類

然後...

IDriverService service = ServiceLocator.Current.GetInstance<IDriverService>(); 

或通過構造函數注入連線DI。我還沒有用網頁的形式走下這條路,所以其他人需要爲我填補:)(我一直住在MVC的土地上大約一年)。

我的例子使用Unity,但你應該能夠很容易地適應任何其他的DI實現。

+0

在應用程序結束時執行「TearDown」真的很有用嗎?無論如何,應用程序域正在被卸載。只有當你在其Dispose方法中註冊了具有拆卸邏輯的單例服務時纔有用,但它仍然是脆弱的,因爲不能保證所有的Dispose方法在卸載AppDomain時實際運行。 – Steven

+0

好問題。我只是試圖按照一次性模式。我不確定除了應用程序結束之外,還有哪些地方可以提供。 – jrummell

1

由於@DarinDimitrov說MVP模式是爲了在Webforms中使用DI/IOC而走的路。

要麼你可以推出自己的實現或使用現有的框架。我聽說Webforms MVP好,但我沒有真正使用它。

根據the docs,它已經通過Castle Windsor,Autofac和Unity建立了對DI的支持。它還爲演示者提供基於約定的自動發現。