使用Microsoft的IoC框架:Unity来对.NET应用进行解耦
1、IoC/DI简介
IoC 即 Inversion of Control,DI 即 Dependency Injection,前一个中文含义为控制反转,后一个译为依赖注入,可以理解成一种编程模式,详细的说明可参见大牛Martin Fowler的强文 http://martinfowler.com/articles/injection.html,借用Hollywood的名言:Don't call us, we'll call you,意即你呆着别动,到时我会找你。控制反转的核心是控制权的转移,从原有的应用程序转移到框架如IoC容器,从而实现模块间的解耦。
2、Unity是什么?
Unity是微软Patterns & Practices团队所开发的一个轻量级的,并且可扩展的依赖注入(Dependency Injection)容器,它支持常用的三种依赖注入方式:构造器注入(Constructor Injection)、属性注入(Property Injection),以及方法调用注入(Method Call Injection)。现在Unity最新的版本的3.5版,可以在微软的开源站点:https://github.com/unitycontainer/unity下载最新的发布版本和文档。
它有助于构建松耦合的应用程序和为开发者提供以下便利:
- 简化对象的创建,特别在分层对象结构和依赖的情形下;
- 它支持需求的抽象化,这允许开发人员在运行时或在配置文件中指定依赖,简化横切关注点(crosscutting concerns)的管理;
- 它通过把组件配置推给容器来决定,增加了灵活性;
- 服务定位能力; 这使客户端能够存储或缓存容器;
- 轻松构建松耦合结构的程序,从而让整个程序框架变得清晰和易于维护。
3、如何使用Unity?
下面我们用一个简单的例子来演示如何使用Ioc框架:Unity。我们的大多数应用程序都是由两个或是更多的类通过彼此的合作来实现业务逻辑,这使得每个对象都需要获取与其合作的对象(也就是它所依赖的对象)的引用。如果这个获取过程要靠自身实现,那么这将导致代码高度耦合并且难以维护和调试。使用Unity就是要解决这个这个问题。
Martin Fowler在那篇著名的文章《Inversion of Control Containers and the Dependency Injection pattern》中将具体依赖注入划分为三种形式,即构造器注入、属性(设置)注入和接口注入,习惯将其划分为一种(类型)匹配和三种注入:
- 类型匹配(Type Matching):虽然我们通过接口(或者抽象类)来进行服务调用,但是服务本身还是实现在某个具体的服务类型中,这就需要某个类型注册机制来解决服务接口和服务实现类型之间的匹配关系;
- 构造器注入(Constructor Injection):IoC容器会智能选择选择和调用适合的构造函数以创建依赖的对象。如果被选择的构造函数具有相应的参数,IoC容器在调用构造函数之前解析注册的依赖关系并自行获得相应参数对象;
- 属性注入(Property Injection):如果需要使用到被依赖对象的某个属性,在被依赖对象被创建之后,IoC容器会自动初始化该属性;
- 方法注入(Method Injection):如果被依赖对象需要调用某个方法进行相应的初始化,在该对象创建之后,IoC容器会自动调用该方法。
依赖翻转的核心原则:
1、高层模块不应该依赖底层模块,两个都应该依赖抽象(抽象类或接口);
2、抽象不应该依赖细节,细节应该依赖抽象。
下面我采用“属性注入(Property Injection)”的方式演示如何使用Unity框架。
3.1、定义接口类
一个简单日志记录接口类,用于记录请求的报文。
public interface ILogger
{
Tuple<string, string> GetLogContent(AtomRequest request, AtomResponse response = null);
void Log(Tuple<string, string> logInfo);
}
3.2、定义接口的实现类
定义2个接口的实现类,分别用来实现对请求的响应的报文进行日志记录。
internal class LogRequest : ILogger
{
public Tuple<string, string> GetLogContent(AtomRequest request, AtomResponse response = null)
{
var reqType = request.GetType().Name;
switch (reqType)
{
case "AtomQueryRequest":
{
var req = (AtomQueryRequest) request;
return Tuple.Create(request.CollectionId, string.Format("流水号:{0} 交易类型:{1}", req.TransNo, req.TransType));
}
case "AtomSaleRequest":
{
var req = (AtomSaleRequest) request;
return Tuple.Create(req.CollectionId, string.Format("流水号:{0} 订单号:{1} 金额:{2}", req.TransNo, req.OrderNo, req.LocalAmount));
}
default:
{
throw new CheckRequestException("无效的交易类型:".Contact(reqType));
}
}
} public void Log(Tuple<string, string> logInfo)
{
LogManager.InfoRequest(logInfo.Item1, logInfo.Item2);
}
}
3.3、定义容器并注册接口及接口实现类之间的映射关系
项目引用:Microsoft.Practices.Unity.dll
internal class ServiceContainer
{
// 核心容器类型的定义,设置为静态的,公开的,可为其它任何类型调用。
public static UnityContainer RtpContainer; // 初始化容器并在容器中注册项目程序中所有的依赖关系
static ServiceContainer()
{
RtpContainer = new UnityContainer(); // 每次调用,容器都会生成一个新的对象实例
RtpContainer.RegisterType<IResponseProcessor, ResponseProcessor>(); // 注册为单例,任何时候调用都使用同一个对象实例
RtpContainer.RegisterType<ILogger, LogRequest>("Request", new ContainerControlledLifetimeManager());
RtpContainer.RegisterType<ILogger, LogResponse>("Response", new ContainerControlledLifetimeManager());
}
}
什么时候注册为单例,我的个人标准为:实现类,比如:LogRequest类,没有静态成员或者静态成员没有并发写的可能都可以用,使用单例可以减少频繁创建对象可能造成的开销。在注册类型时,如果要注册为单例模式,额外传入一个:new ContainerControlledLifetimeManager() 参数即可,表示创建对象的生命周期由容器来控制。另外如果一个接口由多个实现类,如上面的LogRequest和LogResponse都实现了ILogger接口。这样在注册map映射关系时,需要额外使用一个name参数(比如上面的“Request”,“Response”)来唯一标识map关系。使用XML也可以实现依赖关系的注册,但我更倾向于使用:约定优于配置(convention over configuration)的原则,也称作按约定编程,是一种软件设计范式,旨在减少软件开发人员需做决定的数量,获得简单的好处,而又不失灵活性。
3.4、依赖注入
internal abstract class AbstractRequest
{
static AbstractRequest()
{
JsConfig.EmitCamelCaseNames = true;
JsConfig.IncludeNullValues = true;
} [Dependency("Request")]
public ILogger Logger { get; set; } [Dependency]
public IRequestCheck RequestCheck { get; set; } protected virtual void CheckRequest(AtomRequest request)
{
if (null == request)
{
throw new ArgumentNullException("request", "请求实体不能为NULL");
}
}
}
上面的代码我采用了“属性注入(Property Injection)”的方式注入了一个Logger属性,并且Dependency属性类的name参数为:Request,标识当AbstractRequest的实现类被实例化时,IoC容器自动初始化该Logger属性为一个LogRequest对象实例。
3.5、使用注入的属性
internal class ProcessAtomSaleRequest : AbstractRequest, IAtomRequest
{
[Dependency("AtomSale")]
public MessageProviderFactory MessageProviderFactory { get; set; } public object Execute(AtomRequest request)
{
CheckRequest(request); // 使用AbstractRequest类中注入的属性Logger,ProcessAtomSaleRequest被实例化时Logger属性自动被初始化为LogRequest实例对象
Logger.Log(Logger.GetLogContent(request));
}
}
3.6、使用容器来解析并创建对象实例
public class AtomSale : AtomTransaction
{
protected override object ProcessRequest(AtomRequest request)
{
return ((IAtomRequest)ServiceContainer.RtpContainer.Resolve(typeof(IAtomRequest), "AtomSale")).Execute(request);
}
}
ServiceContainer.RtpContainer就是我们前面定义的静态的、公共的容器类(类型为:UnityContainer),在第一次被调用时初始化。Resolve方法通过指定抽象类型及对应的name属性来确定唯一映射关系并创建对象,最后执行对象的Execute方法。
总结:
使用IoC框架后,使用相同架构模型的应用,其高层抽象可以完全移植,只须关注实现类的业务细节即可,业务逻辑修改时也只须改动实现的部分,这样就实现了抽象层和实现层的分离,同样对象创建职责也做了转移。系统解耦或松耦合也就顺理成章了。如果所有代码都揉合在一起,任何代码的修改都可能对其它代码造成影响,如果没能细致和全面的回归测试,线上故障也难免发生。
帮助到您了吗?
打赏作者(支付宝):

