Prism初研究之依赖管理

Prism初研究之依赖管理
关键决定:选择DI容器
考虑使用容器
核心情景
类型注册
使用Unity容器注册类型
使用MEF容器注册类型
依赖解析
Unity实例解析
MEF实例解析
在Prism中使用依赖注入容器
IServiceLocator
扩展阅读

注意:Prism框架本身不提供指定的依赖注入容器,你可以使用其它的依赖注入容器,比如:Castle Windsor,StructureMap,和Spring.NET。示例中有使用Unity(Unity Application Block)作为容器的,也有使用MEF(Managed Extensibility Framework)的。

关键决定:选择DI容器

Prism提供了Unity和MEF两种可供选择的DI容器,如果选用其它的容器需要多做一些工作。

  • Unity和MEF都提供了以下一些功能:

    • 都使用容器注册类型;
    • 都使用容器注册实例;
    • 都使用命令式地方式创建注册类型的实例;
    • 都将依赖注入到构造函数中;
    • 都将依赖注入到属性中;
    • 都支持特性标注的类型依赖注入;
    • 都解析对象图中的依赖关系。
  • Unity特有的功能:
    • 能够解析具体类型而无需注册;
    • 能解析开放泛型;
    • 它截取对对象的调用,并给目标对象添加额外的功能。
  • MEF特有的功能:
    • 能发现文件夹中的程序集;
    • 使用XAP文件来下载和装配发现;
    • 它将发现的属性和集合重写为新的类型;
    • 自动导出派生类型;
    • 和.NET框架一起部署。

考虑使用容器

考虑在选择前:

  • 是否使用容器来注册和解析组件:

    • 考虑性能开销是否可接受,因为使用反射创建实例开销比较大;
    • 如果有很多或者深度的依赖关系,创建实例的开销会显著增加;
    • 如果一个组件没有依赖,或者也不依赖于其它类型,不用将它放入容器中;
    • 如果一个组件有一组依赖关系,并且依赖关系不会改变,不用将它放入容器中。
  • 考虑一个组件的生命周期,来决定注册为单例还是一个实例:
    • 如果组件是一个全局的服务,作为一个单一的资源来进行管理,应该将它注册为单例。比如日志服务;
    • 如果一个组件提供共享的状态给多个客户端,应该将它注册为单例;
    • 如果一个对象每次被注入时需要一个新的实例,应该将它注册为实例。
  • 考虑通过代码还是配置文件来配置容器:
    • 如果想要集中管理所有不同的服务,就使用配置文件来配置容器;
    • 如果想要按照条件注册指定的服务,就需要通过编码来配置容器;
    • 如果有模块级别的服务,就需要通过编码来配置容器,防止注册发生在模块加载前。

      有一些DI容器不支持配置文件,比如MEF。

核心情景

DI容器可以达到两个不低,类型的注册和依赖解析。

类型注册

一般来讲,有两种类型的注册方式:

  • 在容器中注册类型或者映射,在合适的时候,返回指定类型的一个实例;
  • 在容器中注册一个已经存在的对象实例,作为单例。容器会返回一个单例对象的引用。

使用Unity容器注册类型

在初始化过程中,一个类型允许注册其它的类型,比如视图类和服务类。注册使依赖的请求可以通过容器来实现。因此,模块的构造函数需要拥有容器的依赖注入。

  1. public class OrderModule : IModule
  2. {
  3. private readonly IRegionManager regionManager;
  4. private readonly IUnityContainer container;
  5. public OrderModule( IUnityContainer container, IRegionManager regionManager )
  6. {
  7. this.container = container;
  8. this.regionManager = regionManager;
  9. }
  10. public void Initialize()
  11. {
  12. this.container.RegisterType<IOrdersRepository, OrdersRepository>(new ContainerControlledLifetimeManager());
  13. // Show the Orders Editor view in the shell's main region.
  14. this.regionManager.RegisterViewWithRegion( "MainRegion", () => this.container.Resolve<OrdersEditorView>() );
  15. // Show the Orders Toolbar view in the shell's toolbar region.
  16. this.regionManager.RegisterViewWithRegion( "GlobalCommandsRegion", () => this.container.Resolve<OrdersToolBar>() );
  17. }
  18. }

使用代码注册比配置文件注册的好处是,可以保证只有模块加载之后才会发生注册。

使用MEF容器注册类型

MEF可以使用特性系统来完成类型注册到容器:

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

MEF还可以通过注册一个特定的实例:

  1. protected override void ConfigureContainer()
  2. {
  3. base.ConfigureContainer();
  4. this.Container.ComposeExportedValue<CallbackLogger>(this.callbackLogger);
  5. }

注意:如果使用MEF容器,推荐使用特性系统来完成类型注册。

依赖解析

当一个类型依赖需要被解析时,会发生三件事:

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

    注意:有一些容器(包括Unity),允许未被注册的具体类型。

  2. 如果类型被注册为单例,容器会返回单例实例,如果类型是第一次被请求,容器会创建这个单例并一直维护它。

  3. 如果类型未被注册为单例,容器会返回一个新的实例对象。

    注意:MEF默认注册为单例,并且容器会维护一个单例的引用。Unity默认返回一个新的实例对象,并且容器不会维护这些实例的引用。

