2012-01-20 131 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的支持。它还为演示者提供基于约定的自动发现。