Prism 文档 第三章 管理组件之间的依赖关系

                                                                      第3章:管理组件之间的依赖关系

基于Prism库的复合应用程序可能包含许多松耦合的类型和服务。他们需要提供内容和接收基于用户行为的通知。因为他们是松散耦合的,他们需要一种方式进行互动和相互沟通来提供所需的业务功能。

为了集中这些不同的块,基于Prism库的应用程序依赖于依赖注入容器。依赖注入容器通过提供设施去实例化类的实例和管理他们基于容器配置的寿命来减少对象之间的依赖耦合。在对象的创建过程中,容器注入对象需要的任何依赖。如果那些依赖尚未创建,容器首先创建和解析他们的依赖关系。在某些情况下,容器本身是作为一个依赖被解析。例如,使用Unity Application Block(Unity)作为容器,容器注入模块,所以他们可以在容器中注册他们的View和服务。

下面是使用容器的一些有利之处:

  1. 容器移除对组件的需要以致可以找到它的依赖或管理他们的寿命。
  2. 容器在不影响组建的情况下,允许实现依赖的交换。
  3. 容器允许模拟依赖以致有利于可测性。
  4. 容器允许新的组件被容易地添加到系统中以致增加可维护性。

在基于Prism库的应用程序中,容器有特定的优势:

  1. 当模块被加载时,容器予其注入模块依赖。
  2. 容器被用于注册和解析View models 和 views
  3. 容器可以穿件view models 和 注入 view。
  4. 容器注入组件服务,比如说区域管理和事件集合
  5. 容器用于注册具有特定模块功能的特定模块服务,

          NotePrism指导中的一些样品依靠Unity Application BlockUnity)作为容器。其他代码案例,例如Modularity QuickStarts,使用Managed Extensibility Framework   (MEF)。Prism库本身不是特定的容器,你可以在其他容器中使用它的服务与模式,如Castle WindsorStructureMapSpring.NET

关键的抉择:选择一个依赖注入容器

Prism库为依赖注入容器提供了两个选择:Unity或MEF。Prism是可扩展的,从而允许其他容器被用来代替一点点工作。Unity和MEF都为依赖注入提供相同的基本功能,尽管他们非常不同。由两个容器提供的一些功能包括以下:

  1. 它们两个都在容器中注册类型。
  2. 它们两个都在容器中注册实例。
  3. 它们两个都需要创建注册类型的实例。
  4. 它们都注入注册型实例到构造函数。
  5. 它们都注入注册型实例到属性。
  6. 它们都有为标记类型和需要管理的依赖关系声明属性。
  7. 它们都解析一个对象图的依赖关系

Unity具有的而MEF所不具有的一些功能:

  1. 它解析没有注册的具体类型。
  2. 它解析开放泛型。
  3. 它采用侦听去捕获调用对象并且向目标对象添加额外的功能。

MEF具有的而Unity所不具有的一些功能:

  1. 它在一个目录中发现组件。
  2. 它使用XAP文件下载和组件发现。
  3. 它将属性和集合作为一种新类型的发现。
  4. 它自动导出派生类型。
  5. 它被部署在.NET Framework下。

容器间的能力是不同的,工作也是不同的,但Prism库配合任何容器的工作并且提供类似的功能。当考虑使用哪种容器,记住前面提到的能力,确定适合您的更好的方案。

考虑使用容器

在使用容器之前,你应该考虑下列所述:

  1. 考虑使用容器的时候是否它适合用来注册和解析组件。

(1) 考虑在容器中注册和用容器解析实例对性能的影响是否可以接受。

(2) 如果有许多或深层次的依赖,创造的成本会显著增加。

(3) 如果组件没有任何依赖或不依赖其他类型,没。

(4) 如果组件有单一的对型积分和永远不变的一套依赖关系,必要把它放在容器里。

2. 考虑一个组件的寿命是否应注册为一个单例或实例

(1) 如果组件是一个全局的作为一个单一资源的资源管理器的服务,如日志服务,你可能想将它注册成为单例。

(2) 如果组件向多个消费者提供共享状态,你可能想将它注册成为单例。

(3) 如果每次正在被注入的对象需要有一个它注入过的新实例,也就是依赖对象所需要的那个,那么将其注册为非单例。例如,每个视图可能需要一个视图模型的一个新实例。

