dependency injection选择

我正在寻找dependency injection,我可以看到它的好处,但我遇到了它创建的语法问题。 我有这个例子

public class BusinessProducts { IDataContext _dx; BusinessProducts(IDataContext dx) { _dx = dx; } public List GetProducts() { return dx.GetProducts(); } } 

问题是我不想写

 BusinessProducts bp = new BusinessProducts(dataContextImplementation); 

我会继续写

 BusinessProducts bp = new BusinessProducts(); 

因为我觉得第一种选择只是感觉不自然。 我不想知道BusinessProduct“依赖”什么来获取产品,我也觉得它使我的代码更难以理解。

是否有任何替代方法,因为我希望保留我创建对象的原始语法,但我希望仍然能够在unit testing时伪造依赖关系,或者这个dependency injection框架可以为我做什么?

我在c#编码,但欢迎其他语言的替代品

我使用工厂作为我的上下文并注入它,如果提供的工厂为null,则提供合适的默认值。 我这样做有两个原因。 首先,我使用数据上下文作为工作范围对象的单元,因此我需要能够在需要时创建它们,而不是保持一个。 其次,我主要使用DI来提高可测性,而仅仅考虑次要因素。

所以我的商业产品类看起来像:

 public class BusinessProducts { private IDataContextFactory DataContextFactory { get; set; } // my interface public BusinessProducts() : this(null) {} public BusinessProducts( IDataContextFactory factory ) { this.DataContext = factory ?? new BusinessProductsDataContextFactory(); } public void DoSomething() { using (DataContext dc = this.DataContextFactory().CreateDataContext()) { ... } } 

另一种方法是使工厂属性可公开设置,并通过设置属性注入备用工厂。 无论哪种方式,如果你想保留null构造函数,你需要提供一个默认值。

你可以创建一个工厂。 DI容器最适合在设置时发生的布线 – 而不是在运行时(因为这看起来是这样)。 工厂可以以不同的方式实施,具体取决于它需要的可插拔性,以及需要使用它的地方数量。

我通常会有一个空构造函数,它使用一个实体实例(或由IoC创建的实例),并使用DI。 即

 public class BusinessProducts { IDataContext _dx; BusinessProducts() { _dx = new SolidDataContext(); } BusinessProducts(IDataContext dx) { _dx = dx; } } 

这样,您可以使用DI来覆盖unit testing测试中的默认实例。

你的感受虽然有效,却是错位的。

dependency injection模式是控制反转原理的直接应用。

这意味着,不是您的类控制它消耗的其他类的实例,而是反转该关系并向其提供依赖关系。

因此,您的类通过构造函数参数或属性自然地公开它们的依赖项。 对这种结构表示不屑,说你还没有真正理解这种模式。

这里有两个不同的案例:

在生产代码中,您永远不会

 new BusinessProducts(dataContextImplementation) 

因为dependency injection通常会为您创建完整的对象层次结构。 这是dependency injection模式的“病毒”性质,它们倾向于完全控制您的服务创建。

在unit testing代码中,您通常会自己创建它,但通常您将提供模拟对象或dataContextImplementation的存根实现。 因此,通常您将注入一个没有大量后续依赖项的对象。

通常,框架本身将具有构建整个对象树的逻辑。 例如,而不是

 new SomeObjectO(diContext) 

你会像这样调用框架:

 DIFramework.GetNew(); 

要么

 DIFramework.Get(); 

另一个有趣的框架,看看你是否想了解DI和过程是微软的Unity和Object Builder项目。

如果您真的不喜欢在构造函数中注入此实例,则可以尝试将CommonServiceLocator与您最喜欢的兼容.NETdependency injection框架一起使用。 这将允许您编写如下代码:

 public class BusinessProducts { IDataContext _dx; BusinessProducts() { _dx = Microsoft.Practices.ServiceLocation.ServiceLocator.Current.GetInstance(); } public List GetProducts() { return dx.GetProducts(); } } 

但是,请注意,当他们知道您使用dependency injection框架时,这并不是大多数人所期望的。 我认为使用dependency injection框架并让它为您创建所有对象更为常见。

 BusinessProducts bp = Microsoft.Practices.ServiceLocation.ServiceLocator.Current.GetInstance(); 

如果您想避免dependency injection框架路径,使用工厂可能是最好的方法。

有一种叫做穷人的DI的技术看起来像这样

 public class BusinessProducts { IDataContext _dx; BusinessProducts() : this(new DataContext()) {} BusinessProducts(IDataContext dx) { _dx = dx; } public List GetProducts() { return dx.GetProducts(); } } 

这并不理想,因为它将您与实现联系起来,但它是解耦代码的良好基础。 这类似于@tvanfosson,但更简单。

我是温莎的推荐

我的代码将引用Microsoft Unity,但我确信它非常适用于所有DI框架。 如果你正确使用DI,你永远不需要调用新的BusinessObject(新的dataContext),DI协会将为你处理所有这些。

我的例子有点长,因为我将粘贴一些代码,用于运行完全由DI加载的Model View Presenter网站。 (如果您想要完整的源代码检查我的博客并从我的Assembla SVN服务器下载它)

加载容器(可以是我喜欢的代码或使用配置)

 protected void Application_Start(object sender, EventArgs e) { Application.GetContainer() // presenters / controllers are per request .RegisterType(new ContextLifetimeManager()) //Data Providers are Per session .RegisterType(new SessionLifetimeManager()) //Session Factory is life time .RegisterType(new ContainerControlledLifetimeManager()); } 

自定义HTTP模块在OnPreRequest调用期间为每个页面调用Unity BuildUp Method。

 private static void OnPreRequestHandlerExecute(object sender, EventArgs e) { var handler = HttpContext.Current.Handler; HttpContext.Current.Application.GetContainer().BuildUp(handler.GetType(), handler); // User Controls are ready to be built up after the page initialization is complete var page = HttpContext.Current.Handler as Page; if (page != null) { page.InitComplete += OnPageInitComplete; } } 

使用[Dependency]属性修饰的页面容器展示器

 public partial class Employees : Page, IEmployeeView { private EmployeePresenter _presenter; [Dependency] public EmployeePresenter Presenter { set { _presenter = value; _presenter.View = this; } } } 

Presenter with InjectionConstructor方法

 public class EmployeePresenter : Presenter { private readonly IEmployeeController _controller; [InjectionConstructor] } public EmployeePresenter(IEmployeeController controller) { _controller = controller; } 

控制器也是如此

 public class EmployeeController : IEmployeeController { private readonly IEmployeeDataProvider _provider; [InjectionConstructor] public EmployeeController(IEmployeeDataProvider DataProvider) { _provider = DataProvider; } } 

与提供者相同

 public class EmployeeController : IEmployeeController { private readonly IEmployeeDataProvider _provider; [InjectionConstructor] public EmployeeController(IEmployeeDataProvider DataProvider) { _provider = DataProvider; } } 

最后是会话管理器,它只包含一个常规构造函数。

 public class NHibernateSessionManager : INHibernateSessionManager { private readonly ISessionFactory _sessionFactory; public NHibernateSessionManager() { _sessionFactory = GetSessionFactory(); } } 

那么当页面请求启动时会发生什么,HttpModule会在页面上调用BuildUp()方法。 然后,Unity会看到标有Dependency属性的Property,并检查它的容器以查看它内部是否存在EmployeePresenter对象。

由于容器中没有这样的对象,因此它将尝试创建EmployeePresenter。 在检查创建它在Presenter中看到的类时,它需要一个需要注入IEmployeeController的构造函数。 由于容器实际上有一个控制器管理器,因此它将查看容器的实例是否存在于页面请求开始时不存在的容器中,因此它将实例化控制器。

然后Unity会看到控制器需要注入一个IEmployeeDataProvider,它将继续执行此过程,直到它最终到达Provider需要注入会话管理器的点。 由于会话管理器不再需要注入,因此Unity将创建会话管理器的实例,将其存储在容器中,以便为其提供ContainerLifeTimeManager,将其注入Provider并存储该实例,依此类推,直至创建完成的位置。 EmployeePresenter对页面的依赖性。

你也可以看看温莎的IoC。