本文内容

  • Unity 概述
  • 环境
  • 一个真实的例子
  • 类型注册(Type Registrations)
  • 解析类型(Resolving Types)

跳槽,新公司使用了 Unity,初步看了一下,公司的使用还是比较简单的,其实 Unity 本身的用法很多。另外,前段时间我翻译和实验了 Martin Fowler 的《Java 控制反转和依赖注入模式》,本文是 .NET 平台下的依赖注入。

Unity 涉及的内容和用法比较多,之后慢慢说,本文先大概介绍如何用 Unity 进行依赖注入,它基本可以分为两个操作:注册(RegisterType)和解析(Resolve),也就是说,先注册类型;然后解析类型,返回创建的对象。

下载 MyDemo and DIwithUnitySample

下载 MyDemo and DIwithUnitySample v2(补充)

下载 Unity 3

下载 Unity bootstrapper for ASP.NET MVC

下载 Unity bootstrapper for ASP.NET WebApi

Unity 概述


Unity Application Block(Unity)是一个轻量级的,可扩展的依赖注入容器,它支持构造函数注入,属性注入和方法调用注入。它为开发人员提供了以下优点:

  • 提供简化的对象创建,特别是层级对象结构和依赖,简化应用程序代码;
  • 支持需求抽象;这可以让开发者在运行时或是配置文件指定依赖,简化横切关注点(crosscutting concerns)的管理;
  • 通过延迟组件配置到容器,增加了灵活性;
  • 具有服务定位器功能;这可以让客户存储或缓存容器。对 ASP.NET Web 应用程序特别有用,开发者可以在 ASP.NET 会话或应用程序中持久容器。

环境


  • Windows 7 旗舰版 SP1
  • Microsoft Visual Studio Ultimate 2013 Update 4

一个真实的例子


咋看上去,RegisterTypes 方法有点复杂;下面会详细讨论各种的类型注册;再讨论应用程序如何注册以在运行时需要时解析类型。这个例子也说明,在你应用程序的一个方法内如何完成所有类型的注册。

public static void RegisterTypes(IUnityContainer container)

        {

            Trace.WriteLine(string.Format("Called RegisterTypes in ContainerBootstrapper"), "UNITY");

 

            var storageAccountType = typeof(StorageAccount);

            var retryPolicyFactoryType = typeof(IRetryPolicyFactory);

 

            // 实例注册

            StorageAccount account =

              ApplicationConfiguration.GetStorageAccount("DataConnectionString");

            container.RegisterInstance(account);

 

            // 注册工厂

            container

              .RegisterInstance<IRetryPolicyFactory>(new ConfiguredRetryPolicyFactory())

              .RegisterType<ISurveyAnswerContainerFactory, SurveyAnswerContainerFactory>(new ContainerControlledLifetimeManager());

 

            // 注册 table 类型

            container

              .RegisterType<IDataTable<SurveyRow>, DataTable<SurveyRow>>(new InjectionConstructor(storageAccountType, retryPolicyFactoryType, Constants.SurveysTableName))

              .RegisterType<IDataTable<QuestionRow>, DataTable<QuestionRow>>(new InjectionConstructor(storageAccountType, retryPolicyFactoryType, Constants.QuestionsTableName));

 

            // 注册 message queue 类型, 使用带泛型的 typeof

            container

              .RegisterType(

                  typeof(IMessageQueue<>),

                  typeof(MessageQueue<>),

                  new InjectionConstructor(storageAccountType, retryPolicyFactoryType, typeof(String)));

 

            // 注册 blob 类型

            container

              .RegisterType<IBlobContainer<List<string>>,

                EntitiesBlobContainer<List<string>>>(

                  new InjectionConstructor(storageAccountType, retryPolicyFactoryType, Constants.SurveyAnswersListsBlobName))

              .RegisterType<IBlobContainer<Tenant>,

                EntitiesBlobContainer<Tenant>>(

                  new InjectionConstructor(storageAccountType, retryPolicyFactoryType, Constants.TenantsBlobName))

              .RegisterType<IBlobContainer<byte[]>,

                FilesBlobContainer>(

                  new InjectionConstructor(storageAccountType, retryPolicyFactoryType, Constants.LogosBlobName, "image/jpeg"))

              .RegisterType<IBlobContainer<SurveyAnswer>,

                EntitiesBlobContainer<SurveyAnswer>>(

                  new InjectionConstructor(storageAccountType, retryPolicyFactoryType, typeof(String)));

 

            // 注册 store 类型

            container

              .RegisterType<ISurveyStore, SurveyStore>()

              .RegisterType<ITenantStore, TenantStore>()

              .RegisterType<ISurveyAnswerStore, SurveyAnswerStore>(

                new InjectionFactory((c, t, s) => new SurveyAnswerStore(

                  container.Resolve<ITenantStore>(),

                  container.Resolve<ISurveyAnswerContainerFactory>(),

                  container.Resolve<IMessageQueue<SurveyAnswerStoredMessage>>(new ParameterOverride("queueName", Constants.StandardAnswerQueueName)),

                  container.Resolve<IMessageQueue<SurveyAnswerStoredMessage>>(new ParameterOverride("queueName", Constants.PremiumAnswerQueueName)),

                  container.Resolve<IBlobContainer<List<String>>>())));

        }