Unity实例解析

示例:Commanding QuickStart

  • 容器解析OrdersEditorView和OrdersToolBar视图的依赖关系:
  1. // OrderModule.cs
  2. public class OrderModule : IModule
  3. {
  4. public void Initialize()
  5. {
  6. this.container.RegisterType<IOrdersRepository, OrdersRepository>(new ContainerControlledLifetimeManager());
  7. //在main region显示Orders Editor 视图
  8. this.regionManager.RegisterViewWithRegion("MainRegion", () => this.container.Resolve<OrdersEditorView>());
  9. //在工具栏Region显示工具栏
  10. this.regionManager.RegisterViewWithRegon("GlobalCommandsRegion", () => this.container.Resolve<OrdersToolBar>());
  11. }
  12. }
  • OrdersEditorViewModel的构造函数依赖注入:
  1. public OrdersEditorViewModel(IOrdersRepository ordersRepository, OrdersCommandProxy commandProxy)
  2. {
  3. this.ordersRepository = ordersRepository;
  4. this.commandProxy = commandProxy;
  5. //创建虚拟的订单数据
  6. this.PopulateOrders();
  7. this.Orders = new ListCollectionView( _orders );
  8. //跟踪目前的选择
  9. this.Orders.CurrentChanged += SelectedOrderChanged;
  10. this.Orders.MoveCurrentTo(null);
  11. }
  • Unity还可以使用属性来进行依赖注入,任何拥有[Dependency]特性的属性都能够进行自动的依赖解析

MEF实例解析

示例:Modularity with MEF QuickStart

  • 请求具体的类型
  1. protected override DependencyObject CreateShell()
  2. {
  3. return this.Container.GetExportedValue<Shell>();
  4. }
  • 通过构造函数依赖注入:示例中的ModuleA
  1. [ImportintConstructor]
  2. public ModuleA(ILoggerFacade logger, IModuleTracker moduleTracker)
  3. {
  4. if(logger == null)
  5. {
  6. throw new ArgumentNullException("logger");
  7. }
  8. if(moduleTracker == null)
  9. {
  10. throw new ArgumentNullException("moduleTracker");
  11. }
  12. this.logger = logger;
  13. this.moduleTracker = moduleTracker;
  14. this.moduleTracker.RecordModuleConstructed(WellKnownModuleNames.ModuleA);
  15. }
  • 通过属性进行依赖注入:ModuleTracker
  1. [Export(typeof(IModuleTracker))]
  2. public class ModuleTracker : IModuleTracker
  3. {
  4. [Import]
  5. private ILoggerFacade Logger;
  6. }

在Prism中使用依赖注入容器

Prism框架提供Unity和MEF两种依赖注册容器的支持,但是这两种DI容器并不是指定的。因为框架通过IServiceLocator接口来访问DI容器,所以DI容器可以被替换,只需要实现IServiceLocator接口即可。通常如果要替换DI容器,同时需要提供自己指定的Bootstrapper。IServiceLocator接口定义在Microsoft.Practices.ServiceLocation类库中(通用服务类库),这是一个开源的控制反转容器(IoC
)。
Prism提供UnityServiceLocatorAdapter和MefServiceLocatorAdapter,它们都继承自ServiceLocatorImplBase类型,后者实现了IServiceLocator接口。

Prism并不指定DI容器,而是由特定的应用来指定。

IServiceLocator

IserviceLocator接口:

  1. public interface IServiceLocator : IServiceProvider
  2. {
  3. object GetInstance(Type serviceType);
  4. object GetInstance(Type serviceType, string key);
  5. IEnumerable<object> GetAllInstances(Type serviceType);
  6. TService GetInstance<TService>();
  7. TService GetInstance<TService>(string key);
  8. IEnumerable<TService> GetAllInstances<TService>();
  9. }

IServiceLocator接口在Prism中通过扩展方法进行了扩展,你可以看到IServiceLocator接口只用来进行依赖解析,并不能用来进行类型注册。扩展方法如下:

  1. public static class ServiceLocatorExtensions
  2. {
  3. public static object TryResolve(this IServiceLocator locator, Type type)
  4. {
  5. if (locator == null) throw new ArgumentNullException("locator");
  6. try
  7. {
  8. return locator.GetInstance(type);
  9. }
  10. catch (ActivationException)
  11. {
  12. return null;
  13. }
  14. }
  15. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter")]
  16. public static T TryResolve<T>(this IServiceLocator locator) where T : class
  17. {
  18. return locator.TryResolve(typeof(T)) as T;
  19. }
  20. }

TryResolve扩展方法(Unity不支持),返回一个注册类型的实例,否则返回null。
ModuleInitializer使用IServiceLocator接口来解析模块的加载。例如:

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

扩展阅读

DI容器的相关资料:

· Unity Application Block on MSDN.

· Unity community site on CodePlex.

· Managed Extensibility Framework Overview on MSDN.

· MEF community site on CodePlex.

