Prism初研究之依赖管理

摘要:
选择DI容器。考虑使用容器核心场景类型进行注册。使用Unity容器注册类型。使用MEF容器注册类型。解析Unity实例。解析MEF实例。使用Prism中的依赖注入容器。IServiceLocator扩展。注意:Prism框架不提供指定的依赖注入容器,而是使用容器来注册类型;两者都使用命令样式创建注册类型的实例;能够在没有注册的情况下解析特定类型;是否使用容器注册和解析组件:在容器中注册类型或映射。
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中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)


免责声明:文章转载自《Prism初研究之依赖管理》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇什么是物联网网关?物联网网关具备什么功能?_转ajax页面刷新小错误(提交按钮type必须为button,而不能是submit)下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

Oracle 12c 容器讲解

Oracle 12c一个重要新特性是插接式数据库。 插接式数据库由一个使用 CDB(Container Database)选项创建的容器数据库和一个或多个 PDB(Pluggable Database)组成,CDB 作为容器容纳 PDB,而 PDB 彼此隔离,就像一个独立的数据库般在 CDB 中存在。PDB 是一组 Schema 的集合,在 CDB 中作为...

关于Spring的问题(一)

一、核心容器(应用上下文) 模块 这是基本的Spring模块,提供spring 框架的基础功能,BeanFactory 是 任何以spring为基础的应用的核心。Spring 框架建立在此模块之上,它使Spring成为一个容器。 二、BeanFactory – BeanFactory 实现举例 Bean 工厂是工厂模式的一个实现,提供了控制反转功能,用来把...

k8s-整体概述和架构

1、Kubernetes是什么 Kubernetes是一个轻便的和可扩展的开源平台,用于管理容器化应用和服务。通过Kubernetes能够进行应用的自动化部署和扩缩容。在Kubernetes中,会将组成应用的容器组合成一个逻辑单元以更易管理和发现。Kubernetes积累了作为Google生产环境运行工作负载15年的经验,并吸收了来自于社区的最佳想法和实践...

Tomcat模型结构

一、请求过程 Tomca的两大组件:Connecter和Container Connecter组件 1、Connecter将在某个指定的端口上侦听客户请求,接收浏览器的发过来的 tcp 连接请求,创建一个 Request 和 Response 对象分别用于和请求端交换数据,然后会产生一个线程来处理这个请求并把产生的 Request 和 Response...

Django学习篇:ORM

目录 Django ORM中常用字段和参数 一些说明: 常用字段: 1.AutoField 2.IntegerField 3.CharField Django ORM中常用字段和参数 一些说明: 表myapp_person的名称是自动生成的,如果你要自定义表明,需要在model的Meta类中指定db_table参数,强烈建议使用小写表...

Tomcat学习总结(10)——Tomcat多实例冗余部署

昨天在跟群友做技术交流的时候,了解到,有很多大公司都是采用了高可用的,分布式的,实例沉余1+台。但是在小公司的同学也很多,他们反映并不是所有公司都有那样的资源来供你调度。往往公司只会给你一台机器,因为有些应用挂了公司也不会有损失的,我们往往一台机器就可以搞定。 但是,我们也要为我们做出来的应用负责,毕竟东西做出来是为了给人用的,如果做出来的东西经常挂了,...