类型注册(Type Registrations)


上面的代码列出了用 Unity 容器完成不同类型的注册。下面单独说明。

实例注册

最简单的类型注册就是实例注册,Unity 容器以单件实例来负责维护对象的引用。例如:

StorageAccount account =

              ApplicationConfiguration.GetStorageAccount("DataConnectionString");

            container.RegisterInstance(account);

StorageAccount 对象在注册时间就被创建,并且在容器中只有一个该对象的实例。这个单独的实例被容器中很多其他对象共享。

你也可以在 RegisterType 方法中使用 ContainerControlledLifetimeManager 类来创建单件实例,有容器维护对象的引用。

简单类型注册

最常见的类型注册是把一个接口类型映射到一个具体的类型。例如:

container.RegisterType<ISurveyStore, SurveyStore>();

接下来,你可以按如下代码解析 ISurveyStore 类型,容器将把任何所需的依赖注入到 SurveyStore 对象,并创建。

var surveyStore = container.Resolve<ISurveyStore>();

构造函数注入

下面的代码段说明 DataTable 类的具有三个参数的构造函数。

public DataTable(StorageAccount account, IRetryPolicyFactory retryPolicyFactory, string tableName)

    : base(retryPolicyFactory)

{

    Trace.WriteLine(string.Format("Called constructor in DataTable with account={0}, tableName={1}", account.ConnectionString, tableName), "UNITY");

    this.account = account;

    this.tableName = tableName;

}

在容器中注册 DataTable 类型会包含一个容器如何解析参数类型的 InjectionConstructor 定义:Storage-Account 和 RetryPolicyFactory 类型,以及表名。

container

             .RegisterType<IDataTable<SurveyRow>, DataTable<SurveyRow>>(new InjectionConstructor(storageAccountType, retryPolicyFactoryType, Constants.SurveysTableName))

             .RegisterType<IDataTable<QuestionRow>, DataTable<QuestionRow>>(new InjectionConstructor(storageAccountType, retryPolicyFactoryType, Constants.QuestionsTableName));

blob 类型也使用类似的方法:

container

              .RegisterType<IBlobContainer<List<string>>,

                EntitiesBlobContainer<List<string>>>(

                  new InjectionConstructor(storageAccountType, retryPolicyFactoryType, Constants.SurveyAnswersListsBlobName))

              .RegisterType<IBlobContainer<Tenant>,

                EntitiesBlobContainer<Tenant>>(

                  new InjectionConstructor(storageAccountType, retryPolicyFactoryType, Constants.TenantsBlobName))

              .RegisterType<IBlobContainer<byte[]>,

                FilesBlobContainer>(

                  new InjectionConstructor(storageAccountType, retryPolicyFactoryType, Constants.LogosBlobName, "image/jpeg"))

              .RegisterType<IBlobContainer<SurveyAnswer>,

                EntitiesBlobContainer<SurveyAnswer>>(

                  new InjectionConstructor(storageAccountType, retryPolicyFactoryType, typeof(String)));

这里的 blob,跟实际数据库中的 Binary Lob 无关。

除了构造函数注入外,Unity 也支持属性和方法注册。如果你使用属性注入,应该确保属性具有默认值。这个很容易忘记。

注册开放泛型

下面代码段使用稍微不同的方法注册 MessageQueue 类型:它使用 RegisterTypes 方法的一个重载。

container

             .RegisterType(

                 typeof(IMessageQueue<>),

                 typeof(MessageQueue<>),

                 new InjectionConstructor(storageAccountType, retryPolicyFactoryType, typeof(String)));

所谓“开放的泛型”,是泛型的尖括号里没有内容。

该方法使你用任何参数解析 MessageQueue 类型。下面代码段使用 SurveyAnswerStoredMessage 类型:

