.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容器在调用构造函数之前会自定义创建相 ...
随机推荐
- liunx centox ssh 配置
https://www.cnblogs.com/xubing-613/p/6844564.html 一. 查看是否安装了ssh: rpm -qa | grep ssh 重启ssh service s ...
- python3 + selenium 多iframe(框架)切换
html演示: frame.html: <html> <head> <meta http-equiv="content-type" content=& ...
- 全局JavaScript对象
- Js引擎解析执行 阅读笔记
Js引擎解析执行 阅读笔记 一篇阅读笔记 http://km.oa.com/group/2178/articles/show/145691?kmref=search&from_page=1&a ...
- AOJ 2249 Road Construction (dijkstra)
某国王需要修路,王国有一个首都和多个城市,需要修路.已经有修路计划了,但是修路费用太高. 为了减少修路费用,国王决定从计划中去掉一些路,但是需要满足一下两点: 保证所有城市都能连通 所有城市到首都的最 ...
- Vijos1910 NOIP2014提高组 Day2T3 解方程 其他
欢迎访问~原文出处——博客园-zhouzhendong 去博客园看该题解 题目传送门 - Vijos1910 题意概括 已知多项式方程: a0+a1x+a2x2+...+anxn=0 求这个方程在[1 ...
- java导出数据EXCEL的工具类(以spring-webmvc-4.0.4jar为基础)
1.本工具类继承于 spring-webmvc-4.0.4jar文件心中的一个类 AbstractExcelView 2.代码如下 package com.skjd.util; import j ...
- 【目录】LeetCode Java实现
这里记录一下自己刷的LeetCode题目. 有些博客用英文阐述自己的思路和收获,相当于练习一下英文的表达能力. 比较好的题目有加粗. 1. Two Sum 3. Longest Substring W ...
- jstl标签比较格式化后的时间
c:set 里面不支持任何标签,这样写不好讲格式化的值放到bdateVar里面 <c:set var="bdateVar" value="<fmt:forma ...
- BZOJ.4817.[SDOI2017]树点涂色(LCT DFS序 线段树)
题目链接 操作\(1.2\)裸树剖,但是操作\(3\)每个点的答案\(val\)很不好维护.. 如果我们把同种颜色的点划分到同一连通块中,那么向根染色的过程就是Access()! 最初所有点间都是虚边 ...