.NET 用 Unity 依赖注入——概述注册和解析类型(1)
本文内容
- 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>>>())));
参考资料
- Unity Application Block 1.2 - October 2008,该链接的内容已经过期,不再更新,但还是有一定参考价值。关于最新的 Unity 信息在 Unity Application Block site
下载 MyDemo and DIwithUnitySample
下载 MyDemo and DIwithUnitySample v2(补充)
下载 Unity bootstrapper for ASP.NET MVC
下载 Unity bootstrapper for ASP.NET WebApi
Unity XML 配置文件(2)
.NET 用 Unity 依赖注入——概述注册和解析类型(1)的更多相关文章
- Unity 依赖注入之二
1. 构造子注入 1.1 构造子注入初级代码 container.RegisterType<IMyWork, MyWork>(new InjectionConstructor(new Bo ...
- Unity依赖注入使用详解
写在前面 构造器注入 Dependency属性注入 InjectionMethod方法注入 非泛型注入 标识键 ContainerControlledLifetimeManager单例 Unity注册 ...
- WPF PRISM开发入门二(Unity依赖注入容器使用)
这篇博客将通过一个控制台程序简单了解下PRISM下Unity依赖注入容器的使用.我已经创建了一个例子,通过一个控制台程序进行加减乘除运算,项目当中将输入输出等都用接口封装后,结构如下: 当前代码可以点 ...
- C# Unity依赖注入
简介: 控制反转:我们向IOC容器发出获取一个对象实例的一个请求,IOC容器便把这个对象实例“注入”到我们的手中,在这个过程中你不是一个控制者而是一个请求者,依赖于容器提供给你的资源,控制权落到了容器 ...
- Unity 依赖注入
关于Ioc的框架有很多,比如astle Windsor.Unity.Spring.NET.StructureMap,我们这边使用微软提供的Unity做示例,你可以使用Nuget添加Unity,也可以引 ...
- 使用Microsoft.Practices.Unity 依赖注入
Unity是微软Patterns & Practices团队所开发的一个轻量级的,并且可扩展的依赖注入(Dependency Injection)容器,它支持常用的三种依赖注入方式:构造器注入 ...
- 使用Microsoft.Practices.Unity 依赖注入 转载https://www.cnblogs.com/slardar1978/p/4205394.html
Unity是微软Patterns & Practices团队所开发的一个轻量级的,并且可扩展的依赖注入(Dependency Injection)容器,它支持常用的三种依赖注入方式:构造器注入 ...
- c# Unity依赖注入WebService
1.IOC与DI简介 IOC全称是Inversion Of Control(控制反转),不是一种技术,只是一种思想,一个重要的面相对象编程的法则,它能知道我们如何设计出松耦合,更优良的程序.传统应用程 ...
- Unity依赖注入使用
构造器注入(Constructor Injection):IoC容器会智能地选择选择和调用适合的构造函数以创建依赖的对象.如果被选择的构造函数具有相应的参数,IoC容器在调用构造函数之前会自定义创建相 ...
随机推荐
- poj1179 环形+区间dp
因为要用到模,所以左起点设置为0比较好 #include<iostream> #include<cstdio> #include<cstring> #define ...
- python简单笔记
Remarks:python中注意缩进(Tab键或者4个空格) print(输出) 格式:print(values) 字符串.数字.变量等都可以输出: 实例: print(1)->1 print ...
- Java 和 C++ 的部分区别
1. Java是解释型语言,所谓的解释型语言,就是源码会先经过一次编译,成为中间码,中间码再被解释器解释成机器码.对于Java而言,中间码就是字节码(.class),而解释器在JVM中内置了. 2. ...
- HDU 3980 (SG 环变成链 之前的先手变成后手)
题意 两个人在一个由 n 个玻璃珠组成的一个圆环上玩涂色游戏,游戏的规则是: 1.每人一轮,每轮选择一个长度为 m 的连续的.没有涂过色的玻璃珠串涂色 2.不能涂色的那个人输掉游戏 Aekdycoin ...
- SG 大法(Sprague-Grundy函数)
SG函数的定义: g(x) = mex ( sg(y) |y是x的后继结点 ) 其中mex(x)(x是一个自然是集合)函数是x关于自然数集合的补集中的最小值,比如x={0,1,2,4,6} 则mex( ...
- 天天爱跑步&&弹球
题解: 弹球题目地址:https://www.nowcoder.com/acm/contest/113/E 后面这题 应该是天天爱跑步的加强版本 原理都是查询子树中dep[x]+f[x]的值的个数 由 ...
- 【BZOJ3307】雨天的尾巴
题解: win下的10mb和linux下的好像不是很一样 明天再看看 求lca用的离线求,注意bz数组开2*n 这道题的线段树合并还是很好想的 我们只要把操作差分一下就好了 时间复杂度nlogn的 写 ...
- URAL - 1495 One-two, One-two 2
URAL - 1495 这是在dp的专题里写了,想了半天的dp,其实就是暴力... 题目大意:给你一个n,问你在30位以内有没有一个只由1或2 构成的数被 n 整除,如果 有则输出最小的那个,否则输出 ...
- php 代码中的箭头“ ->”是什么意思
类是一个复杂数据类型,这个类型的数据主要有属性.方法两种东西. 属性其实是一些变量,可以存放数据,存放的数据可以是整数.字符串,也可以是数组,甚至是类. 方法实际上是一些函数,用来完成某些功能. 引用 ...
- Srorm并发机制
一:介绍 1.运行组件 2.并发度 就是executor数量 executor线程是物理线程 task是执行线程 二:增加并发度 三:Worker层次 1.worker工作进程上 各个Spout组件. ...