3. 考虑你是否想通过代码或配置去配置容器:

(1) 如果你想集中管理所有不同的服务,那就通过配置来配置容器。

(2) 如果你想有条件的注册特定的服务,那就通过代码配置容器。

(3) 如果你具有模块级的服务,考虑通过代码来配置容器,以至于那些服务只有在被加载的时候才会被注册

Note:一些容器,比如MEF,不能通过配置文件来配置,必须通过代码来配置。

 

核心方案

容器被用于两个主要目的----注册和解析

注册

在你将依赖项注入到一个对象之前,依赖关系的类型必须在容器中注册。注册类型通常是通过容器接口和具体实现该接口的类型。主要有两种类型和对象注册的方式:通过代码或通过配置。具体的根据容器的不同而不同。

典型的,在容器中有两种方式通过代码注册类型与对象:

1.你可以用容器注册一个类型或映射,在适当的时候,容器将建立你指定类型的一个实例。

2.在容器中,你可以注册一个现有的对象实例作为一个单例,容器将返回该对象的引用。

Unity容器注册类型

在初始化过程中,一个类型可以被注册为其他类型,比如视图和服务。注册允许他们的依赖性属性通过容器被提供,并且允许他们被其他类型访问。为了达到这个,类型需要有一个容器注入到模块构造函数。下列代码展示在Commanding QuickStart中的OrderModule类型注册了一个类型。

1 public class OrderModule : IModule
2 {
3     public void Initialize()
4     {
5         this.container.RegisterType<IOrdersRepository, OrdersRepository>(new ContainerControlledLifetimeManager());
6         ...
7     }
8     ...
9 }

      Note:和配置相比,在代码中注册的有利之处在于,只有模块被加载的时候注册才会发生。

 

用MEF来注册类型

为了使用容器注册类型,MEF使用基于属性的系统。自然而然,添加类型注册到容器是很简单的:它需要附加【Export】属性到类型中,如下所示:

1 [Export(typeof(ILoggerFacade))]
2 public class CallbackLogger: ILoggerFacade
3 {
4 }

当使用MEF时的另一个选择是创建一个类的实例并且用容器注册该特定实例。在Modularity for Silverlight with MEF QuickStart 中的QuickStartBootstrapper展示了一个用ConfigureContainer方法的例子

protected   override void ConfigureContainer()

{

    base.ConfigureContainer();

    // Because we created the CallbackLogger   and it needs to

    // be used immediately, we compose it to   satisfy any imports it has.

      this.Container.ComposeExportedValue<CallbackLogger>(this.callbackLogger);

}

解析

类型被注册之后,它可以作为一个依赖属性被解析或者注入。当一个类型已被解析,容器需要去创建一个新的实例,并注入依赖性属性到这些实例中。

通常而言,当一个类型被解析,会发生下列三件事之一:

  1. 如果类型没有被注册容器会抛出一个异常。

      Note:包括Unity在内的一些容器允许解析一个具体的尚未被注册的类型。

2.如果类型被注册为一个单例,容器将返回单例的实例。如果类型被第一次调用,容器会创建它并为将来的调用一直保留。

3.如果类型没有被注册为一个单例,容器将返回一个新的实例。

       Note默认情况下,用MEF注册的类型是单例并且容器保留对象的引用。在Unity中,新的对象实例在默认情况下返回,容器不保留对该对象的引用。

 

用Unity解析实例

