将DI实践应用于遗留应用程序系统

时间:2012-02-09 16:13:47

标签: .net dependency-injection unity-container

我们的客户拥有一个庞大的遗留系统,正在从.NET 1.1升级到.NET 4.0。与此同时,将基础框架移植到WCSF 2010 for Web applications,以及Enterprise Library 5.0 ou1作为基础企业框架。 DI设施一起提供这些框架。

WCSF有效地迫使MVP和模块设计依赖于ObjectBuilder及其属性,因此Web应用程序升级遵循该模式。

Web应用程序在应用层(通过WCF)上消耗额外的应用程序服务。由于底层框架已成为企业库,Unity app块已“免费下载”。但是现有的编码风格仍然是传统的,因此与网络层不同,它没有从根本上切换到DI。

应用层的典型图层是

  • 商业门面(BF) - 公开公共粗粮业务功能,控制交易。
  • 业务组件(BC) - 由facade方法协调的粒度逻辑组件,用于执行事务中的实际工作。
  • 数据访问(DA) - 业务组件的读/写数据

为什么我上面提到的样式仍然是遗留的,因为层之间的服务通信是通过在网络上传递 DataSets (并且它们很大)来实现的。此DataSet将传递到每个对象的构造函数中。例如。伪造的代码

BF.SomeExposedMethod(StrongTypeDS ds)
{
    this.BeginTransaction();
    BizComp bc = new BizComp(ds);
    bc.ShareTransaction();
    bc.DoWork();
    this.CommitTransaction();
}

BC.DoWork()
{
    DataAccess da = new DataAccess(this.transaction, this.ds);
    // perform work
    da.SaveWork(this.ds);
}

这些类的构造函数还基于其父抽象类的样式;他们希望在实例化时接收DataSet。

我一直在阅读Mark Seeman的 DI in .NET 一书和其他各种有关DI问题的公共互联网资料。我似乎从所有这些讨论中理解的是,DI看起来很适合准备好干净对象的图表,以便做好工作。干净我的意思是没有上下文数据。这些遗留代码模式的构造函数从一开始就期望工作数据和上下文。

如果我正确理解了Composition Root的原理,那么必须重新设计构造函数和方法,以便通过属性传递上下文DataSet。我不清楚DI容器(在这种情况下是Unity)如何能够自己找出要注入的DataSet。确定它不能是一个空的全新DataSet。在这个开发阶段(我没有直接参与项目但是从侧面支持),我将无法建议对WCF实现进行根本性更改,以便在实例化Facade对象之前使其成为组合根。

此外,我还没有收集有关DI如何应用于基于运行时条件实例化对象的重要建议?基于数据的执行状态,可以实例化附加对象,并且类型可以基于数据类别而不同。在这个应用程序中引入某种级别的DIP和DI练习似乎是最简单的方法是将Unity容器用作服务定位器;根据需要仅在执行点抓取一个实例。

更新回答

根据Mark Seeman的建议,我制作了以下POC

使用Unity容器的BF。虽然我已经成功地尝试了Unity.Wcf包,但我还为每个BF创建了下一个最接近的组合根,其中每个facade方法将调用容器来解析执行方法工作所需的对象图。 (考虑到这种情况,这种环境可能会更加现实。)

static ExampleBF()
{
    container = new UnityContainer().LoadConfiguration();
}

BF获得一个已解析的IBCFactory,它将实例化一个具有传入的实时上下文数据的具体BC。

DataSet IExampleService.GetProducts(DataSet ds)
{
    IExampleBC bc = container.Resolve<IBCFactory>().CreateBC(ds);
    // GetProducts() takes no parameter because DataSet already passed in on construction.
    return bc.GetProducts();
}

BCFactory通过构造函数注入接收IDACFactory。它将它交给每个实例化的BC。

public BCFactory(IDACFactory DACFactory)
{
    this.DACFactory = DACFactory;
}

IExampleBC IBCFactory.CreateBC(DataSet ds)
{
    return new ExampleBC(this.DACFactory, ds);
}

BC将依靠IDACFactory为其提供DAC。

DataSet IExampleBC.GetProducts()
{
    IExampleDAC dac = this.dacFactory.CreateDAC(this.ds);
    return dac.GetProducts();
}

DACFactory类似地根据上下文数据实例化DAC。

IExampleDAC IDACFactory.CreateDAC(DataSet ds)
{
    return new OrderDAC(ds);
}

当然,代码库的复杂性情况的实际情况在程度上要大一些,但是这个简单的例子应该足以向他们展示DI概念。

1 个答案:

答案 0 :(得分:3)

据我了解相关类的描述,它们听起来更像是数据载体(实体,如果你愿意),而不是服务。如果是这样,那就是not the responsibility of the Composition Root to compose data objects

但是,如果必须根据运行时条件解析服务,请Abstract Factory is the universal solution

由于您正在使用遗留代码库,我想推荐一本书Working Effectively with Legacy Code,它为如何解耦紧密耦合的遗留代码库提供了很多有价值的指导。

相关问题