container.Resolve<IMessageQueue<SurveyAnswerStoredMessage>>(...);

参数覆盖

本文最开始的代码中,InjectionConstructor 构造函数的其中一个参数是 typeof(string)。如下所示:

container

              .RegisterType(

                  typeof(IMessageQueue<>),

                  typeof(MessageQueue<>),

                  new InjectionConstructor(storageAccountType, retryPolicyFactoryType, typeof(String)));

……

container

             .RegisterType<IBlobContainer<SurveyAnswer>,

               EntitiesBlobContainer<SurveyAnswer>>(

                 new InjectionConstructor(storageAccountType, retryPolicyFactoryType, typeof(String)));

容器不包括解决这种类型的注册。这提供了一个方便的方法来传递在注册时未知的参数值,容器通过 ParameterOverride 类型来创建实例。

解析类型(Resolving Types)


可以在三个地方完成注册:在初始化存储的一个单独的应用程序(a standalone application that initializes the storage),在 Web 应用程序的开始阶段(web application’s start-up phase),以及一个工厂类(factory class)。

简单解析

在简单的独立的应用程序中使用很简单:调用 RegisterTypes 方法完成注册,解析对象,然后调用它们的 Initialize 方法完成初始化工作。如下所示:

static void Main(string[] args)

{

    TextWriterTraceListener tr1 = new TextWriterTraceListener(System.Console.Out);

    Debug.Listeners.Add(tr1);

 

    using (var container = new UnityContainer())

    {

        Console.WriteLine("# Performing Registrations...");

        ContainerBootstrapper.RegisterTypes(container);

        Console.WriteLine("Container has {0} Registrations:",

              container.Registrations.Count());

        foreach (ContainerRegistration item in container.Registrations)

        {

            Console.WriteLine(item.GetMappingAsString());

        }

        Console.WriteLine();

        Console.WriteLine("# Performing type resolutions...");

        container.Resolve<ISurveyStore>().Initialize();

        container.Resolve<ISurveyAnswerStore>().Initialize();

        container.Resolve<ITenantStore>().Initialize();

 

        Console.WriteLine("Done");

        Console.ReadLine();

    }

}

Initialization 方法执行后,容器会被释放。

在一个 MVC 应用程序中解析

在 MVC 应用程序中的使用更要复杂点:应用程序配置容器,这样应用程序在启动时就会使用,之后,解析各种类型。记住,这是 ASP.NET MVC 应用程序;因此,容器必须能注入 MVC 控制器类。“Unity bootstrapper for ASP.NET MVC”NuGet package 简化了这些。当你将该包添加到你的项目后,会生成一个 UnityConfig 类,下面代码段说明该类的注册方法。你可以选择从你的应用程序文件加载 Unity 配置或直接添加注册。

using System;

using Microsoft.Practices.Unity;

using Microsoft.Practices.Unity.Configuration;

 

namespace UnityBootstrapperForMVCDemo.App_Start

{

    /// <summary>

    /// Specifies the Unity configuration for the main container.

    /// </summary>

    public class UnityConfig

    {

        #region Unity Container

        private static Lazy<IUnityContainer> container = new Lazy<IUnityContainer>(() =>

        {

            var container = new UnityContainer();

            RegisterTypes(container);

            return container;

        });

 

        /// <summary>

        /// Gets the configured Unity container.

        /// </summary>

        public static IUnityContainer GetConfiguredContainer()

        {

            return container.Value;

        }

        #endregion

 

        /// <summary>Registers the type mappings with the Unity container.</summary>

        /// <param name="container">The unity container to configure.</param>

        /// <remarks>There is no need to register concrete types such as controllers or API controllers (unless you want to 

        /// change the defaults), as Unity allows resolving a concrete type even if it was not previously registered.</remarks>

        public static void RegisterTypes(IUnityContainer container)

        {

            // NOTE: To load from web.config uncomment the line below. Make sure to add a Microsoft.Practices.Unity.Configuration to the using statements.

            // container.LoadConfiguration();

 

            // TODO: Register your types here

            // container.RegisterType<IProductRepository, ProductRepository>();

        }

    }

}

“Unity bootstrapper for ASP.NET MVC”提供一个 UnityDependencyResolver 类,该类从容器解析控制器。如果你需要为控制器类配置注入,那么你需要手动添加注册,或者向控制器类注入属性。

public class ManagementController : Controller

   {

       private readonly ITenantStore tenantStore;

 

       public ManagementController(ITenantStore tenantStore)

       {

           this.tenantStore = tenantStore;

       }

        ……

   }