来自Commanding QuickStart的下列代码事例展示了 OrdersEditorViewOrdersToolBar 视图从容器关联他们到相应的区域处被解析。

 1 public class OrderModule : IModule
 2
 3 {
 4
 5     public void Initialize()
 6
 7     {
 8
 9           this.container.RegisterType<IOrdersRepository, OrdersRepository>(new   ContainerControlledLifetimeManager());
10
11
12
13         // Show the Orders Editor   view in the shell‘s main region.
14
15           this.regionManager.RegisterViewWithRegion("MainRegion",
16
17                                                       () => this.container.Resolve<OrdersEditorView>());
18
19
20
21         // Show the Orders Toolbar   view in the shell‘s toolbar region.
22
23           this.regionManager.RegisterViewWithRegion("GlobalCommandsRegion",
24
25                                                       () => this.container.Resolve<OrdersToolBar>());
26
27     }
28
29     ...
30
31 }

      OrdersEditorPresentationModel 构造函数包含下列依赖属性,当它被解析时,依赖性属性被注入。

 1 public OrdersEditorPresentationModel( IOrdersRepository   ordersRepository, OrdersCommandProxy commandProxy )
 2
 3 {
 4
 5     this.ordersRepository =   ordersRepository;
 6
 7     this.commandProxy     = commandProxy;
 8
 9
10
11     // Create dummy order data.
12
13     this.PopulateOrders();
14
15
16
17     // Initialize a CollectionView   for the underlying Orders collection.
18
19 #if SILVERLIGHT
20
21     this.Orders = new   PagedCollectionView( _orders );
22
23 #else
24
25     this.Orders = new   ListCollectionView( _orders );
26
27 #endif
28
29     // Track the current   selection.
30
31     this.Orders.CurrentChanged +=   SelectedOrderChanged;
32
33       this.Orders.MoveCurrentTo(null);
34
35 }

除了在前面的代码显示的构造函数注入,Unity也允许属性注入。当对象被解析时,任何具有【依赖】性质的属性都是被自动解析并注入。

用MEF解析实例

下面的代码示例演示在Modularity forSilverlight with MEF QuickStart中引导程序如何获得外壳的一个实例。代码可以请求接口的实例,而不是请求一个具体类型.

1 protected override DependencyObject CreateShell()
2
3 {
4
5     return   this.Container.GetExportedValue<Shell>();
6
7 }

在任何由MEF解析的类中,你也可以使用构造函数注入,如下面的来自ModuleA in the Modularity for Silverlight with MEF QuickStart的代码示例所示,其中有一个ILoggerFacadeIModuleTracker注入。

 1 [ImportingConstructor]
 2
 3 public ModuleA(ILoggerFacade logger, IModuleTracker   moduleTracker)
 4
 5 {
 6
 7     if (logger   == null)
 8
 9     {
10
11         throw   new ArgumentNullException("logger");
12
13     }
14
15     if   (moduleTracker == null)
16
17     {
18
19         throw   new ArgumentNullException("moduleTracker");
20
21     }
22
23       this.logger = logger;
24
25       this.moduleTracker = moduleTracker;
26
27       this.moduleTracker.RecordModuleConstructed(WellKnownModuleNames.ModuleA);
28
29 }

 

另外一个功能就是依赖注入,正如在ModuleTracker类(来自Modularity for Silverlight with MEF QuickStart)中所示,这个类存在一个ILoggerFacade注入的实例.

 1 [Export(typeof(IModuleTracker))]
 2
 3 public class ModuleTracker : IModuleTracker
 4
 5 {
 6
 7      // Due to   Silverlight/MEF restrictions, this must be public.
 8
 9      [Import]   public ILoggerFacade Logger;
10
11 }

       Note:在Silverlight中,导入属性和域必须是public.

 

Prism,使用依赖注入容器和服务

通常被称为是“容器”的依赖注入容器,是用来满足组件之间的依赖关系. 满足这些依赖关系通常涉及到的注册和解析.Prism为Unity容器和MEF提供支持,但不是特定容器. 由于库通过IServiceLocator接口访问容器,所以容器可以被取代。要做到这一点,你的容器必须实现IServiceLocator接口。通常,如果要更换容器,您还需要提供你自己的特定容器的引导程序. IServiceLocator接口被定义在Common Service Locator Library中。这是开源的并提供一个抽象 IoC(控制反转)的容器,如依赖注入容器,和服务定位器。使用这个库的目的是在未绑到一个特定的实现的情况下,利用IoC和服务定位。

Prism库提供的UnityServiceLocatorAdapter和MefServiceLocatorAdapter。两个适配器通过扩展ServiceLocatorimplBase类型实现ISeviceLocator接口。下面的插图显示了类层次。

The Common Service Locator implementations in Prism

虽然Prism库不参考或依赖于一个特定的容器,对于应用程序依赖于一个特定的容器却是典型的。这意味着,对于依赖于容器的一个具体应用是合理的,但Prism库不是直接依赖容器。例如,股票交易日数的快速入门包括棱镜依靠统一为容器。其他样品和快速入门依赖MEF。包括Prism在内的the Stock Trader RI and several of the QuickStarts 依赖于Unity作为容器. 其他的例子的QuickStarts依赖于MEF.