使用Microsoft的IoC框架:Unity来对.NET应用进行解耦的更多相关文章
- .NET MVC4 实训记录之一(引入Unity3.0 Ioc框架)
一直在做维护项目,没有机会接触完整的架构,于是自学.NET MVC.自今日起,将自学的过程.遇到的问题以及解决方案记录下来. 在WebApp项目中右键,使用NuGet引入Unity3.0.
- .Net常见的IOC框架及AOP框架
IOC框架 Unity:微软patterns&practicest团队开发的IOC依赖注入框架,支持AOP横切关注点. MEF(Managed Extensibility Framework) ...
- .Net IOC 框架
CastleWindsor 参见:CastleWindsor | .Net IOC 框架 AutoFace 参见:AutoFace | .Net IOC 框架 Unity 参见:Unity | .Ne ...
- .Net IOC框架入门之一 Unity
一.概述 IOC:英文全称:Inversion of Control,中文名称:控制反转,它还有个名字叫依赖注入(Dependency Injection). 作用:将各层的对象以松耦合的方式组织在一 ...
- .Net IOC框架入门之——Unity
一.概述 IOC:英文全称:Inversion of Control,中文名称:控制反转,它还有个名字叫依赖注入(Dependency Injection). 作用:将各层的对象以松耦合的方式组织在一 ...
- 依赖反转Ioc和unity,autofac,castle框架教程及比较
1.依赖倒置的相关概念 http://www.cnblogs.com/fuchongjundream/p/3873073.html IoC模式(依赖.依赖倒置.依赖注入.控制反转) 2.依赖倒置的方式 ...
- MVC4 基于 Unity Ioc 框架的 ControllerFactory
首先引入Untiy框架. 可以在NuGet程序包 管理器中直接安装. 新建 继承DefaultControllerFactory 的UnityControllerFactory: 重写虚方法GetC ...
- 今天研究Unity Ioc 框架
今天研究Unity Ioc 框架,被自己坑了两个多小时. 运行就报错,反反复复检查了很多次,配置文件,代码都没有问题,也从新写了好几遍. 最后仔细看报错消息才知道,config文件没有生成到目录……… ...
- ASP.NET MVC IOC之Unity攻略
ASP.NET MVC IOC之Unity攻略 一.你知道IOC与DI吗? 1.IOC(Inversion of Control )——控制反转 即依赖对象不在被依赖模块的类中直接通过new来获取 先 ...
随机推荐
- 如何一步一步用DDD设计一个电商网站(五)—— 停下脚步,重新出发
阅读目录 前言 单元测试 纠正错误,重新出发 结语 一.前言 实际编码已经写了2篇了,在这过程中非常感谢有听到观点不同的声音,借着这个契机,今天这篇就把大家提出的建议一个个的过一遍,重新整理,重新出发 ...
- 深入理解C#
简单认识.NET框架 (1)首先我们得知道 .NET框架具有两个主要组件:公共语言进行时CLR(Common Language Runtime)和框架类库FCL(Framework Class ...
- ASP.NET MVC5+EF6+EasyUI 后台管理系统(55)-Web打印
系列目录 前言 1.本次主要弥补工作流,用户表单数据的打印 2.使用JQprint做为web打印插件 3.兼容:FireFox,Chrome,IE. 4.没有依赖也没有配置,使用简单 代码下载:htt ...
- pt-pmp
pt-pmp有两方面的作用:一是获取进程的堆栈信息,二是对这些堆栈信息进行汇总. 进程的堆栈信息是利用gdb获取的,所以在获取的过程中,会对mysql服务端的性能有一定的影响. 用官方的话说: Thi ...
- zookeeper源码分析之四服务端(单机)处理请求流程
上文: zookeeper源码分析之一服务端启动过程 中,我们介绍了zookeeper服务器的启动过程,其中单机是ZookeeperServer启动,集群使用QuorumPeer启动,那么这次我们分析 ...
- Oracle 数据库语句大全
Oracle数据库语句大全 ORACLE支持五种类型的完整性约束 NOT NULL (非空)--防止NULL值进入指定的列,在单列基础上定义,默认情况下,ORACLE允许在任何列中有NULL值. CH ...
- 深入理解 Android 之 View 的绘制流程
概述 本篇文章会从源码(基于Android 6.0)角度分析Android中View的绘制流程,侧重于对整体流程的分析,对一些难以理解的点加以重点阐述,目的是把View绘制的整个流程把握好,而对于特定 ...
- 自建git node pm2 (不赘述,就说遇见的问题)
//======================[git]部分 主题部分还是按照网上的办法进行安装. 安装的话 分为两个办法(一个是yum (contos办法) 或者sudo(ubuntu办法) ...
- 全网独家MongoDB Certified DBA Associate考试认证视频
该视频意在让所有学员一次通过考试,避免重复考试而承担的巨额考试费用! 目前MongDB发展迅猛,有赶超mysql,和oracle看齐的苗头.在这个时候MongoDB也适时的推出了官方的认证考试&quo ...
- jQuery radio的取值与赋值
取值: $("input[name='radioName']:checked").val(); 赋值: $("input[name='radioName'][value= ...