c#中的Unity容器
DIP是依赖倒置原则:一种软件架构设计的原则(抽象概念)。依赖于抽象不依赖于细节
IOC即为控制反转(Inversion of Control):传统开发,上端依赖(调用/指定)下端对象,会有依赖,把对下端对象的依赖转移到第三方容器(工厂+配置文件+反射),能够程序拥有更好的扩展性,是DIP的具体实现方式,可以用来减低计算机代码之间的耦合度。
DI 即为依赖注入(Dependency Injection):
- 是实现IOC的手段和方法,就是能做到构造某个对象时,将依赖的对象自动初始化并注入 :
- 有三种注入方式:构造函数注入--属性注入--方法注入(按时间顺序):
- 构造函数注入用的最多,默认找参数最多的构造函数,可以不用特性,可以去掉对容器的依赖
Unity容器:是微软推出的IOC框架,使用这个框架,可以实现AOP面向切面编程,便于代码的后期维护,此外,这套框架还自带单例模式,可以提高程序的运行效率。
上面介绍了一下Unity,IOC,DI的概念,那我们项目中什么时候会使用Unity呢,总结分析得到如下情况:
- 所构建的系统依赖于健全的面向对象原则,但是大量不同的代码交织在一起而难以维护。
- 构建的对象和类需要依赖其他对象或类。
- 依赖于复杂的或需要抽象的对象。
- 希望利用构造函数、方法或属性的调用注入优势。
- 希望管理对象实例的生命周期。
- 希望能够在运行时管理并改变依赖关系。
- 希望在拦截方法或属性调用的时候生成一个策略链或管道处理容器来实现横切(AOP)任务。
- 希望在Web Application中的回发操作时能够缓存或持久化依赖关系。
使用Unity的好处:
- Unity支持简单对象创建,特别是分层对象结构和依赖,以简化程序代码。其包含一个编译那些可能存在依赖于其他对象的对象实例机制。
- Unity支持必要的抽象,其允许开发者在运行时或配置去指定依赖关系同时可以简单的管理横切点(AOP)。
- Unity增加了推迟到容器组件配置的灵活性。其同样支持一个容器层次的结构。
- Unity拥有服务定位能力,对于一个程序在许多情况下重复使用组件来分离和集中功能是非常有用的。
- Unity允许客户端储存或缓存容器。对于在ASP.NET Web applications中开发者将容器持久化于ASP.NET中的session或application中特别有效。
- Unity拥有拦截能力,其允许开发者通过创建并执行handlers(在方法或属性被调用到达之前)来为已存在的组件增加一个函数,并再次为返回调用结果。
- Unity可以从标准配置系统中读取配置信息,例如:XML文件,同时使用配置文件来配置容器。
- Unity支持开发者实现自定义容器扩展,例如:你可以实现方法来允许额外的对象构造和容器特征,例如缓存。
- Unity允许架构师和开发者在现代化的程序中更简单的实现通用设计模式
1 namespace Entity 2 { 3 public class User 4 { 5 public string UserName { set; get; } 6 public int UserId { set; get; } 7 public string Password { get; set; } 8 } 9 } 10 namespace Entity 11 { 12 public class Role 13 { 14 public int Id { set; get; } 15 } 16 } 17 namespace Entity 18 { 19 public class Department 20 { 21 public int Id { set; get; } 22 public string Name { get; set; } 23 } 24 } 25 namespace Entity 26 { 27 public class Company 28 { 29 public string CompanyName { set; get; } 30 public int CompanyId { set; get; } 31 } 32 }
2:接口类,程序集和命名空间都是:IService
1 namespace IService 2 { 3 public interface ICompany 4 { 5 void RegCompany(Company company); 6 } 7 } 8 9 namespace IService 10 { 11 public interface IRepository<T> 12 { 13 int Insert(T t); 14 T LoadById(int id); 15 } 16 } 17 namespace IService 18 { 19 public interface IRole 20 { 21 int Insert(Role role); 22 } 23 } 24 25 namespace IService 26 { 27 public interface IUser 28 { 29 void RegUser(User user); 30 } 31 }
3:业务类,程序集合是:Service;命名空间是:Service1
1 namespace Service1 2 { 3 public class UserManager : IUser 4 { 5 public UserManager() 6 { 7 Console.WriteLine("userManager无参构造函数"); 8 } 9 public UserManager(int id,string name) 10 { 11 Console.WriteLine($"userManager有参构造函数id={id};name={name}"); 12 } 13 public UserManager(int id) 14 { 15 Console.WriteLine($"userManager有参构造函数id={id};"); 16 } 17 public void RegUser(User user) 18 { 19 Console.WriteLine($"注册用户,UserName={user.UserName},UserId={user.UserId}"); 20 } 21 } 22 23 public class RoleManager : IRole 24 { 25 public RoleManager(int id) 26 { 27 Console.WriteLine($"有参构造函数:Id={id}"); 28 } 29 public int Insert(Role role) 30 { 31 Console.WriteLine($"insert方法{role.Id}"); 32 return 1; 33 } 34 } 35 36 public class DepartmentManager : IRepository<Department> 37 { 38 public int Insert(Department t) 39 { 40 Console.WriteLine($"新增 id={t.Id},Name={t.Name}"); 41 return 0; 42 } 43 44 public Department LoadById(int id) 45 { 46 Console.WriteLine("获取Id"); 47 return null; 48 } 49 } 50 51 public class CompanyManager : ICompany 52 { 53 public CompanyManager() 54 { 55 Console.WriteLine("CompanyManager无参构造函数"); 56 } 57 public CompanyManager(int id, string name) 58 { 59 Console.WriteLine($"CompanyManager有参构造函数id={id};name={name}"); 60 } 61 public CompanyManager(int id) 62 { 63 Console.WriteLine($"CompanyManager有参构造函数id={id};"); 64 } 65 public void RegCompany(Company company) 66 { 67 Console.WriteLine($"注册公司,CompanyName={company.CompanyName},CompanyId={company.CompanyId}"); 68 } 69 } 70 }
上面是基础类,下面的业务代码中有用到 !
1 static void Main(string[] args) 2 { 3 try 4 { 5 #region 普通方法的注册 6 //声明unityContainer容器 7 IUnityContainer container = new UnityContainer(); 8 9 container.RegisterType(typeof(ICompany), typeof(CompanyManager)); 10 var compay = container.Resolve<ICompany>(); 11 compay.RegCompany(new Company() { CompanyId = 1, CompanyName = "companyName" }); 12 13 //把UserManager映射成IUser,下次Resolve<IUser>直接new UserManager 14 container.RegisterType<IUser, UserManager>(); 15 16 //1:等价于IUser u = new UserManager(); 调用无参的构造函数 17 var userService4NoParam = container.Resolve<IUser>(); 18 19 //2:等价于IUser u = new UserManager(1, "wss"); 调用两个参数的构造函数 20 var userService4TwoParam = container.Resolve<IUser>( 21 new ResolverOverride[] { 22 new ParameterOverride("id",1), 23 new ParameterOverride("name","wss") 24 }); 25 26 //3:等价于IUser u = new UserManager(1); 调用一个参数的构造函数 27 var userService4OneParam = container.Resolve<IUser>( 28 new ResolverOverride[] { new ParameterOverride("id", 1) }); 29 userService4OneParam.RegUser(new User() { UserId = 1, UserName = "wss" }); 30 31 #endregion 32 33 Console.ReadLine(); 34 } 35 catch (Exception ex) 36 { 37 Console.WriteLine($"错误:{ex.Message}"); 38 }
使用Unity来管理对象与对象之间的关系可以分为以下几步:
1 <configuration> 2 <configSections> 3 <section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection,Unity.Configuration"/> 4 <!--type="命名空间,程序集即是dll或者exe的命名空间"--> 5 </configSections> 6 7 <unity> 8 <!--***别名设置开始,下面三种设置方式都可以***--> 9 <!--管理生命周期类型,以及一些类型别名的设置,方便我们映射对象的编写,比如同一个类型注册多次,我们只要在typeAlias添加一个类型别名,这样我们再添加这个类型映射的时候只要写个别名就可以了。--> 10 <alias alias="MyDateTime" type="System.DateTime" /> 11 <typeAliases> 12 <!--寿命管理器类型--> 13 <typeAlias alias="singleton" type="Microsoft.Practices.Unity.ContainerControlledLifetimeManager,Microsoft.Practices.Unity" /> 14 <typeAlias alias="external" type="Microsoft.Practices.Unity.ExternallyControlledLifetimeManager, Microsoft.Practices.Unity" /> 15 <!--用户定义的类型别名--> 16 <typeAlias alias="UserManager" type="Service1.UserManager,Service" /> 17 </typeAliases> 18 <aliases> 19 <!--type="命名空间.类名,程序集"--> 20 <add alias="IUser" type="IService.IUser,IService"></add> 21 </aliases> 22 <!--***别名设置结束***--> 23 24 <!--**想要加unity自带拦截器的第1步**--> 25 <sectionExtension type="Microsoft.Practices.Unity.InterceptionExtension.Configuration.InterceptionConfigurationExtension,Unity.Interception.Configuration"/> 26 <!--<sectionExtension type="命名空间.类,程序集即是dll或者exe的命名空间"/>--> 27 <containers> 28 <!--如果有多个container,每个container的name是不能重复的,否则读取的时候会报错--> 29 <!--container为容器管理,下面包含多个类型映射,我们平常使用的构造器注册、属性注册和方法注册,就可以在constructor、property、method节点进行配置。--> 30 <container name="aopContainer"> 31 <!--**想要加unity自带拦截器的第2步**--> 32 <extension type="Interception"/> 33 <!--容器中类型映射有3种,一种使用register,一种是instances,另外一种是types--> 34 <!--使用上面的别名Aliases--> 35 <register type="IUser" mapTo="UserManager"> 36 <!--**想要加unity自带拦截器的第3步**--> 37 <interceptor type="InterfaceInterceptor"/> 38 <!--自己定义的拦截器开始--> 39 <interceptionBehavior type="MyTest.UnityWay.MonitorBehavior, MyTest"/> 40 <interceptionBehavior type="MyTest.UnityWay.LogBeforeBehavior, MyTest"/> 41 <interceptionBehavior type="MyTest.UnityWay.ParameterCheckBehavior, MyTest"/> 42 <interceptionBehavior type="MyTest.UnityWay.CachingBehavior, MyTest"/> 43 <interceptionBehavior type="MyTest.UnityWay.ExceptionLoggingBehavior, MyTest"/> 44 <interceptionBehavior type="MyTest.UnityWay.LogAfterBehavior, MyTest"/> 45 <!--自己定义的拦截器结束--> 46 </register> 47 48 <!--注册类+构造函数--> 49 <!--<register type="命名空间.接口类型2,程序集" mapTo="命名空间.实现类型2,程序集" />--> 50 <register type="IService.IRole,IService" mapTo="Service1.RoleManager,Service"/> 51 52 <instances> 53 <!--instances:这些对象被用容器的 RegisterInstance 方法进行注册,一般用于简单类型,并且是已知值,所以value是必填,type不写默认为sytem.string--> 54 <add name="MyInstance1" type="System.String" value="Some value" /> 55 <add name="MyInstance2" type="MyDateTime" value="2019-02-19T11:13:00" /> 56 </instances> 57 <types> 58 <!--type这些配置被container.RegisterType<TFrom,TTo>()注册--> 59 <type type="IService.ICompany,IService" mapTo="Service1.CompanyManager,Service" /> 60 <!--<type name="注册名称可选" type="源类型(格式:命名空间.类名,程序集dll的名称),必填" mapTo="目标类型,可选" lifetime="声明周期,可选">--> 61 <!--lifetime:Transient:是默认的,每次创建一个实例;Singleton:单例模式,每次创建都返回同一个实例--> 62 63 <!--[]方括号表示的是泛型参数,具体可以通过 typeof(IRepository<Department>).AssemblyQualifiedName,然后去掉Version=1.0.0.0, Culture=neutral, PublicKeyToken=null--> 64 <type type="IService.IRepository`1[[Entity.Department, Entity]],IService" mapTo="Service1.DepartmentManager,Service" /> 65 </types> 66 </container> 67 </containers> 68 </unity> 69 </configuration>
然后下图是关于unity.config中节点的标签,可以参考一下:
然后我们程序中读取xml配置文件并且实现如下:
1 public static void Show() 2 { 3 //配置UnityContainer 4 IUnityContainer container = GetContainer("aopContainer");//GetContainer(); 5 6 //1:获取instances注册的类型,一般适用于简单的类型且值是固定的常量 7 var data4Str = container.Resolve<string>("MyInstance1"); 8 var data4DateTime = container.Resolve<DateTime>("MyInstance2"); 9 10 //2:获取types注册的类型 11 ICompany companyManager = container.Resolve<ICompany>(); 12 companyManager.RegCompany(new Company() { CompanyId = 1, CompanyName = "公司" }); 13 14 //3:获取register注册的类型 15 IUser userManager = container.Resolve<IUser>(); 16 userManager.RegUser(new User() { UserName = "wss", UserId = 1 }); 17 18 //4:获取泛型类 19 IRepository<Department> department = container.Resolve<DepartmentManager>(); 20 department.Insert(new Department() { Id = 1, Name = "部门1" }); 21 22 //5:获取有参的构造函数 23 IRole roleManager = container.Resolve<RoleManager>(new ResolverOverride[] { new ParameterOverride("id", 1) }); 24 roleManager.Insert(new Role() { Id = 1 }); 25 } 26 27 private static IUnityContainer GetContainer(string containerName = "") 28 { 29 //配置UnityContainer 30 IUnityContainer container = new UnityContainer(); 31 ExeConfigurationFileMap fileMap = new ExeConfigurationFileMap(); 32 //得到配置文件的路径 33 fileMap.ExeConfigFilename = Path.Combine(AppDomain.CurrentDomain.BaseDirectory + "CfgFiles\\Unity.Config"); 34 35 //从config文件中读取配置信息 36 Configuration configuration = ConfigurationManager.OpenMappedExeConfiguration(fileMap, ConfigurationUserLevel.None); 37 38 //获取指定名称的配置节,UnityConfigurationSection.SectionName=unity,得到containers集合下面的container节点 39 UnityConfigurationSection configSection = (UnityConfigurationSection)configuration.GetSection(UnityConfigurationSection.SectionName); 40 41 //unity.config中的containers下面可以有多个container,但是每个container的name一定要不同,不然会报错 42 if (string.IsNullOrEmpty(containerName)) 43 { 44 //获取Unity.Config中的container没有设置Name属性的节点 45 configSection.Configure(container); 46 } 47 else 48 { 49 //获取节点aopContainer的container节点 50 configSection.Configure(container, containerName); 51 } 52 return container; 53 }
这样即实现了unity动态创建对象的功能,如果以后想要新增或者修改,只需要修改xml文件的映射即可!
三:unity的统一拦截器
使用拦截器之前,我们必须在nuget上面拉一下:Unity.Interception 和Unity.Interception.Configuration 这个两个dll,然后新增使用拦截器的代码,具体如下:
1 using Entity; 2 using System; 3 using System.Collections.Generic; 4 using Unity.Interception.InterceptionBehaviors; 5 using Unity.Interception.PolicyInjection.Pipeline; 6 7 namespace MyTest.UnityWay 8 { 9 /// <summary> 10 /// 不需要特性 11 /// </summary> 12 public class CachingBehavior : IInterceptionBehavior 13 { 14 public IEnumerable<Type> GetRequiredInterfaces() 15 { 16 return Type.EmptyTypes; 17 } 18 19 public IMethodReturn Invoke(IMethodInvocation input, GetNextInterceptionBehaviorDelegate getNext) 20 { 21 Console.WriteLine("CachingBehavior"); 22 //input.Target.GetType().GetCustomAttributes() 23 if (input.MethodBase.Name.Equals("GetUser")) 24 return input.CreateMethodReturn(new User() { UserId = 234, UserName = "wss" }); 25 return getNext().Invoke(input, getNext); 26 } 27 28 public bool WillExecute 29 { 30 get { return true; } 31 } 32 } 33 }
1 //using Microsoft.Practices.Unity.InterceptionExtension; 2 using System; 3 using System.Collections.Generic; 4 using Unity.Interception.InterceptionBehaviors; 5 using Unity.Interception.PolicyInjection.Pipeline; 6 7 namespace MyTest.UnityWay 8 { 9 public class ExceptionLoggingBehavior : IInterceptionBehavior 10 { 11 public IEnumerable<Type> GetRequiredInterfaces() 12 { 13 return Type.EmptyTypes; 14 } 15 16 public IMethodReturn Invoke(IMethodInvocation input, GetNextInterceptionBehaviorDelegate getNext) 17 { 18 Console.WriteLine("ExceptionLoggingBehavior"); 19 IMethodReturn methodReturn = getNext()(input, getNext); 20 if (methodReturn.Exception == null) 21 { 22 Console.WriteLine("无异常"); 23 } 24 else 25 { 26 Console.WriteLine($"异常:{methodReturn.Exception.Message}"); 27 } 28 return methodReturn; 29 } 30 31 public bool WillExecute 32 { 33 get { return true; } 34 } 35 } 36 }
1 //using Microsoft.Practices.Unity.InterceptionExtension; 2 using System; 3 using System.Collections.Generic; 4 using Unity.Interception.InterceptionBehaviors; 5 using Unity.Interception.PolicyInjection.Pipeline; 6 7 namespace MyTest.UnityWay 8 { 9 public class LogAfterBehavior : IInterceptionBehavior 10 { 11 public IEnumerable<Type> GetRequiredInterfaces() 12 { 13 return Type.EmptyTypes; 14 } 15 16 public IMethodReturn Invoke(IMethodInvocation input, GetNextInterceptionBehaviorDelegate getNext) 17 { 18 Console.WriteLine("LogAfterBehavior"); 19 foreach (var item in input.Inputs) 20 { 21 Console.WriteLine(item.ToString());//反射获取更多信息 22 } 23 IMethodReturn methodReturn = getNext()(input, getNext); 24 Console.WriteLine("LogAfterBehavior" + methodReturn.ReturnValue); 25 return methodReturn; 26 } 27 28 public bool WillExecute 29 { 30 get { return true; } 31 } 32 } 33 }
1 //using Microsoft.Practices.Unity.InterceptionExtension; 2 using System; 3 using System.Collections.Generic; 4 using Unity.Interception.InterceptionBehaviors; 5 using Unity.Interception.PolicyInjection.Pipeline; 6 7 namespace MyTest.UnityWay 8 { 9 /// <summary> 10 /// 不需要特性 11 /// </summary> 12 public class LogBeforeBehavior : IInterceptionBehavior 13 { 14 public IEnumerable<Type> GetRequiredInterfaces() 15 { 16 return Type.EmptyTypes; 17 } 18 19 public IMethodReturn Invoke(IMethodInvocation input, GetNextInterceptionBehaviorDelegate getNext) 20 { 21 Console.WriteLine("LogBeforeBehavior"); 22 foreach (var item in input.Inputs) 23 { 24 Console.WriteLine(item.ToString());//反射获取更多信息 25 } 26 return getNext().Invoke(input, getNext); 27 } 28 29 public bool WillExecute 30 { 31 get { return true; } 32 } 33 } 34 }
1 //using Microsoft.Practices.Unity.InterceptionExtension; 2 using System; 3 using System.Collections.Generic; 4 using System.Diagnostics; 5 using Unity.Interception.InterceptionBehaviors; 6 using Unity.Interception.PolicyInjection.Pipeline; 7 8 namespace MyTest.UnityWay 9 { 10 /// <summary> 11 /// 性能监控的AOP扩展 12 /// </summary> 13 public class MonitorBehavior : IInterceptionBehavior 14 { 15 public IEnumerable<Type> GetRequiredInterfaces() 16 { 17 return Type.EmptyTypes; 18 } 19 20 public IMethodReturn Invoke(IMethodInvocation input, GetNextInterceptionBehaviorDelegate getNext) 21 { 22 Console.WriteLine(this.GetType().Name); 23 string methodName = input.MethodBase.Name; 24 Stopwatch stopwatch = new Stopwatch(); 25 stopwatch.Start(); 26 27 var methodReturn = getNext().Invoke(input, getNext);//后续逻辑执行 28 29 stopwatch.Stop(); 30 Console.WriteLine($"{this.GetType().Name}统计方法{methodName}执行耗时{stopwatch.ElapsedMilliseconds}ms"); 31 32 return methodReturn; 33 } 34 35 public bool WillExecute 36 { 37 get { return true; } 38 } 39 } 40 }
1 //using Microsoft.Practices.Unity.InterceptionExtension; 2 using Entity; 3 using System; 4 using System.Collections.Generic; 5 using Unity.Interception.InterceptionBehaviors; 6 using Unity.Interception.PolicyInjection.Pipeline; 7 8 namespace MyTest.UnityWay 9 { 10 public class ParameterCheckBehavior : IInterceptionBehavior 11 { 12 public IEnumerable<Type> GetRequiredInterfaces() 13 { 14 return Type.EmptyTypes; 15 } 16 17 public IMethodReturn Invoke(IMethodInvocation input, GetNextInterceptionBehaviorDelegate getNext) 18 { 19 Console.WriteLine("ParameterCheckBehavior"); 20 User user = input.Inputs[0] as User; 21 if (user.Password.Length < 10) 22 { 23 return input.CreateExceptionMethodReturn(new Exception("密码长度不能小于10位")); 24 } 25 else 26 { 27 Console.WriteLine("参数检测无误"); 28 return getNext().Invoke(input, getNext); 29 } 30 } 31 32 public bool WillExecute 33 { 34 get { return true; } 35 } 36 } 37 }