IServiceLocator

下列代码展示了IServiceLocator接口

 1 public interface IServiceLocator : IServiceProvider
 2
 3 {
 4
 5     object   GetInstance(Type serviceType);
 6
 7     object   GetInstance(Type serviceType, string key);
 8
 9       IEnumerable<object> GetAllInstances(Type serviceType);
10
11     TService   GetInstance<TService>();
12
13     TService   GetInstance<TService>(string key);
14
15       IEnumerable<TService> GetAllInstances<TService>();
16
17 }

服务定位器是用下面的代码所示的扩展方法在Prism中被扩展的。你可以看到,IServiceLocator只用于解析,这意味着它是用来获取一个实例而不用于注册。

 1 public   static class ServiceLocatorExtensions
 2
 3 {
 4
 5     public static object TryResolve(this   IServiceLocator locator, Type type)
 6
 7     {
 8
 9         try
10
11         {
12
13             return locator.GetInstance(type);
14
15         }
16
17         catch (ActivationException)
18
19         {
20
21             return null;
22
23         }
24
25     }
26
27
28
29     public static T TryResolve<T>(this   IServiceLocator locator) where T: class
30
31     {
32
33         return locator.TryResolve(typeof(T))   as T;
34
35     }
36
37 }

TryResolve扩展方法-----Unity容器不支持----返回类型的一个实例,该类型如果没有被注册,将会被解析;否则返回null。
      在模块加载时, ModuleInitializer使用 IServiceLocator来解析模块,如下面的代码示例所示。

 1 IModule   moduleInstance = null;
 2
 3 try
 4
 5 {
 6
 7     moduleInstance =   this.CreateModule(moduleInfo);
 8
 9     moduleInstance.Initialize();
10
11 }
12
13 ...

 1 protected virtual IModule CreateModule(string   typeName)
 2
 3 {
 4
 5     Type   moduleType = Type.GetType(typeName);
 6
 7     if   (moduleType == null)
 8
 9     {
10
11         throw   new ModuleInitializeException(string.Format(CultureInfo.CurrentCulture,   Properties.Resources.FailedToGetType, typeName));
12
13     }
14
15
16
17     return   (IModule)this.serviceLocator.GetInstance(moduleType);
18
19 }

考虑使用IServiceLocator

      IServiceLocator并不意味着是通用的容器。容器的使用具有不同的语义,这往往使决定为什么那个容器被选择。因此,Stock Trader RI直接使用Unity而不是使用IServiceLocator。这是为你的应用程序的开发所推荐的方法。

在下列解决方法中,使用IServiceLocator或许是合适的:

  1. 你是一个独立软件供应商(ISV),设计需要支持多个容器第三方服务.
  2. 你正在设计一个被应用于组织的服务,这个组织使用多容器.

More Infomation

与容器相关的信息,如下所示:

时间: 2024-08-01 10:44:12

Prism 文档 第三章 管理组件之间的依赖关系的相关文章

Prism4 文档翻译系列---第3章 管理组件间的依赖关系

基于Prism类库的应用程序可能是由多个松耦合的类型和服务组成的复杂应用程序,他们需要根据用户的动作发出内容和接收通知进行互动,由于他们是松耦合的,他们需要一种方式来互动和交流来传递业务功能的需求. 为了将这些零散的模块组合在一起,基于Prism的应用程序使用了一个依赖注入容器,依赖注入容器通过基于容器的配置提供实例化类对象并且管理它们的声明周期减少了对象之间的依赖耦合关系.在创建对象时,容器将其所需要的依赖全部注入其中.如果这些依赖还没有被创建,容器将会首先创建并解析依赖所需要的对象或者它的依

Ext JS 6学习文档-第3章-基础组件

基础组件 在本章中,你将学习到一些 Ext JS 基础组件的使用.同时我们会结合所学创建一个小项目.这一章我们将学习以下知识点: 熟悉基本的组件 – 按钮,文本框,日期选择器等等 表单字段的校验 菜单和工具栏 设计一个表单 计算器程序– 本章的示例项目 转载请注明出处:http://www.jeeboot.com/archives/1219.html 本章的主要目的是创建一个表单设计和一个计算器示例项目.以下图分别展示了表单设计和计算器设计. 首先,你观察下列表单设计,你会发现我们使用了大量的控