在 MVC 和 WebAPI 应用程序中使用 Per Request Lifetime Manager

前面的例子展示了如何使用“Unity bootstrapper for ASP.NET MVC”NuGet package 在 MVC 应用程序中处理注册和解析控制器。该软件包还包括一个 PerRequestLifetime 管理器。该生命周期管理器使你可以创建一个已注册类型的实例,其行为就像一个 HTTP 请求范围内的单件。

如果您正在使用的ASP.NET Web API 项目,有一个“Unity bootstrapper for ASP.NET WebApi”NuGet软件包,会提供了等同的功能(搜索Unity3中的NuGet包管理器)。你可以在同一个项目中同时使用了“Unity bootstrapper for ASP.NET WebApi”和“Unity bootstrapper for ASP.NET MVC”,它们将共享同一个容器配置类。

用实时信息的解析

在设计时,你不会总知道你需要构造一个依赖的值。在下面例子中显示,用户提供一个应用程序必须在运行时必须创建的 blob 容器。例子中,类型解析发生在一个工厂类,它在注册时确定一个构造函数的参数。下面的代码示例显示了这个工厂类。

public class SurveyAnswerContainerFactory : ISurveyAnswerContainerFactory

{

    private readonly IUnityContainer unityContainer;

 

    public SurveyAnswerContainerFactory(IUnityContainer unityContainer)

    {

        Trace.WriteLine(string.Format("Called constructor in SurveyAnswerContainerFactory"), "UNITY");

 

        this.unityContainer = unityContainer;

    }

 

    public IBlobContainer<SurveyAnswer> Create(string tenant, string surveySlug)

    {

        Trace.WriteLine(string.Format("Called Create in SurveyAnswerContainerFactory with tenant={0}, surveySlug={1}", tenant, surveySlug), "UNITY");

 

        var blobContainerName = string.Format(

            CultureInfo.InvariantCulture,

            "surveyanswers-{0}-{1}",

            tenant.ToLowerInvariant(),

            surveySlug.ToLowerInvariant());

        return this.unityContainer.Resolve<IBlobContainer<SurveyAnswer>>(

            new ParameterOverride("blobContainerName", blobContainerName));

    }

}

在本例中,Resolve 方法使用一个参数覆盖,以对 blobContainerName 参数提供一个值,来构造 Entities-BlobContainer 类,该类已在容器注册,被注入到 SurveyAnswerContainerFactory 对象。

你前面看到应用程序如何在注入构造函数用 string 参数注册 IBlobContainer<Survey-Answer> 类型。如果没有参数覆盖,这个注册将失败,因为容器无法解析 string 类型。

你还可以在 ContainerBootstrapper 类看到参数覆盖的使用。本例中,参数覆盖提供 message queues 的创建。当已注册的 InjectionFactory 执行时,在解析时提供参数覆盖。

container

              .RegisterType<ISurveyAnswerStore, SurveyAnswerStore>(

                new InjectionFactory((c, t, s) => new SurveyAnswerStore(

                  container.Resolve<ITenantStore>(),

                  container.Resolve<ISurveyAnswerContainerFactory>(),

                  container.Resolve<IMessageQueue<SurveyAnswerStoredMessage>>(new ParameterOverride("queueName", Constants.StandardAnswerQueueName)),

                  container.Resolve<IMessageQueue<SurveyAnswerStoredMessage>>(new ParameterOverride("queueName", Constants.PremiumAnswerQueueName)),

                  container.Resolve<IBlobContainer<List<String>>>())));

参考资料


 

下载 MyDemo and DIwithUnitySample

下载 MyDemo and DIwithUnitySample v2(补充)

下载 Unity3

下载 Unity bootstrapper for ASP.NET MVC

下载 Unity bootstrapper for ASP.NET WebApi

 

Unity XML 配置文件(2)

