在介绍如何在程序中使用Unity之前,首先说一下什么是IOC:
IOC是Inversion of Control的缩写,被翻译为控制反转,是一种全新的设计模式,用来削减计算机程序的耦合问题,把程序上层对下层的依赖,转移到第三方的容器来装配。控制反转一般分为两种类型,依赖注入(Dependency Injection,简称DI)和依赖查找(Dependency Lookup)。依赖注入应用比较广泛。
废话不多说上直接上干货,项目搭建和运行情况
一.创建项目UnityIOC解决方案二.引入包是4个,其他的文章只引入了3前三个,发现到最后做配置文件管理的时候会报异常,原因是没有引入第四个包,一定要全部引入。Unity
unity Interception
Unity.Configuration
Unity.Interception.Configuration
首先来看下面的例子:
1、定义一个接口,封装数据库的基本CRUD操作,接口定义如下:
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 using System.Data; 7 8 namespace DataBase.Interface 9 {10 /// 11 /// 数据访问接口12 /// 13 public interface IDbInterface14 {15 string Insert();16 string Delete();17 string Update();18 string Query();19 }20 }
2、定义一个MSSQL类实现该接口,用来模仿SQLServer操作,MSSQL类定义如下:
1 using DataBase.Interface; 2 using System; 3 using System.Collections.Generic; 4 using System.Linq; 5 using System.Text; 6 using System.Threading.Tasks; 7 8 namespace DataBase.MSSQL 9 {10 public class DbMSSQL : IDbInterface11 {12 public string Delete()13 {14 return "MSSQL执行删除";15 }16 17 public string Insert()18 {19 return "MSSQL执行插入";20 }21 22 public string Query()23 {24 return "MSSQL执行查询";25 }26 27 public string Update()28 {29 return "MSSQL执行更新";30 }31 }32 }
3、定义一个Oracle类实现该接口,模仿Oracle的操作,Oracle类定义如下:
1 using DataBase.Interface; 2 using System; 3 using System.Collections.Generic; 4 using System.Linq; 5 using System.Text; 6 using System.Threading.Tasks; 7 8 namespace DataBase.Oracle 9 {10 public class DbOracle : IDbInterface11 {12 public string Delete()13 {14 return "Oracle执行删除";15 }16 17 public string Insert()18 {19 return "Oracle执行插入";20 }21 22 public string Query()23 {24 return "Oracle执行查询";25 }26 27 public string Update()28 {29 return "Oracle执行更新";30 }31 }32 }
4、定义一个控制台应用程序来调用:
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 using DataBase.Interface; 7 using DataBase.MSSQL; 8 9 namespace IOCConApp10 {11 class Program12 {13 static void Main(string[] args)14 {15 // 常规做法,即程序的上端,依赖于下端,依赖于细节16 DbMSSQL mssql = new DbMSSQL();17 }18 }19 }
常规做法是添加引用,然后直接实例化类,但是这样会依赖于细节实现,现将代码修改如下:
1 2 |
|
但是这样修改以后,虽然左边是抽象了,但是右边还是依赖于细节。
那就究竟什么是IOC呢?
IOC(Inversion of Control)即控制反转,是一个重要的面向对象编程的法则来消减程序之间的耦合问题,把程序中上层对下层依赖,转移到一个第三方容器中来装配。IOC是程序设计的目标,实现方式包含依赖注入和依赖查找,在.net中只有依赖注入。
说到IOC,就不能不说DI。DI:即依赖注入,是IOC的实现手段。
二、使用Unity实现IOC
Unity是一个IoC容器,用来实现依赖注入(Dependency Injection,DI),减少耦合的,Unity出自于伟大的微软。
unity组件网址:http://unity.codeplex.com/
unity能够做什么呢,列举部分如下:
1.Unity支持简单对象创建,特别是分层对象结构和依赖,以简化程序代码。其包含一个编译那些可能存在依赖于其他对象的对象实例机制。
2.Unity支持必要的抽象,其允许开发者在运行时或配置去指定依赖关系同时可以简单的管理横切点(AOP)。
3.Unity增加了推迟到容器组件配置的灵活性。其同样支持一个容器层次的结构。
4.Unity拥有服务定位能力,对于一个程序在许多情况下重复使用组件来分离和集中功能是非常有用的。
5.Unity允许客户端储存或缓存容器。对于在ASP.NET Web applications中开发者将容器持久化于ASP.NET中的session或application中特别有效。
6.Unity拥有拦截能力,其允许开发者通过创建并执行handlers(在方法或属性被调用到达之前)来为已存在的组件增加一个函数,并再次为返回调用结果。
7.Unity可以从标准配置系统中读取配置信息,例如:XML文件,同时使用配置文件来配置容器。
8.Unity支持开发者实现自定义容器扩展,例如:你可以实现方法来允许额外的对象构造和容器特征,例如缓存。
9.Unity允许架构师和开发者在现代化的程序中更简单的实现通用设计模式。
什么情况下要使用unity呢?
1.所构建的系统依赖于健全的面向对象原则,但是大量不同的代码交织在一起而难以维护。
2.构建的对象和类需要依赖其他对象或类。
3.依赖于复杂的或需要抽象的对象。
4.希望利用构造函数、方法或属性的调用注入优势。
5.希望管理对象实例的生命周期。
6.希望能够在运行时管理并改变依赖关系。
7.希望在拦截方法或属性调用的时候生成一个策略链或管道处理容器来实现横切(AOP)任务。
8.希望在Web Application中的回发操作时能够缓存或持久化依赖关系。
1、程序中安装Unity
使用管理NuGet程序包来安装Unity,在项目上右键,选择管理NuGet程序包:
在搜索框里面输入Unity,点击右侧安装按钮进行安装:
出现以下信息表示安装成功:
2、使用Unity实现DI
先来看看最简单的Unity实现方式:
IUnityContainer container = new UnityContainer();//1、定义一个空容器
container.RegisterType();//2、注册类型,表示遇到IDbInterface的类型,创建DbMSSQL的实例var db = container.Resolve();
Console.WriteLine(db.Insert());
Console.ReadKey();
结果:
从结果中可以看出,db是DbMSSQL类型的实例。
除了使用RegisterType注册类型以外,还可以注册一个实例,例如:
// 使用RegisterInstance注册IDbInterface的实例:new DbMSSQL()
container.RegisterInstance(new DbMSSQL());
3、三种注入方式
三种注入方式:构造函数注入、属性注入、方法注入。
3.1 定义IHeadphone接口,代码如下:
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 namespace DataBase.Interface 8 { 9 public interface IHeadphone10 {11 12 }13 }
3.2 定义IMicrophone接口,代码如下:
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 namespace DataBase.Interface 8 { 9 public interface IMicrophone10 {11 12 }13 }
3.3 定义IPower接口,代码如下:
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 namespace DataBase.Interface 8 { 9 public interface IPower10 {11 12 }13 }
3.4 定义IPhone接口,代码如下:
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 namespace DataBase.Interface 8 { 9 public interface IPhone10 {11 void Call();12 IMicrophone iMicrophone { get; set; }13 IHeadphone iHeadphone { get; set; }14 IPower iPower { get; set; }15 }16 }
3.5 分别实现上面定义的接口
IPhone接口的实现如下:
1 using DataBase.Interface; 2 using System; 3 using System.Collections.Generic; 4 using System.Linq; 5 using System.Text; 6 using System.Threading.Tasks; 7 using Unity.Attributes; 8 9 namespace DataBase.MSSQL10 {11 public class ApplePhone : IPhone12 {13 [Dependency]//属性注入14 public IMicrophone iMicrophone { get; set; }15 public IHeadphone iHeadphone { get; set; }16 public IPower iPower { get; set; }17 18 [InjectionConstructor]//构造函数注入19 public ApplePhone(IHeadphone headphone)20 {21 this.iHeadphone = headphone;22 Console.WriteLine("{0}带参数构造函数", this.GetType().Name);23 }24 25 public void Call()26 {27 Console.WriteLine("{0}打电话", this.GetType().Name); ;28 }29 30 [InjectionMethod]//方法注入31 public void Init1234(IPower power)32 {33 this.iPower = power;34 }35 }36 }
IHeadphone接口的实现如下:
1 using DataBase.Interface; 2 using System; 3 using System.Collections.Generic; 4 using System.Linq; 5 using System.Text; 6 using System.Threading.Tasks; 7 8 namespace DataBase.MSSQL 9 {10 public class Headphone : IHeadphone11 {12 public Headphone()13 {14 Console.WriteLine("Headphone 被构造");15 }16 }17 }
IMicrophone接口的实现如下:
1 using DataBase.Interface; 2 using System; 3 using System.Collections.Generic; 4 using System.Linq; 5 using System.Text; 6 using System.Threading.Tasks; 7 8 namespace DataBase.MSSQL 9 {10 public class Microphone : IMicrophone11 {12 public Microphone()13 {14 Console.WriteLine("Microphone 被构造");15 }16 }17 }
IPower接口的实现如下:
1 using DataBase.Interface; 2 using System; 3 using System.Collections.Generic; 4 using System.Linq; 5 using System.Text; 6 using System.Threading.Tasks; 7 8 namespace DataBase.MSSQL 9 {10 public class Power : IPower11 {12 public Power()13 {14 Console.WriteLine("Power 被构造");15 }16 }17 }
控制台程序调用:
using Microsoft.Practices.Unity.Configuration;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Unity;
using Unity.Lifetime;
using Unity.Interception.Configuration;
using UnityIOC.Interface;
using UnityIOC.MSSQL;
using UnityIOC.Oracle;
namespace UnityIOC
{
///
/// IOC():控制反转,把程序上层对下层的依赖,转移到第三方的容器来装配
/// 是程序设计的目标,实现方式包含了依赖注入和依赖查找(.net里面只有依赖注入)
/// DI:依赖注入,是IOC的实习方式。
/// Unity
///unity Interception
///Unity.Configuration
/// Unity.Interception.Configuration
///
class Program
{
static void Main(string[] args)
{
#region 没有使用依赖注入之前
string str = string.Empty;
// 1 常规做法,即程序的上端,依赖于下端,依赖于细节
DbMSSQL mssql = new DbMSSQL();
str = mssql.Delete();
Console.WriteLine(str);
// 2 通过抽象来依赖
IDbInterface dbInterface = new DbMSSQL();
str = dbInterface.Insert();
Console.WriteLine(str);
#endregion
#region 简单使用Unity实现DI
IUnityContainer container = new UnityContainer();//1、定义一个空容器
container.RegisterType();//2、注册类型,表示遇到IDbInterface的类型,创建DbMSSQL的实例 第一种方式注入
// 使用RegisterInstance注册IDbInterface的实例:new DbMSSQL()
//container.RegisterInstance(new DbMSSQL());//2、注册类型,表示遇到IDbInterface的类型,创建DbMSSQL的实例 第二种方式注入
var db = container.Resolve(); // db是DbMSSQL类型的实例
Console.WriteLine(db.Insert());
#endregion
#region 三种注入方式:构造函数注入、属性注入、方法注入
container = new UnityContainer();//声明一个新的容器
container.RegisterType();//开启注册,applephone 是 Iphone 的实现类
container.RegisterType();// 开启注册,Microphone 是 IMicrophone 的实现类
container.RegisterType();// 开启注册,Headphone 是 IHeadphone 的实现类
container.RegisterType(); //开启注册,Power 是 IPower 的实现类
IPhone phone = container.Resolve(); //解决,去实现接口类的方法内容
phone.Call();
Console.WriteLine($"phone.iHeadphone==null? {phone.iHeadphone == null}");
Console.WriteLine($"phone.iMicrophone==null? {phone.iMicrophone == null}");
Console.WriteLine($"phone.iPower==null? {phone.iPower == null}");
//从输出结果中可以看出三种注入方式的执行顺序:先执行构造函数注入,在执行属性注入,最后执行方法注入。
//注意:默认情况下如果构造函数上面没有使用特性,那么默认找参数最多的构造函数执行注入。
#endregion
#region 一个接口多个实现进行注册
container = new UnityContainer();//1、定义一个空容器
container.RegisterType();//2、注册类型,表示遇到IDbInterface的类型,创建DbMSSQL的实例
container.RegisterType();//表示遇到IDbInterface的类型,创建DbMSSQL的实例
db = container.Resolve();
Console.WriteLine(db.Insert());
//从运行结果中可以看出,后面注册的类型会把前面注册的类型给覆盖掉,那么该如何解决呢?可以通过参数的方式来解决,代码如下
container = new UnityContainer();//1、定义一个空容器
container.RegisterType("sql");//2、注册类型,表示遇到IDbInterface的类型,创建DbMSSQL的实例
container.RegisterType("oracle");//表示遇到IDbInterface的类型,创建DbMSSQL的实例
var sql = container.Resolve("sql");
var oracle = container.Resolve("oracle");
Console.WriteLine(sql.Insert());
Console.WriteLine(oracle.Insert());
#endregion
#region 生命周期
container = new UnityContainer();
container.RegisterType();
IDbInterface db1 = container.Resolve();
IDbInterface db2 = container.Resolve();
Console.WriteLine("HashCode:" + db1.GetHashCode().ToString());
Console.WriteLine("HashCode:" + db2.GetHashCode().ToString());
Console.WriteLine(object.ReferenceEquals(db1, db2));
//表明db1和db2是两个不同的实例,即默认情况下生命周期是瞬时的,每次都是创建一个新的实例。
//container.RegisterType(new TransientLifetimeManager()); 表示是瞬时生命周期,默认情况下即这种。
container = new UnityContainer();
container.RegisterType(new ContainerControlledLifetimeManager());
IDbInterface db3 = container.Resolve();
IDbInterface db4 = container.Resolve();
Console.WriteLine("HashCode:" + db3.GetHashCode().ToString());
Console.WriteLine("HashCode:" + db4.GetHashCode().ToString());
Console.WriteLine(object.ReferenceEquals(db3, db4));
//上图的结果可以看出,db1和db2是同一个实例。
//container.RegisterType(new ContainerControlledLifetimeManager())表示是容器单例,每次都是同一个实例。
#endregion
#region 使用配置文件实现
//在上面的例子中,所有的例子都是一直在依赖于细节,那么怎么解决不依赖于细节呢?答案是只能使用配置文件,配置文件如下:
ExeConfigurationFileMap fileMap = new ExeConfigurationFileMap();
fileMap.ExeConfigFilename = Path.Combine(AppDomain.CurrentDomain.BaseDirectory + "\\unity.config");//找配置文件的路径
Configuration configuration = ConfigurationManager.OpenMappedExeConfiguration(fileMap, ConfigurationUserLevel.None);
UnityConfigurationSection section = (UnityConfigurationSection)configuration.GetSection(UnityConfigurationSection.SectionName);
container = new UnityContainer();
section.Configure(container, "testContainer");
IDbInterface db5 = container.Resolve("sql");
Console.WriteLine(db.Insert());
#endregion
Console.ReadLine();
}
}
}
结果:
更多技术请关注