Ext JS 6学习文档-第5章-表格组件(grid)

使用 Grid 本章将探索 Ext JS 的高级组件 grid .还将使用它帮助读者建立一个功能齐全的公司目录.本章介绍下列几点主题: 基本的 grid 排序 渲染器 过滤 分页 单元格编辑 行编辑 分组 分组 grid(pivot grid) 公司目录 -一个示例项目 转载请注明出处:http://www.jeeboot.com/archives/1225.html grid 组件是 Ext JS 中最强大的一个组件.它有很多的选项和配置,能以任何你希望的形式来构建 grid. Ext JS

Prism 4 文档 ---第11章 部署Prism应用程序

要成功移动Prism应用到生产中,需要对部署计划为应用程序的设计过程的一部分.本章介绍了注意事项和你需要采取的准备以部署应用程序,以及你要在用户手中获得部署程序所需要采取的行动. Silverlight和WPF有两个不同的承载环境,所以部署考虑的内容就不同了,这依赖于是否你在构建一个Silverlight Prism应用程序还是一个WPF Prism应用程序. 部署Silverlight Prism应用程序 Silverlight应用程序通过Http请求昨晚XAP文件从浏览器被分发.XAP文件其

《Javascript权威指南》学习笔记之十九--HTML5 DOM新标准---处理文档元信息和管理交互能力

一.了解DOM 1.DOM是Document Object Model的缩写,即文档对象类型,是文档在内存中的表示形式,是一个应用程序接口,定义了文档的逻辑结构以及一套访问和处理文档的方法. 2.HTML DOM与Core DOM的区别:前者提供了大量的方法和属性,与现有的程序模型一致,更便于脚本的编写者控制. 二.document对象 使用window.document属性返回一个document对象,代表当前window内加载的文档.window可以省略.winName.document返回

HTML与CSS入门——第三章 理解HTML和XHTML的关系

知识点: 1.以HTML创建一个简单网页的方法 2.包含每个网页必须有的所有HTML标签的方法 3.用段落和换行组织页面的方法 4.用标题组织内容的方法 5.HTML.XML.XHTML和HTML5之间的差别 3.1 从一个简单的网页开始: 作者建议:从简单的文本编辑器开始学习,之后再转向可视化工具. 扩展名支持:.htm以及.html 如.jsp,.asp,.php之类的文件类型使用超出了HTML范围的服务器端技术,需要专门的服务端支持.比如Apache服务器 3.2 每个XHMTL网页必须有

McAfee Vulnerability Manager(Foundstone)各组件之间的通信关系

Components andwhat they do McAfeeVulnerability Manager consists of components that work together to monitor yoursystems. Enterprise manager – Uses Microsoft Internet Information Services (IIS) to provideauthorized users with access to McAfee Vulnerab

Prism 4 文档 ---第8章 导航

作为同用户具有丰富的交互的客户端应用程序,它的用户界面(UI)将会持续不断的更新来反映用户工作的当前的任务和数据.用户界面可以进行一段时间相当大的变化作为用户交互的应用程序中完成各种任务.通过该应用程序协调这些用户界面的变化的过程通常被称为导航. 经常,导航意味着某些控件将会从UI中移除,其他的控件将会被添加到UI中.在另外的情况下,导航也意味着一个或多个存在的控件的可视化状态被更新.---例如,一些控件可能被简单的隐藏或收缩而另外的一些控件被显示后展开.类似的,导航可能意味着一个控件展示的绑定

Ext JS 6学习文档–第1章–ExtJS入门指南

Ext JS 入门指南 前言 本来我是打算自己写一个系列的 ExtJS 6 学习笔记的,因为 ExtJS 6 目前的中文学习资料还很少.google 搜索资料时找到了一本国外牛人写的关于 ExtJS 6 的电子书 [Ext JS 6 By Example].这份资料在 PACKT 上卖 35.99 刀的,当然了万能的 google 还是帮我下载到了 PDF 文档.大概看了一下,讲的很详细,例子也比较简单,容易理解,现我准备利用工作之余翻译这份文档,为自己学习加深理解,也希望能帮助更多的人学习.