· Inversion of Control containers and the Dependency Injection pattern on Martin Fowler’s website.

· Design Patterns: Dependency Injection in MSDN Magazine.

· Loosen Up: Tame Your Software Dependencies for More Flexible Apps in MSDN Magazine.

· Castle Project

· StructureMap

· Spring.NET

来自为知笔记(Wiz)

转载于:https://www.cnblogs.com/qianzi067/p/5804873.html

Prism初研究之依赖管理相关推荐

  1. 用CocoaPods做iOS程序的依赖管理

    CocoaPods 简介 每种语言发展到一个阶段,就会出现相应的依赖管理工具,例如 Java 语言的 Maven,nodejs 的 npm.随着 iOS 开发者的增多,业界也出现了为 iOS 程序提供 ...

  2. python 依赖包管理_依赖管理

    如果我们的项目依赖第三方的jar包,例如commons logging,那么问题来了:commons logging发布的jar包在哪下载? 如果我们还希望依赖log4j,那么使用log4j需要哪些j ...

  3. java单例模式深入详解_javascript 模块依赖管理的本质深入详解

    本文实例讲述了javascript 模块依赖管理的本质.分享给大家供大家参考,具体如下: 模块模式定义 模块是'javascript'的一种设计模式,它为函数定义一个包装函数,并且该包装函数的返回值与 ...

  4. gradle依赖管理_依赖管理

    gradle依赖管理 Why Bother 何必呢 Writing software is a very expensive process, and most systems we interact ...

  5. Maven项目依赖管理 学习笔记

    本学习笔记根据慕课视频[Maven项目依赖管理]摘录,主要学习maven的使用方式,从理念到实践.基础到应用,掌握基于maven管理java项目的方式. 目录 第一章 Maven概述 Maven简介 ...

  6. maven教程 IDEA中使用Maven Tomcat配置 依赖管理

    1.背景 1.1 软件是一个工程 我们在日常生活常能听到工程这个词,像桥梁工程.道路工程.南水北调工程等等. 工程说简单点就是各个行业的从业人员通过总结规律或者方法,以最短的时间和人力.物力来做出高效 ...

  7. 编程语言的支撑体系:构建系统、IDE 和依赖管理

    年关(annual review)将近,这一段时间,我在梳理 2020 年做的一些事情,并试着制定下一年的计划.过程中,我发现我做的一些事情,或是工作相关,或是兴趣上的探索,还都可以继续总结出一些文章 ...

  8. Maven 依赖管理与依赖标签

    原文链接 1.概述 在本教程中,我们将回顾两个重要的Maven标签--dependencyManagement 和 dependencies. 这些特性对于多模块项目特别有用. 我们将回顾这两个标签的 ...

  9. Gradle系列教程之依赖管理

    这一章我将介绍Gradle对依赖管理的强大支持,学习依赖分组和定位不同类型仓库.依赖管理看起来很容易,但是当出现依赖解析冲突时就会很棘手,复杂的依赖关系可能导致构建中依赖一个库的多个版本.Gradle ...

  10. [笔记]解决m2eclipse给项目添加maven依赖管理时可能不给项目的build path...

    为什么80%的码农都做不了架构师?>>>    解决办法:在m2eclipse提供的菜单里关闭项目的Maven依赖管理,然后再启动Maven的依赖管理!!! 检查:1)查看项目的.c ...

最新文章

  1. linux系统分析命令,Linux操作系统基础解析之(四)——Linux基本命令剖析(2)
  2. Golang+Python 实现安全动态开机密码+服务器存储
  3. [Share]10 Free EBooks for Web Designers
  4. 基于vivado的fir ip核的重采样设计与实现
  5. form 中Enctype=multipart/form-data 的作用
  6. 前端工程师的修真秘籍(css、javascript和其它)
  7. Vim配置IDE开发环境
  8. 欢乐纪中A组周六赛【2019.3.23】
  9. 用php人工使网页过期
  10. 创建一个java项目
  11. 《R语言数据挖掘:实用项目解析》——1.11 apply原理
  12. 【书籍推荐】《美国黒室》
  13. 解决Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.1:compile (default-compile
  14. 免费破解版Xshell和Xftp
  15. 对目录的操作(opendir)
  16. 专业编写c语言的软件,适合编写C语言代码的几款软件
  17. 比较全的Struts介绍04
  18. 自己组装高档游戏型计算机表格,推荐高端笔记本配置表和完整的高配置计算机物理检查...
  19. python中迭代啥意思_Python中什么是迭代
  20. UVa1646 - Edge Case

热门文章

  1. DBeaverUE for Mac(数据库管理软件)旗舰版
  2. MacOS入门技巧分享
  3. 如何在Mac设置开机自启程序项
  4. python实现oss文件下载
  5. Django搭建个人博客:改写View视图
  6. [javase] 1.请从键盘随机输入10个整数保存到List中,并按倒序、从大到小的顺序显示出来...
  7. 如何通过ildasm/ilasm修改assembly的IL代码
  8. 用php实现遍历目录
  9. Struts如何获取客户端ip地址
  10. 职场这样发邮件,你死定了!