.NET 用 Unity 依赖注入——概述注册和解析类型(1)的更多相关文章

  1. Unity 依赖注入之二

    1. 构造子注入 1.1 构造子注入初级代码 container.RegisterType<IMyWork, MyWork>(new InjectionConstructor(new Bo ...

  2. Unity依赖注入使用详解

    写在前面 构造器注入 Dependency属性注入 InjectionMethod方法注入 非泛型注入 标识键 ContainerControlledLifetimeManager单例 Unity注册 ...

  3. WPF PRISM开发入门二(Unity依赖注入容器使用)

    这篇博客将通过一个控制台程序简单了解下PRISM下Unity依赖注入容器的使用.我已经创建了一个例子,通过一个控制台程序进行加减乘除运算,项目当中将输入输出等都用接口封装后,结构如下: 当前代码可以点 ...

  4. C# Unity依赖注入

    简介: 控制反转:我们向IOC容器发出获取一个对象实例的一个请求,IOC容器便把这个对象实例“注入”到我们的手中,在这个过程中你不是一个控制者而是一个请求者,依赖于容器提供给你的资源,控制权落到了容器 ...

  5. Unity 依赖注入

    关于Ioc的框架有很多,比如astle Windsor.Unity.Spring.NET.StructureMap,我们这边使用微软提供的Unity做示例,你可以使用Nuget添加Unity,也可以引 ...

  6. 使用Microsoft.Practices.Unity 依赖注入

    Unity是微软Patterns & Practices团队所开发的一个轻量级的,并且可扩展的依赖注入(Dependency Injection)容器,它支持常用的三种依赖注入方式:构造器注入 ...

  7. 使用Microsoft.Practices.Unity 依赖注入 转载https://www.cnblogs.com/slardar1978/p/4205394.html

    Unity是微软Patterns & Practices团队所开发的一个轻量级的,并且可扩展的依赖注入(Dependency Injection)容器,它支持常用的三种依赖注入方式:构造器注入 ...

  8. c# Unity依赖注入WebService

    1.IOC与DI简介 IOC全称是Inversion Of Control(控制反转),不是一种技术,只是一种思想,一个重要的面相对象编程的法则,它能知道我们如何设计出松耦合,更优良的程序.传统应用程 ...

  9. Unity依赖注入使用

    构造器注入(Constructor Injection):IoC容器会智能地选择选择和调用适合的构造函数以创建依赖的对象.如果被选择的构造函数具有相应的参数,IoC容器在调用构造函数之前会自定义创建相 ...

随机推荐

  1. poj1179 环形+区间dp

    因为要用到模,所以左起点设置为0比较好 #include<iostream> #include<cstdio> #include<cstring> #define ...

  2. python简单笔记

    Remarks:python中注意缩进(Tab键或者4个空格) print(输出) 格式:print(values) 字符串.数字.变量等都可以输出: 实例: print(1)->1 print ...

  3. Java 和 C++ 的部分区别

    1. Java是解释型语言,所谓的解释型语言,就是源码会先经过一次编译,成为中间码,中间码再被解释器解释成机器码.对于Java而言,中间码就是字节码(.class),而解释器在JVM中内置了. 2. ...

  4. HDU 3980 (SG 环变成链 之前的先手变成后手)

    题意 两个人在一个由 n 个玻璃珠组成的一个圆环上玩涂色游戏,游戏的规则是: 1.每人一轮,每轮选择一个长度为 m 的连续的.没有涂过色的玻璃珠串涂色 2.不能涂色的那个人输掉游戏 Aekdycoin ...

  5. SG 大法(Sprague-Grundy函数)

    SG函数的定义: g(x) = mex ( sg(y) |y是x的后继结点 ) 其中mex(x)(x是一个自然是集合)函数是x关于自然数集合的补集中的最小值,比如x={0,1,2,4,6} 则mex( ...

  6. 天天爱跑步&&弹球

    题解: 弹球题目地址:https://www.nowcoder.com/acm/contest/113/E 后面这题 应该是天天爱跑步的加强版本 原理都是查询子树中dep[x]+f[x]的值的个数 由 ...

  7. 【BZOJ3307】雨天的尾巴

    题解: win下的10mb和linux下的好像不是很一样 明天再看看 求lca用的离线求,注意bz数组开2*n 这道题的线段树合并还是很好想的 我们只要把操作差分一下就好了 时间复杂度nlogn的 写 ...

  8. URAL - 1495 One-two, One-two 2

    URAL - 1495 这是在dp的专题里写了,想了半天的dp,其实就是暴力... 题目大意:给你一个n,问你在30位以内有没有一个只由1或2 构成的数被 n 整除,如果 有则输出最小的那个,否则输出 ...

  9. php 代码中的箭头“ ->”是什么意思

    类是一个复杂数据类型,这个类型的数据主要有属性.方法两种东西. 属性其实是一些变量,可以存放数据,存放的数据可以是整数.字符串,也可以是数组,甚至是类. 方法实际上是一些函数,用来完成某些功能. 引用 ...

  10. Srorm并发机制

    一:介绍 1.运行组件 2.并发度 就是executor数量 executor线程是物理线程 task是执行线程 二:增加并发度 三:Worker层次 1.worker工作进程上 各个Spout组件. ...