Microsoft NLayerApp案例理论与实践 - 基础结构层(Cross-Cutting部分)

Microsoft NLayerApp案例理论与实践 - 基础结构层(Cross-Cutting部分) NLayerApp中IoC容器的实现在应用程序设计的过程中我们会基于这样一个设计准则就是类型之间的关联应该依赖于接口或者抽象而非具体的实现。这样就使得我们能够在保证整个程序结构不变的情况下很方便地替换组件的具体实现方式这不仅使得Service Stub模式的应用成为可能从而提高了系统的可测试性而且解耦了组件之间的依赖关系降低了应用程序的维护成本。IoC容器是这样一种对象它在应用程序的执行环境中维护着接口与其实现之间的映射关系以及各个实现对象之间的依赖关系以便当客户程序向IoC容器提出请求时能够返回与所请求的接口或抽象类型所对应的具体实现客户程序不需要去关心返回的具体实现究竟是什么以及如何去初始化这个具体实现。本文不会对IoC作过多的介绍有兴趣的朋友可以阅读《Inversion of Control Containers and the Dependency Injection pattern》这篇文章。NLayerApp中IoC容器的实现依赖于Microsoft PatternsPractices Unity其实大多数应用程序甚至是开发框架都会依赖于第三方的类库来实现IoC容器因为IoC本身涉及的内容就比较多很好地解决类型之间复杂的依赖关系也不是一件很容易的事情。Unity并非IoC的唯一选择除了Unity之外Spring.NET、Castle Windsor、Ninject、StructureMap等都可以成为IoC容器不错的选择。NLayerApp中与IoC容器实现有关的类及其之间的关系如下图所示在上图中IContainer接口定义了IoC容器相关的方法它是与具体的实现技术无关的接口接口的层次结构树、其中定义的方法的参数以及返回值等都不会依赖于任何第三方的组件因此理论上我们可以通过继承IContainer接口然后用我们自己的技术方式来实现IoC容器。NLayerApp是使用Unity作为IoC容器的因此上图的IoCUnityContainer类实现了IContainer接口然后在IoCFactory的单件实例中通过new关键字创建了IoCUnityContainer的实例12345678910#region Constructor/// summary/// Only for singleton pattern, remove before field init IL anotation/// /summarystaticIoCFactory() { }IoCFactory(){_CurrentContainer newIoCUnityContainer();}#endregion当然对于NLayerApp这一特定的应用程序案例而言这样做是没什么问题的但如果我们目前设计的是一个开发框架的话直接使用new关键字来创建IoCUnityContainer的实例就会使得IoCFactory强行依赖于IoCUnityContainer类型于是也就违背了“关联应该依赖于接口或者抽象而非具体实现”的设计准则。在最新版的Apworks框架的代码中开发人员可以通过应用程序的配置信息来选择合适的IoC容器比如你可以在应用程序启动的时候就决定是使用Unity还是Castle Windsor这就使得框架本身具有更好的扩展性。刚才我们也讨论过如果要使NLayerApp能够使用我们自定义的IoC容器就要继承IContainer接口那么现在我们还需要修改IoCFactory的私有构造函数以使用我们自己的IoC容器来初始化_CurrentContainer私有成员。Unity容器有一个非常实用的特点就是“根容器”与“子容器”的概念在“根容器”上通过调用CreateChildContainer方法即可创建与之关联的子容器。根容器和子容器都可以接受抽象类型的注册。每当客户程序向子容器请求类型Resolve Type时Unity首先检查子容器中是否有所请求的类型如果有则直接返回该类型的具体实现如果没有则会将该请求转发给其父容器。利用Unity的这种特性我们可以将针对不同部署环境的IoC容器进行统一管理比如将各种部署环境中相同的类型映射注册在根容器中然后为每个部署环境创建一个子容器将与部署环境相关的特定类型映射注册在各自的子容器中。下图展示了NLayerApp中Unity IoC容器的基本情况通过阅读IoCUnityContainer的源代码我们可以了解到在IoCUnityContainer的构造函数中创建了rootContainer并在rootContainer上使用CreateChildContainer创建了用于真实运行环境的realAppContainer以及用于单体测试的fakeAppContainer之后就是使用下面的私有方法逐个初始化这些容器12345678910111213141516171819202122232425262728293031/// summary/// Configure root container.Register types and life time managers for unity builder process/// /summary/// param namecontainerContainer to configure/paramvoidConfigureRootContainer(IUnityContainer container){// Omitted... Please refer to the source code for details.}/// summary/// Configure real container. Register types and life time managers for unity builder process/// /summary/// param namecontainerContainer to configure/paramvoidConfigureRealContainer(IUnityContainer container){container.RegisterTypeIMainModuleUnitOfWork, MainModuleUnitOfWork(newPerExecutionContextLifetimeManager(),newInjectionConstructor());}/// summary/// Configure fake container.Register types and life time managers for unity builder process/// /summary/// param namecontainerContainer to configure/paramvoidConfigureFakeContainer(IUnityContainer container){//Note: Generic register type method cannot be used here,//MainModuleFakeContext cannot have implicit conversion to IMainModuleContextcontainer.RegisterType(typeof(IMainModuleUnitOfWork),typeof(FakeMainModuleUnitOfWork),newPerExecutionContextLifetimeManager());}在ConfigureRootContainer方法中对所有环境真实运行环境以及单体测试环境需要用到的类型进行了注册然后就IMainModuleUnitOfWork而言由于真实运行环境和单体测试环境所使用的Unit Of Work具体实现不同真实运行环境使用的是MainModuleUnitOfWork实现而测试环境则是使用的FakeMainModuleUnitOfWork于是也就在ConfigureRealContainer和ConfigureFakeContainer方法中分别作了注册。最后每当IContainer.Resolve方法被调用时系统会通过读取配置文件来决定目前应该使用哪个容器来解析类型因此我们只需要在配置文件中正确设置容器的名称即可在NLayerApp中使用指定的Unity IoC容器。下面这段配置信息来自于DistributedServices.Deployment项目从中我们可以看到NLayerApp的Distributed Services使用的是realAppContainer123456appSettings!--RealAppContext - Real Container--!--FakeAppContext - Fake Container--!--add keydefaultIoCContainer valueFakeAppContext /--addkeydefaultIoCContainer valueRealAppContext //appSettingsNLayerApp中用于跟踪程序执行过程的Trace工具NLayerApp中Trace工具的实现非常简单在Infrastructure.CrossCutting项目中定义了ITraceManager然后在Infrastructure.CrossCutting.NetFramework项目中定义了ITraceManager的具体实现。TraceManager使用了System.Diagnostics命名空间下与Trace相关的类型实现其功能应用程序则通过IoCFactory来获得ITraceManager的具体实现。在上面讨论的ConfigureRootContainer方法中NLayerApp对ITraceManager类型进行了注册12//Register crosscuting mappingscontainer.RegisterTypeITraceManager, TraceManager(newTransientLifetimeManager());因此在整个应用程序中就可以使用下面的方式来获取ITraceManager的具体实现以便完成Trace功能12ITraceManager traceManager IoCFactory.Instance.CurrentContainer.ResolveITraceManager();traceManager.TraceError(/* error message*/);