胖胖滴加菲猫

导航

c#中的Unity容器

DIP是依赖倒置原则:一种软件架构设计的原则(抽象概念)。依赖于抽象不依赖于细节

IOC即为控制反转(Inversion of Control):传统开发,上端依赖(调用/指定)下端对象,会有依赖,把对下端对象的依赖转移到第三方容器(工厂+配置文件+反射),能够程序拥有更好的扩展性,是DIP的具体实现方式,可以用来减低计算机代码之间的耦合度。

DI 即为依赖注入(Dependency Injection):

  1. 是实现IOC的手段和方法,就是能做到构造某个对象时,将依赖的对象自动初始化并注入 :
  2. 有三种注入方式:构造函数注入--属性注入--方法注入(按时间顺序):
  3. 构造函数注入用的最多,默认找参数最多的构造函数,可以不用特性,可以去掉对容器的依赖

Unity容器:是微软推出的IOC框架,使用这个框架,可以实现AOP面向切面编程,便于代码的后期维护,此外,这套框架还自带单例模式,可以提高程序的运行效率。

 

上面介绍了一下Unity,IOC,DI的概念,那我们项目中什么时候会使用Unity呢,总结分析得到如下情况:

  1. 所构建的系统依赖于健全的面向对象原则,但是大量不同的代码交织在一起而难以维护。 
  2. 构建的对象和类需要依赖其他对象或类。 
  3. 依赖于复杂的或需要抽象的对象。 
  4. 希望利用构造函数、方法或属性的调用注入优势。 
  5. 希望管理对象实例的生命周期。 
  6. 希望能够在运行时管理并改变依赖关系。 
  7. 希望在拦截方法或属性调用的时候生成一个策略链或管道处理容器来实现横切(AOP)任务。 
  8. 希望在Web Application中的回发操作时能够缓存或持久化依赖关系。

使用Unity的好处:

  • Unity支持简单对象创建,特别是分层对象结构和依赖,以简化程序代码。其包含一个编译那些可能存在依赖于其他对象的对象实例机制。
  • Unity支持必要的抽象,其允许开发者在运行时或配置去指定依赖关系同时可以简单的管理横切点(AOP)。
  • Unity增加了推迟到容器组件配置的灵活性。其同样支持一个容器层次的结构。
  • Unity拥有服务定位能力,对于一个程序在许多情况下重复使用组件来分离和集中功能是非常有用的。
  • Unity允许客户端储存或缓存容器。对于在ASP.NET Web applications中开发者将容器持久化于ASP.NET中的session或application中特别有效。
  • Unity拥有拦截能力,其允许开发者通过创建并执行handlers(在方法或属性被调用到达之前)来为已存在的组件增加一个函数,并再次为返回调用结果。
  • Unity可以从标准配置系统中读取配置信息,例如:XML文件,同时使用配置文件来配置容器。
  • Unity支持开发者实现自定义容器扩展,例如:你可以实现方法来允许额外的对象构造和容器特征,例如缓存。
  • Unity允许架构师和开发者在现代化的程序中更简单的实现通用设计模式
 上面我们分析了unity使用的场景以及unity能够解决的问题,那下面我们来具体讲解一下如何使用unity。首先我们先准备一下基础类:
1:实体类,程序集和命名空间都是:Entity
 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 }
View Code

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 }
View Code

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 }
View Code

上面是基础类,下面的业务代码中有用到 !

 

一:我们使用代码来创建对象,如果仅仅使用代码来创建,则只需要再nuget上面添加:Unity,则可以实现下面的代码:
 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     }
View Code

使用Unity来管理对象与对象之间的关系可以分为以下几步:

A、创建一个UnityContainer对象
B、通过UnityContainer对象的RegisterType方法来注册对象与对象之间的关系
C、通过UnityContainer对象的Resolve方法来获取指定对象关联的对象
 
二:使用配置文件来进行映射对象
上面第一种方式面向接口实现有很多好处,可以提供不同灵活的子类实现,增加代码稳定和健壮性等,但是接口一定是需要实现的,如果一个子类实现换成另一个子类实现,就需要在代码中改动,或者建立一个工厂来根据条件生成,还是存着着一定的耦合关系。
而读取XML文件则用来削减程序的耦合问题,它把耦合从代码中移出去,放到统一的XML文件中,通过一个容器在需要的时候把这个依赖关系形成,即把需要的接口实现注入到需要它的类中,当需要换一个实现子类将会变成很简单(一般这样的对象都是实现于某种接口的),只要修改XML就可以,这样可以实现对象的热插拨(有点象USB接口)。
 
上面说那么多,具体实现方式如下:
首先我们先从nuget上面添加:Unity.Configuration,然后在新增文件夹CfgFiles新增一个unity.config文件,具体代码和注释如下:
 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>
View Code

然后下图是关于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         }
View Code

这样即实现了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 }
View Code
 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 }
View Code
 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 }
View Code
 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 }
View Code
 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 }
View Code
 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 }
View Code

 

最后在unity.config中配置如三步:
 
这样就实现了在UserManager中增加了过滤器,执行UserManager方法之前都会先按顺序执行MonitorBehavior,LogBeforeBehavior,ParameterCheckBehavior,CachingBehavior,ExceptionLoggingBehavior,LogAfterBehavior 这些类的。以后想要修改或者增加拦截器则需要在这边配置即可!
 
通过上面我们总结了使用unity的几个dll文件,主dll只需要Unity,Unity.Configuration(读取配置unity.config需要),Unity.Interception和Unity.Interception.Configuration 这两个是拦截器所需要的!
 
 
 

posted on 2019-03-06 18:15  胖胖滴加菲猫  阅读(3288)  评论(0编辑  收藏  举报