在过去几年中,依赖关系注入 (DI) 模式在 .NET 开发人员社区一直受到关注。长时间以来,优秀的博客作者们讨论着 DI 的优点。MSDN 杂志 针对这一主题发表了多篇文章。.NET 4.0 将发布某种类似 DI 的功能,并计划以后将其发展为完整的 DI 系统。 阅读有关 DI 的博客文章时,我注意到,这一主题有一个很小却很重要的倾向。作者们谈论的是如何在整个应用程序环境中使用 DI。但如何编写使用 DI 的库或框架呢?关注重点的变化,对模式的使用有何影响?这是几个月前我们研究 Enterprise Library 5.0 的体系结构时首先遇到的问题。 背景 Microsoft Enterprise Library (Entlib) 是 Microsoft 模式与实施方案组开发的著名版本。迄今为止,其下载次数已超过两百万。可以想到的单位 — 从金融机构、政府机关到餐厅和医疗设备制造商 — 都在使用它。顾名思义,Entlib 是一种库,可帮助开发人员处理许多企业开发人员都会面临的问题。如果您不熟悉 Entlib,请访问我们的网站 p&p 开发中心,以了解更多信息。 Entlib 在很大程度上由配置驱动。它的大部分代码专用于读取配置,然后基于配置组合对象图。Entlib 对象可能非常复杂。大多数块都包含大量可选功能。此外,还有许多用于支持检测等功能的底层基础结构,它们也需要进行关联。我们不希望用户仅仅为了使用 Entlib 而去手动创建检测提供程序、读取配置,等等,所以将对象的创建封装在了工厂对象和静态外层之后。 Entlib 版本 2 到版本 4 的核心是一个名为“ObjectBuilder”的小型框架。ObjectBuilder 的作者将 ObjectBuilder 描述为“一种用于构建依赖关系注入容器的框架”。Enterprise Library 只是使用 ObjectBuilder 的 p&p 项目之一;其他使用 ObjectBuilder 的 p&p 项目包括 Composite UI Application Block、Smart Client Software Factory 和 Web Client Software Factory。Entlib 特别注重说明的“框架”部分,将一个很大的自定义功能集构建至 ObjectBuilder。读取 Entlib 配置和组合对象图时,需要使用这些自定义功能。在很多情况下,也需要用它们来改进现有 ObjectBuilder 实现的性能。 缺点在于,需要不少时间才能对 ObjectBuilder 本身(设计极为抽象,再加上完全没有文档,ObjectBuilder 的复杂性绝非虚言)和 Entlib […]
View Details在前面几篇有关Unity学习的文章中,我对Unity的一些常用功能进行介绍,包括:Unity的基本知识、管理对象之间的关系、生命周期、依赖注入等,今天则是要介绍Unity的另外一个重要功能——拦截(Interception)。 以下是本文所要介绍的内容: 1、Unity与PIAB的关系。 2、使用Unity来接管PIAB的功能实现。 一、Unity与PIAB的关系 Unity中的Interception可以通过Unity的Container或独立的API(Intercept)来实现,这些具体的实现没有包含在Unity本身的类库中,而独立放在Unity.Interception这个类库中,想深入了解的朋友可以查看下这个类的具体源代码。今天介绍的则是Unity与企业库内置的PolicyInjection模块的使用。 在我看来,PolicyInjection就像是Unity.Interception模块的一个通用封装,在PIAB中包含了各种常用的MatchRules(匹配规则)与CallHandler(调用处理程序),其中各种CallHandler都是实现Unity.InterceptionExtension.ICallHandler,而ICallHandler其定义与Unity.InterceptionExtension.IInterceptionBehavior类似的。所以由于PIAB中已经内置好了各种常用的功能,我们只需按照我们的需求调用既可(可参看:内置Call Handler介绍和自定义Matching Rule),如果需要有特殊的逻辑也只需自己定义具体的CallHandler既可(可参看:建立自定义Call Handler实现用户操作日志记录),当然这些都是建立在PIAB的基础上的,如果你不想通过PIAB来实现AOP拦截处理的话,你就可以直接实现Unity中的IInterceptionBehavior来进行具体的业务处理,这个在后面的文章中会介绍到。 其实查看过PIAB模块源码的朋友就可以发现,其实PIAB本质上都是依赖于Unity.Interception模块,其本身没有具体的实现,其具体实现都包含在Unity.Interception.PolicyInjection下(包括MatchRules与CallHandler),有兴趣的朋友可以查看下。由于PIAB依赖于Unity.Interception,所以我们完全可以通过Unity来接管原有PIAB的所有功能(包括各种配置信息)。 二、使用Unity来接管PIAB的功能实现 在建立自定义Call Handler实现用户操作日志记录一文中我已经实现好了一个自定义的CallHandler(如不了解可以先查看下PIAB相关的文章),不过表示层的具体调用还是通过PIAB的PolicyInjection.Create来获取具体对象,而现在我就要通过Unity来接管原来PIAB的这些对象创建,这样极大程度的统一了对象创建与依赖管理。 我这边还是通过代码及配置2种方式来实现Unity接管PIAB的功能,首先是代码的形式,代码配置相对来说比较繁琐,见如下代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
private void Login1() { try { IUnityContainer container = new UnityContainer().AddNewExtension<Interception>(); container.Configure<Interception>() //为IStudentManage设置拦截器为TransparentProxyInterceptor .SetDefaultInterceptorFor<IStudentManage>(new TransparentProxyInterceptor()) .AddPolicy("UserLog") //增加MemberNameMatchingRule,使用InjectionConstructor初始化MemberNameMatchingRule的构造函数 .AddMatchingRule<MemberNameMatchingRule> (new InjectionConstructor("Login")) //增加CallHandler,使用InjectionConstructor来初始化CallHandler的构造函数 .AddCallHandler<UserLogCallHandler> ("UserLogCallHandler", new ContainerControlledLifetimeManager(), new InjectionConstructor("登录成功", "")); //注册对象关系 container.RegisterType<IStudentManage, StudentManage>(); IStudentManage studentBll = container.Resolve<IStudentManage>(); bool isAdmin2 = false; if (studentBll.Login(txtUid.Text.Trim(), txtPwd.Text.Trim(), out isAdmin2)) { if (string.IsNullOrEmpty(Request.QueryString["returnUrl"]) == false) { Response.Redirect(Request.QueryString["returnUrl"]); } else { Response.Redirect("~/Default.aspx"); } } else { ltMsg.Text = "用户名或密码不正确,请重试!"; } } catch (Exception ex) { throw; } } |
在底层不变的情况下,表示层如果想通过Unity来实现需要以上的代码配置。虽然配置较为繁琐,但是Unity很好的接管了PIAB的功能同时很大程度的解决了代码之间的耦合关系,其中有3个注意点: 1、创建Unity的container容器时需要增加Interception扩展:.AddNewExtension<Interception>(); 2、在增加策略是需要为需要拦截的接口增加拦截器:SetDefaultInterceptorFor<IStudentManage>(new TransparentProxyInterceptor()) 3、如果MatchRule或CallHandler有相应的构造函数需要通过InjectionConstructor类来初始化构造函数。 接下来是配置文件的代码,如下代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
<unity xmlns="http://schemas.microsoft.com/practices/2010/unity"> <!--新增配置节扩展,用于下面的<interception>配置节--> <sectionExtension type="Microsoft.Practices.Unity.InterceptionExtension.Configuration.InterceptionConfigurationExtension, Microsoft.Practices.Unity.Interception.Configuration" /> <alias alias="IStudentManage" type="EntLibStudy.IBLL.IStudentManage,EntLibStudy.IBLL"/> <alias alias="StudentManage" type="EntLibStudy.BLL.StudentManage,EntLibStudy.BLL"/> <container> <!--为容器增加Interception扩展,如不增加下面interception配置会报错--> <extension type="Interception"/> <interception> <!--增加一个名为UserLog的拦截策略,用于记录日志--> <policy name="UserLog"> <!--新增MemberNameMatchingRule匹配规则,同时需要同过配置初始化匹配规则构造函数 具体可查看Unity.InterceptionExtension.PolicyInjection.MatchRule下具体类--> <matchingRule name="Member Name Matching Rule" type="MemberNameMatchingRule"> <constructor> <param name="nameToMatch" value="Login"/> </constructor> </matchingRule> <!--增加调用处理程序,这边指定的是我自定义的UserLogCallHandler--> <!--同样也需要初始化构造函数--> <callHandler name="UserLogCallHandler" type="EntLibStudy.Helper.EntLibExtension.PolicyInjectionExtension.UserLogCallHandler, EntLibStudy.Helper, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"> <constructor> <param name="message" type="System.String" value="登录成功!"> </param> <param name="parameterName" type="System.String" value=" "> </param> </constructor> </callHandler> </policy> </interception> <!--注册对象关系,需要注意的是需要为这个注册增加TransparentProxyInterceptor的拦截器--> <register type="IStudentManage" mapTo="StudentManage"> <interceptor isDefaultForType="true" type="TransparentProxyInterceptor"/> </register> </container> </unity> |
读取配置程序代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
private void Login2() { IUnityContainer container = new UnityContainer() .LoadConfiguration("Log"); IStudentManage studentBll = container.Resolve<IStudentManage>(); bool isAdmin2 = false; if (studentBll.Login(txtUid.Text.Trim(), txtPwd.Text.Trim(), out isAdmin2)) { if (string.IsNullOrEmpty(Request.QueryString["returnUrl"]) == false) { Response.Redirect(Request.QueryString["returnUrl"]); } else { Response.Redirect("~/Default.aspx"); } } else { ltMsg.Text = "用户名或密码不正确,请重试!"; } } |
可以看到配置文件代码相对来说也不少,但是对于项目使用来说还是偏向于通过配置来解决代码的耦合问题,而且Unity的XSD提供了很好的智能提示,保证了书写配置文件效率及正确性,而且通过将这种具体的关联移到配置文件中也可以大大的减少程序代码的书写,程序代码只需要一句简单初始化+读取配置节既可完成。 配置方式实现的注意点和代码实现类似,我已经写在上面的配置文件中了,其效果和代码配置是一样的。 以上就是本文的所有内容了,仅仅介绍了如何通过Unity来接管PIAB的工作,我这边举的例子是依赖于我原先自己创建的CallHandler,内置的CallHandler配置也是类似的,这边就不再详细介绍了,想了解的朋友还是去查看Unity的官方文档。 有关Unity的拦截技术点可以查看官方文档:Unity Interception Techniques 源代码下载:点我下载 转自:http://www.cnblogs.com/kyo-yo/archive/2010/12/08/Learning-EntLib-Tenth-Decoupling-Your-System-Using-The-Unity-PART3-Unity-And-PIAB.html
View Details继续学习Unity,在前几篇中已经将Unity的使用方法做了一个还算详细的介绍了,主要是如何通过代码及配置文件来注册对象之间的关系、Unity内置所有的生命周期管理使用介绍,及Unity的Register和Resolve的一些高级应用。通过在PART1——为什么要使用Unity?的学习我们知道Unity可以帮我们简化并管理对象之间的关系(也就是前几篇所介绍的),而今天则要介绍Unity的另外一个重要功能——DI(依赖注入)。 本篇文章将主要介绍: 1、构造函数注入。 2、属性注入。 3、方法注入。 4、使用配置完成各种注入。 5、对已经创建对象进行注入。 一、构造函数注入 在有些时候,我们所编写的类中的构造函数都会包含对其他对象的引用,如下代码:
1 2 3 4 5 |
public Subject2(MyClass myClass1,MyClass myClass2) { myClass1.Name = "班级1"; myClass2.Name = "班级2"; } |
可以看到这个构造函数有2个参数,都依赖于MyClass类,如果一般情况下想要调用,我们总是需要实现构建好2个myclass对象,所以相对来说比较麻烦,而如果使用Unity来调用就会方便许多,如下代码:
1 |
container.Resolve<Subject2>(); |
仅仅一行既可,Unity容器会自动帮我们构建好所需的依赖对象实例。 当然这个只是简单的使用,在实际的情况下我们不会这么编写代码,我们不会直接引用对象,而是直接引用接口,这样可以解除代码的耦合性,如下代码:
1 2 3 4 |
public Subject2(IClass myClass1) { myClass1.Name = "班级1"; } |
这样Subject2类的构造函数仅仅依赖于IClass接口,并依赖于具体的实现类,这种情况下,如果想调用的话,需要实现注册好对象之间的关系,如下代码:
1 2 3 4 5 6 7 |
public static void ConstructorInjection() { //container.Resolve<Subject2>(); container.RegisterType<IClass, MyClass>(); container.Resolve<Subject2>(); } |
通过Unity容器对象的获取对象时候默认总是获取非命名默认注册对象,但是如果调用Subject2的构造函数参数我想指定具体的对象时怎么办? 这时就需要使用Dependency这个特性类来解决了,在需要特殊指定的依赖关系的参数上加上Dependency特性,并为Dependency指定好参数name(此name参数表示注册对象关系时所指定的名称),代码如下:
1 2 3 4 5 |
public Subject2([Dependency("your")] IClass classInfo) { classInfo.Name = "班级1"; } |
调用代码:
1 2 3 |
container.RegisterType<IClass, MyClass>() .RegisterType<IClass, YourClass>("your"); container.Resolve<Subject2>(); |
可以看到,我对IClass注册了2个对象,而我在Subject2的构造函数参数上使用了Dependency特性指定了ClassInfo参数获取注册名为“your”的对象。 现在还有一个问题,就是当一个类存在多个构造函数的时候,我们如何区分哪个构造函数需要实现注入,哪个不需要? 这时就可以使用InjectionConstructor特性来标识,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 |
[InjectionConstructor] public Subject2([Dependency("your")] IClass classInfo) { classInfo.Name = "班级1"; } public Subject2(IClass classInfo1, IClass classInfo2) { classInfo1.Name = "班级1"; classInfo2.Name = "班级2"; } |
Unity只会调用标识了InjectionConstructor特性的构造函数,这样就很好的解决了多构造函数的情况下,Unity调用哪个构造函数。 二、属性注入 属性注入和构造函数注入类似,只需在需要注入的属性上增加一个Dependency特性,同样的也可以为Dependency指定一个name参数用来指定注入属性的具体对象,如下代码,在Subject属性上增加了Dependency特性,来表示这个属性需要注入:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
public class MyClass : IClass { public MyClass() { } public void ShowInfo() { Console.WriteLine("这个是我的班级"); } [Dependency("Subject1")] public ISubject Subject { get; set; } public string Name { get; set; } public string Description { get; set; } } |
具体的调用代码:
1 2 3 4 5 6 7 |
public static void PropertyInjection() { container.RegisterType<ISubject, Subject1>("Subject1") .RegisterType<IClass,MyClass>(); var classInfo = container.Resolve<IClass>(); Console.WriteLine(classInfo.Subject.Name); } |
这样ClassInfo的Subject属性自动关联到了Subject1类上(完成了属性注入),访问classInfo.Subject.Name可以得到“科目1”。 三、方法注入 方法注入同样只需在需要注入的方法上增加一个特性——InjectionMethod既可(其使用方法也和构造注入类似),这样Unity会自动帮我们完成注入,方法注入和构造注入一样,同样可以在方法的参数上指定Dependency特性来指定参数所依赖的注册,下面的类代码中包含了构造注入、属性注入及方法注入,这边集合在一起展示相对来说直观一些:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
public class MyClass : IClass { public MyClass() { } public MyClass(ISubject subjectInfo) { this.TempSubject1 = subjectInfo; } public void ShowInfo() { Console.WriteLine("构造注入成功后临时科目1名称:" + this.TempSubject1.Name); Console.WriteLine("属性注入成功后临时科目名称:" + this.Subject.Name); Console.WriteLine("方法注入成功后临时科目2名称:" + this.TempSubject2.Name); } [InjectionMethod] public void Init(ISubject subjectInfo) { TempSubject2 = subjectInfo; } [Dependency("Subject1")] public ISubject Subject { get; set; } public ISubject TempSubject1 { get; set; } public ISubject TempSubject2 { get; set; } public string Name { get; set; } public string Description { get; set; } } |
具体的调用代码:
1 2 3 4 5 6 7 8 9 |
public static void MethodInjection() { container.RegisterType<ISubject, Subject3>("Subject1") .RegisterType<ISubject, Subject4>(); container.RegisterType<IClass, MyClass>(); var classInfo = container.Resolve<IClass>(); classInfo.ShowInfo(); } |
效果图如下: 4、使用配置完成各种注入 上面所演示的代码都是通过代码来完成对象的注入,下面演示下如何通过配置文件来配置这些注入,具体配置代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
<!--依赖注入配置,包括构造注入,方法注入和属性注入--> <alias alias="IClass" type="UnityStudyConsole.IDemo.IClass, UnityStudyConsole" /> <alias alias="MyClass" type="UnityStudyConsole.Demo.MyClass, UnityStudyConsole" /> <alias alias="ISubject" type="UnityStudyConsole.IDemo.ISubject, UnityStudyConsole" /> <alias alias="Subject3" type="UnityStudyConsole.Demo.Subject3, UnityStudyConsole" /> <alias alias="Subject4" type="UnityStudyConsole.Demo.Subject4, UnityStudyConsole" /> <container name="Third"> <register type="IClass" mapTo="MyClass"> <constructor> <param name="subjectInfo" type="ISubject"> <dependency name="subjectInfo" type="Subject4"/> </param> </constructor> <method name="Init"> <param name="subjectInfo" type="ISubject"> <dependency name="subjectInfo" type="Subject4"/> </param> </method> <property name="Subject"> <dependency name="Subject1" type="Subject3"/> </property> </register> </container> |
代码如下:
1 2 3 4 5 6 7 |
public static void DIConfiguration() { //获取特定配置节下已命名的配置节<container name="Third">下的配置信息 container.LoadConfiguration("Third"); var classInfo = container.Resolve<IClass>(); classInfo.ShowInfo(); } |
具体的效果是和上面截图中是一样的,只不过这边的依赖注入是通过配置文件来实现的。 5、对已经创建对象进行注入 一般来说如果想实现依赖注入需要通过Unity容器来进行对象注册,然后通过Unity容器来获取对象,但是如果对象已经存在(就是不是通过Unity容器来获取的对象),这时如何来通过Unity来实现对已有的对象进行依赖注入呢? Unity容器已经为我们提供好了这种情况的解决办法,就是BuildUp方法,看下下面的代码就能明白了:
1 2 3 4 5 6 7 8 9 10 11 |
public static void BuildUp() { //事先注册好ISubject和MySubject之间的关系 //并指定一个名称以方便在接口中的属性上应用[Dependency("Subject1")]特性 //具体请见IDemo.IClass container.RegisterType<ISubject, Subject1>("Subject1"); IClass classInfo = new MyClass(); IClass classInfo2 = container.BuildUp<IClass>(classInfo); Console.WriteLine(classInfo2.Subject.Name); } #endregion |
在上面的代码中,已经创建好一个对象实例了,这时只需将这个对象作为参数放入BuildUp中,同时还需指定这个对象实例所实现的接口或父类类型,这样Unity就会自动帮我们将这个已存在对象实例中各种注入。 需要注意的是,与上面所说3种依赖注入的不同的是,上面3中的依赖注入需要在具体的类中使用InjectionConstructor、InjectionMethodDependency和特性,而如果对已经存在的对象进行依赖注入,则需要将InjectionConstructor、InjectionMethod和Dependency写在这个对象实例所实现的接口或父类中,否则会报错! 到本文为止,Unity的各种常用功能都已经介绍完毕了,各位可以下载下面的源代码进行查看,同时也可以通过Main方法中的各个方法来查看Unity的各种功能的使用: 示例代码下载:点我下载 (注意:本文示例代码是基于VS2010+Unity2.0,所以请使用VS2010打开,如果没有安装VS2010,请将相关代码复制到相应的VS中运行既可) 转自:http://www.cnblogs.com/kyo-yo/archive/2010/11/29/Learning-EntLib-Tenth-Decoupling-Your-System-Using-The-Unity-PART3-Dependency-Injection.html
View Details今天继续介绍Unity,在上一篇的文章中,我介绍了使用UnityContainer来注册对象之间的关系、注册已存在的对象之间的关系,同时着重介绍了Unity内置的各种生命周期管理器的使用方法,今天则主要介绍Unity的Register和Resolve的一些高级应用。 本篇文章将主要介绍: 1、注册类型同时初始化构造函数参数并重载调用。 2、注册类型同时初始化属性参数并重载调用。 3、延迟获取对象。 4、检索检索容器中注册信息。 一、注册类型同时初始化构造函数参数并重载调用 我们在使用Unity中注册对象之间的关系时,可能对象有相应的构造函数,构造函数中需要传递相应的参数,Unity就支持这样的注册,其主要靠InjectionConstructor这个类来完成,我们首先来看下具体的类构造函数:
1 2 3 4 5 |
public YourClass(string test, MyClass my) { Console.WriteLine(test); Console.WriteLine(my.ToString()); } |
这个构造函数有2个参数,一个字符串和一个MyClass类对象,相应的可以使用如下代码进行注册:
1 2 3 4 5 6 |
//由于所注册的对象的有带有参数的构造函数,所以注册类型时需要提供相应的参数 //这边采用InjectionConstructor这个类来实现 container.RegisterType<IClass, YourClass>( new InjectionConstructor("a", new MyClass())); Console.WriteLine("-----------默认调用输出-------------"); container.Resolve<IClass>(); |
这样既可完成对象注册的同时对构造函数参数进行注入,此时还有另外一个需求,就是虽然在注册的时候已经对构造函数参数进行了初始化,但是在调用的时候我们想更换原先注册的值,这时应该怎么办? 在Unity中,已经帮我们解决了这个问题,我们可以通过ParameterOverride和ParameterOverrides来实现,其中ParameterOverride是针对一个参数,而ParameterOverrides是针对参数列表,有关注册参数初始化及参数重载的全部代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
public static void ResolveParameter() { //由于所注册的对象的有带有参数的构造函数,所以注册类型时需要提供相应的参数 //这边采用InjectionConstructor这个类来实现 container.RegisterType<IClass, YourClass>( new InjectionConstructor("a", new MyClass())); Console.WriteLine("-----------默认调用输出-------------"); container.Resolve<IClass>(); Console.WriteLine("-----------重载后调用输出-------------"); //以下2种Resolve方法效果是一样的 //对于参数过多的时候可以采用第2种方法,如果参数仅仅只有1个可以用第1种 //container.Resolve<IClass>(new ParameterOverride("test", "test"), // new ParameterOverride("my", "new MyClass").OnType<MyClass>()); container.Resolve<IClass>(new ParameterOverrides() { {"test","test"}, {"my",new MyClass()} }.OnType<YourClass>()); } |
其中需要注意的是: 1、在使用ParameterOverride方法来重载参数时,如果注册的参数是一个具体的对象就需要使用OnType这个扩展方法来指定对应的类型,否则会报错。 2、在使用ParameterOverrides进行重载参数时,可以使用如上面代码的方式进行指定,但是同样需要使用OnType来指定,不过这个的OnType指定的类型是注册的对象类型。 效果图如下: 可以看出,其中第一个字符串参数在重载后调用时已经发生了更改。 二、注册类型同时初始化属性并重载调用 这个初始化属性和上面的初始化参数很类似,只不过不同的是,属性的注册初始化是使用InjectionProperty,而重载属性是使用的PropertyOverride和PropertyOverrides,其使用方法也是相同的,这边就不多介绍了,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
public static void ResolveProperty() { //注册对象关系时初始化对象的属性 container.RegisterType<IClass, MyClass>( new InjectionProperty("Name", "A班"), new InjectionProperty("Description", "A班的描述")); Console.WriteLine("-----------默认调用输出-------------"); Console.WriteLine(container.Resolve<IClass>().Name); Console.WriteLine(container.Resolve<IClass>().Description); Console.WriteLine("-----------重载后调用输出-------------"); //以下2种写法效果是一样的,同上面的构造函数参数重载 //var myClass = container.Resolve<IClass>(new PropertyOverride("Name", "重载后的A班"), // new PropertyOverride("Description", "重载后的A班的描述")); var myClass = container.Resolve<IClass>(new PropertyOverrides() { {"Name","重载后的A班"}, {"Description","重载后的A班的描述"} }.OnType<MyClass>()); Console.WriteLine(myClass.Name); Console.WriteLine(myClass.Description); } |
效果图如下: 可以看到2个属性都已经被重载了。 Unity还为我们提供了一个DependencyOverride重载,其使用方法和参数重载、属性重载类似,这边就不演示了,不过需要注意的是DependencyOverride是针对所注册对象类型中所包含的对象类型重载,例如在A类中有构造函数参数是B类,同时也有个属性依赖于B类,当使用了DependencyOverride后,这个A对象原先注册的有关B类的依赖将全部改变。(具体可查看示例代码中的ResolveDependency) 三、延迟获取对象 Unity还有个很不错的特性就是支持延迟获取, 其本质是通过事先建立一个委托,然后再调用这个委托,看下下面的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
public static void DeferringResolve() { var resolver = container.Resolve<Func<IClass>>(); //根据业务逻辑做其他事情。 //注册IClass与MyClass之间的关系 container.RegisterType<IClass, MyClass>(); //获取MyClass实例 var myClass = resolver(); var resolver2 = container.Resolve<Func<IEnumerable<IClass>>>(); //根据业务逻辑做其他事情。 //注册与IClass相关的对象。 container.RegisterType<IClass, MyClass>("my"); container.RegisterType<IClass, YourClass>("your"); //获取与IClass关联的所有命名实例 var classList = resolver2(); } |
这段代码演示了2个延迟获取的方式,都是通过将Func<T>放入Resolve<T>中来实现的,返回的是一委托,这样就可以在实际需要的时候再调用这个委托: 1、第一种是事先通过Resolve<Func<IClass>>(); 来定义获取与IClass关联的对象的委托,然后再注册IClass与MyClass之间的关系,然后再通过resolver(); 来获取。 2、第二种是事先通过Resolve<Func<IEnumerable<IClass>>>(); 来定义获取一个与IClass关联的命名实例列表的委托,然后调用相应的委托就可以一次性获取与IClass关联的所有命名实例。 这2种方式都很好的展示了Unity可以更加灵活的控制对象之间的注册与对象的调用。 四、检索容器中注册信息 当我们在不断使用Unity容器的过程中,我们有时候想看一下容器中到底注册了多少对象,以及各个对象的一些信息,如:什么对象和什么对象关联、具体的注册名称和使用的生命周期管理器,这些信息都可以在容器的Registrations属性中查看到,在Unity文档中已经有个方法来查看这些信息了,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
public static void DisplayContainerRegistrations(IUnityContainer theContainer) { string regName, regType, mapTo, lifetime; Console.WriteLine("容器中 {0} 个注册信息:", theContainer.Registrations.Count()); foreach (ContainerRegistration item in theContainer.Registrations) { regType = item.RegisteredType.Name; mapTo = item.MappedToType.Name; regName = item.Name ?? "[默认]"; lifetime = item.LifetimeManagerType.Name; if (mapTo != regType) { mapTo = " -> " + mapTo; } else { mapTo = string.Empty; } lifetime = lifetime.Substring(0, lifetime.Length - "生命周期管理器".Length); Console.WriteLine("+ {0}{1} '{2}' {3}", regType, mapTo, regName, lifetime); } } |
具体的注册代码如下:
1 2 3 4 5 6 7 8 9 10 |
public static void RegisterAll() { container.RegisterType<IClass, MyClass>("my"); container.RegisterType<IClass, YourClass>("your", new ExternallyControlledLifetimeManager()); container.RegisterType<ISubject, Subject1>("subject1"); container.RegisterType<ISubject, Subject2>("subject2"); DisplayContainerRegistrations(container); } |
效果图如下: 可以看到,我在代码中注册的信息都已经很好的反应出来了。 同时如果想查看某个对象是否已经被注册,可以通过container.IsRegistered<T>来验证,这边就不演示了。 以上就是本文的所有内容了,主要介绍了Unity的Register和Resolve的一些高级应用,英文好的朋友可以直接查看Unity的官方文档。 示例代码下载:点我下载 (注意:本文示例代码是基于VS2010+Unity2.0,所以请使用VS2010打开,如果没有安装VS2010,请将相关代码复制到相应的VS中运行既可) 转自:http://www.cnblogs.com/kyo-yo/archive/2010/11/22/Learning-EntLib-Tenth-Decoupling-Your-System-Using-The-Unity-PART2-Learn-To-Use-Unity-Three.html
View Details在前一篇文章中,我简单的介绍了如何使用代码及配置文件来进行Unity容器配置,今天则继续介绍Unity容器的使用方法。 本篇文章将主要介绍: 1、注册对象之间的关系。 2、为已存在的对象注册关系。 3、Unity中Lifetime Managers介绍。 一、注册对象之间的关系 在上一篇文章中,已经简单介绍了如何使用Unity来注册对象与对象之间的关系,通过RegisterType方法来注册对象之间的关系。 首先来看下类关系图: 有2个接口类:IClass(班级接口)和ISubject(科目接口),其分别有2个实现类,现在首先要注册班级相关的对象关系,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
public static void RegisterTypeCode() { //注册IClass与MyClass之间的关系 //此处为IClass的默认注册,每次调用Resolve<IClass>都会返回MyClass container.RegisterType<IClass, MyClass>(); //如果再次进行默认注册,将会覆盖原来的注册 //container.RegisterType<IClass, YourClass>(); //注册IClass与YourClass之间的关系 //此处多了一个参数"your",表示一个命名注册,只有当调用Resolve<IClass>("your")时才会返回YourClass //这种注册方法解决了当需要为某个对象注册多个对应关系时的冲突 container.RegisterType<IClass, YourClass>("your"); //获取具体的对象并调用 IClass myClass = container.Resolve<IClass>(); IClass yourClass = container.Resolve<IClass>("your"); myClass.ShowInfo(); yourClass.ShowInfo(); //ResolveAll<IClass>方法可以一次性获取与IClass有注册关系的非默认对象实例 //也就是已命名注册的对象,所以列表中只有一个对象YourClass IEnumerable<IClass> classList = container.ResolveAll<IClass>(); foreach (var item in classList) { item.ShowInfo(); } } |
这段代码展示了使用RegisterType方法来注册对象之间的关系,需要注意的是,在进行命名注册的时候,所提供的命名参数是大小写敏感的,所以输入“your”和“Your”表示的不是一个对象注册。同时这边还展示了如何通过Resolve方法来获取所需的对象,以及使用ResolveAll方法来获取与指定对象关联的所有对象列表。 有关RegisterType方法的其他重载我这边就不详细介绍了,可以点此查看详细的重载方法介绍。 接下来看下如何使用配置文件来进行配置(和上一篇的例子类似),配置文件代码如下:
1 2 3 4 5 6 7 8 9 10 11 |
<unity xmlns="http://schemas.microsoft.com/practices/2010/unity"> <alias alias="IClass" type="UnityStudyConsole.IDemo.IClass, UnityStudyConsole" /> <alias alias="MyClass" type="UnityStudyConsole.Demo.MyClass, UnityStudyConsole" /> <alias alias="YourClass" type="UnityStudyConsole.Demo.YourClass, UnityStudyConsole" /> <container name="First"> <register type="IClass" mapTo="MyClass" /> <register type="IClass" mapTo="YourClass" name="your" /> </container> </unity> <span style="font-family: Verdana;">读取并调用代码如下:</span> |
1 2 3 4 5 6 7 8 9 10 |
public static void RegisterTypeCodeConfiguration() { //获取指定名称的配置节 UnityConfigurationSection section = (UnityConfigurationSection)ConfigurationManager.GetSection("unity"); container.LoadConfiguration(section, "First"); IClass classInfo = container.Resolve<IClass>(); classInfo.ShowInfo(); } |
二、为已存在的对象注册关系 在日常开发的过程中我们有时候会自己创建好一个对象,但是你又想对这个已经创建好的对象的生命周期进行管理,这个时候你可以使用Unity提供的RegisterInstance方法,代码示例如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
public static void RegisterInstance() { IClass myClass = new MyClass(); IClass yourClass = new YourClass(); //为myClass实例注册默认实例 container.RegisterInstance<IClass>(myClass); //为yourClass实例注册命名实例,同RegisterType container.RegisterInstance<IClass>("yourInstance", yourClass); container.Resolve<IClass>().ShowInfo(); container.Resolve<IClass>("yourInstance").ShowInfo(); } |
这段代码很简单,就是通过RegisterInstance方法为已存在的对象进行注册,这样可以通过UnityContainer来管理这些对象实例的生命周期。 需要注意的是,使用RegisterInstance来将已存在的实例注册到UnityContainer中,默认情况下其实用的是ContainerControlledLifetimeManager,这个生命周期是由UnityContainer来进行管理,UnityContainer会维护一个对象实例的强引用,当你将已存在的实例注册到UnityContainer后,每次通过Resolve方法获取对象都是同一对象,也就是单件实例(singleton instance),具体有关生命周期相关信息在下面进行介绍。 由于RegisterInstance是对已存在的实例进行注册,所以无法通过配置文件来进行配置。 有关RegisterInstance方法的其他重载我这边就不详细介绍了,可以点此查看详细的重载方法介绍。 三、Unity中Lifetime Managers介绍 我们在系统中引入Unity主要就是想通过Unity来解除对象之间的依赖关系,方便我们根据配置调用到所需的对象,而Unity默认情况下会自动帮我们维护好这些对象的生命周期,可能Unity自动维护的生命周期并不是我们想要的,我们想要根据具体的需求来更改这些对象的生命周期,下面我就介绍一下Unity中内置的生命周期管理器。 1、TransientLifetimeManager,瞬态生命周期,默认情况下,在使用RegisterType进行对象关系注册时如果没有指定生命周期管理器则默认使用这个生命周期管理器,这个生命周期管理器就如同其名字一样,当使用这种管理器的时候,每次通过Resolve或ResolveAll调用对象的时候都会重新创建一个新的对象。 需要注意的是,使用RegisterInstance对已存在的对象进行关系注册的时候无法指定这个生命周期,否则会报异常。 代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 |
public static void TransientLifetimeManagerCode() { //以下2种注册效果是一样的 container.RegisterType<IClass, MyClass>(); container.RegisterType<IClass, MyClass>(new TransientLifetimeManager()); Console.WriteLine("-------TransientLifetimeManager Begin------"); Console.WriteLine("第一次调用RegisterType注册的对象HashCode:" + container.Resolve<IClass>().GetHashCode()); Console.WriteLine("第二次调用RegisterType注册的对象HashCode:" + container.Resolve<IClass>().GetHashCode()); Console.WriteLine("-------TransientLifetimeManager End------"); } |
配置文件如下:
1 2 3 4 5 |
<register type="IClass" mapTo="MyClass"> <lifetime type="transient" /> <!--<lifetime type="SessionLifetimeManager" value="Session#1" typeConverter="SessionLifetimeConverter" />--> </register> |
如果想在配置文件中在在注册关系的时候更改一个生命周期管理器只需在<register>配置节下新增<lifetime>既可(如果不新增则默认使用TransientLifetimeManager)。 其中<lifetime>有3个参数: 1)type,生命期周期管理器的类型,这边可以选择Unity内置的,也可以使用自定义的,其中内置的生命周期管理器会有智能提示。 2)typeConverter,生命周期管理器转换类,用户自定义一个生命周期管理器的时候所创建一个转换器。 3)value,初始化生命周期管理器的值。 配置文件读取代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
public static void TransientLifetimeManagerConfiguration() { //获取指定名称的配置节 UnityConfigurationSection section = (UnityConfigurationSection)ConfigurationManager.GetSection("unity"); container.LoadConfiguration(section, "First"); Console.WriteLine("-------TransientLifetimeManager Begin------"); Console.WriteLine("第一次调用RegisterType注册的对象HashCode:" + container.Resolve<IClass>("transient").GetHashCode()); Console.WriteLine("第二次调用RegisterType注册的对象HashCode:" + container.Resolve<IClass>("transient").GetHashCode()); Console.WriteLine("-------TransientLifetimeManager End------"); } |
效果图如下,可以看出每次产生的对象都是不同的: 2、ContainerControlledLifetimeManager,容器控制生命周期管理,这个生命周期管理器是RegisterInstance默认使用的生命周期管理器,也就是单件实例,UnityContainer会维护一个对象实例的强引用,每次调用的时候都会返回同一对象,示例代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
public static void ContainerControlledLifetimeManagerCode() { IClass myClass = new MyClass(); //以下2种注册效果是一样的 container.RegisterInstance<IClass>("ccl", myClass); container.RegisterInstance<IClass>("ccl", myClass, new ContainerControlledLifetimeManager()); container.RegisterType<IClass, MyClass>(new ContainerControlledLifetimeManager()); Console.WriteLine("-------ContainerControlledLifetimeManager Begin------"); Console.WriteLine("第一次调用RegisterType注册的对象HashCode:" + container.Resolve<IClass>().GetHashCode()); Console.WriteLine("第二次调用RegisterType注册的对象HashCode:" + container.Resolve<IClass>().GetHashCode()); Console.WriteLine("第一次调用RegisterInstance注册的对象HashCode:" + container.Resolve<IClass>("ccl").GetHashCode()); Console.WriteLine("第二次调用RegisterInstance注册的对象HashCode:" + container.Resolve<IClass>("ccl").GetHashCode()); Console.WriteLine("-------ContainerControlledLifetimeManager End------"); } |
配置文件如下:
1 2 3 |
<register type="IClass" mapTo="MyClass" name="ccl"> <lifetime type="singleton" /> </register> |
效果图如下,可以看出每次获取的对象都是同一对象: 3、HierarchicalLifetimeManager,分层生命周期管理器,这个管理器类似于ContainerControlledLifetimeManager,也是由UnityContainer来管理,也就是单件实例。不过与ContainerControlledLifetimeManager不同的是,这个生命周期管理器是分层的,因为Unity的容器时可以嵌套的,所以这个生命周期管理器就是针对这种情况,当使用了这种生命周期管理器,父容器和子容器所维护的对象的生命周期是由各自的容器来管理,代码如下(RegisterInstance情况也类似,这边就不展示了):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
public static void HierarchicalLifetimeManagerCode() { container.RegisterType<IClass, MyClass>(new HierarchicalLifetimeManager()); //创建子容器 var childContainer = container.CreateChildContainer(); childContainer.RegisterType<IClass, MyClass>(new HierarchicalLifetimeManager()); Console.WriteLine("-------ContainerControlledLifetimeManager Begin------"); Console.WriteLine("第一次调用父容器注册的对象HashCode:" + container.Resolve<IClass>().GetHashCode()); Console.WriteLine("第二次调用父容器注册的对象HashCode:" + container.Resolve<IClass>().GetHashCode()); Console.WriteLine("第一次调用子容器注册的对象HashCode:" + childContainer.Resolve<IClass>().GetHashCode()); Console.WriteLine("第二次调用子容器注册的对象HashCode:" + childContainer.Resolve<IClass>().GetHashCode()); Console.WriteLine("-------ContainerControlledLifetimeManager End------"); } |
由于配置文件不能配置这种层级效果,所以配置这种生命周期时只需要更改下生命周期名称:
1 2 3 |
<register type="IClass" mapTo="MyClass" name="hl"> <lifetime type="hierarchical" /> </register> |
具体的效果图如下,可以看出父级和子级维护不同对象实例: 这边需要提一下的就是,Unity这种分级容器的好处就在于我们可以对于有不同生命周期的对象放在不同的容器中,如果一个子容器被释放,不会影响到其它子容器中的对象,但是如果根节点处父容器释放后,所有的子容器都将被释放。 4、PerResolveLifetimeManager,这个生命周期是为了解决循环引用而重复引用的生命周期,先看一下微软官方给出的实例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
public interface IPresenter { } public class MockPresenter : IPresenter { public IView View { get; set; } public MockPresenter(IView view) { View = view; } } public interface IView { IPresenter Presenter { get; set; } } public class View : IView { [Dependency] public IPresenter Presenter { get; set; } } |
从这个例子中可以看出,有2个接口IPresenter和IView,还有2个类MockPresenter和View分别实现这2个接口,同时这2个类中都包含了对另外一个类的对象属性,这个就是一个循环引用,而对应的这个生命周期管理就是针对这种情况而新增的,其类似于TransientLifetimeManager,但是其不同在于,如果应用了这种生命周期管理器,则在第一调用的时候会创建一个新的对象,而再次通过循环引用访问到的时候就会返回先前创建的对象实例(单件实例),代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
public static void PerResolveLifetimeManagerCode() { var container = new UnityContainer() .RegisterType<IPresenter, MockPresenter>() .RegisterType<IView, View>(new PerResolveLifetimeManager()); var view = container.Resolve<IView>(); var tempPresenter = container.Resolve<IPresenter>(); var realPresenter = (MockPresenter)view.Presenter; Console.WriteLine("-------PerResolveLifetimeManager Begin------"); Console.WriteLine("使用了PerResolveLifetimeManager的对象 Begin"); Console.WriteLine("通过Resolve方法获取的View对象:" + view.GetHashCode()); Console.WriteLine("View对象中的Presenter对象所包含的View对象:" + realPresenter.View.GetHashCode()); Console.WriteLine("使用了PerResolveLifetimeManager的对象 End"); Console.WriteLine(""); Console.WriteLine("未使用PerResolveLifetimeManager的对象 Begin"); Console.WriteLine("View对象中的Presenter对象:" + realPresenter.GetHashCode()); Console.WriteLine("通过Resolve方法获取的View对象:" + tempPresenter.GetHashCode()); Console.WriteLine("未使用PerResolveLifetimeManager的对象 End"); Console.WriteLine("-------PerResolveLifetimeManager Begin------"); } |
从代码中可以看出,在注册对象的时候,仅对IView和View应用了PerResolveLifetimeManager,所以第二次访问View对象会返回同一实例。 具体配置文件如下,有关构造函数注入和属性注入的内容在下一篇文章中进行介绍:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
<alias alias="IPresenter" type="UnityStudyConsole.IPresenter, UnityStudyConsole" /> <alias alias="IView" type="UnityStudyConsole.IView, UnityStudyConsole" /> <alias alias="MockPresenter" type="UnityStudyConsole.MockPresenter, UnityStudyConsole" /> <alias alias="View" type="UnityStudyConsole.View, UnityStudyConsole" /> <container name="Second"> <register type="IPresenter" mapTo="MockPresenter"> <constructor> <param name ="view" type="IView"> </param> </constructor> </register> <register type="IView" mapTo="View" > <lifetime type="perresolve"/> <property name="Presenter" dependencyType="IPresenter"></property> </register> </container> |
读取配置文件代码类似于前面其他配置文件读取代码,这里就不展示,具体请看示例代码。 具体的效果图如下: 可以看出2次调用View对象的HashCode都是一样的,而Presenter对象的HashCode不同。 5、PerThreadLifetimeManager,每线程生命周期管理器,就是保证每个线程返回同一实例,具体代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
public static void PerThreadLifetimeManagerCode() { container.RegisterType<IClass, MyClass>(new PerThreadLifetimeManager()); var thread = new Thread(new ParameterizedThreadStart(Thread1)); Console.WriteLine("-------PerResolveLifetimeManager Begin------"); Console.WriteLine("默认线程 Begin"); Console.WriteLine("第一调用:" + container.Resolve<IClass>().GetHashCode()); Console.WriteLine("第二调用:" + container.Resolve<IClass>().GetHashCode()); Console.WriteLine("默认线程 End"); thread.Start(container); } public static void Thread1(object obj) { var tmpContainer = obj as UnityContainer; Console.WriteLine("新建线程 Begin"); Console.WriteLine("第一调用:" + tmpContainer.Resolve<IClass>().GetHashCode()); Console.WriteLine("第二调用:" + tmpContainer.Resolve<IClass>().GetHashCode()); Console.WriteLine("新建线程 End"); |
Console.WriteLine("——-PerResolveLifetimeManager End——"); }
1 |
有关配置相关的代码与前面的生命周期管理器差不多,这边就不贴代码了,请看示例代码。 具体效果图如下: 同时需要注意的是,一般来说不建议在使用RegisterInstance对已存在的对象注册关系时使用PerThreadLifetimeManager,因为此时的对象已经在一个线程内创建了,如果再使用这个生命周期管理器,将无法保证其正确调用。 6、ExternallyControlledLifetimeManager,外部控制生命周期管理器,这个生命周期管理允许你使用RegisterType和RegisterInstance来注册对象之间的关系,但是其只会对对象保留一个弱引用,其生命周期交由外部控制,也就是意味着你可以将这个对象缓存或者销毁而不用在意UnityContainer,而当其他地方没有强引用这个对象时,其会被GC给销毁掉。 在默认情况下,使用这个生命周期管理器,每次调用Resolve都会返回同一对象(单件实例),如果被GC回收后再次调用Resolve方法将会重新创建新的对象,示例代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
public static void ExternallyControlledLifetimeManagerCode() { container.RegisterType<IClass, MyClass>(new ExternallyControlledLifetimeManager()); var myClass1 = container.Resolve<IClass>(); var myClass2 = container.Resolve<IClass>(); Console.WriteLine("-------ExternallyControlledLifetimeManager Begin------"); Console.WriteLine("第一次调用:" + myClass1.GetHashCode()); Console.WriteLine("第二次调用:" + myClass2.GetHashCode()); myClass1 = myClass2 = null; GC.Collect(); Console.WriteLine("****GC回收过后****"); Console.WriteLine("第一次调用:" + container.Resolve<IClass>().GetHashCode()); Console.WriteLine("第二次调用:" + container.Resolve<IClass>().GetHashCode()); Console.WriteLine("-------ExternallyControlledLifetimeManager End------"); } |
有关配置相关的代码与前面的生命周期管理器差不多,这边就不贴代码了,请看示例代码。 效果图如下: 以上就是本文的全部内容了,主要介绍了使用UnityContainer来注册对象之间的关系、注册已存在的对象之间的关系和Unity内置的生命周期管理器。 示例代码下载:点我下载 (注意:本文示例代码是基于VS2010+Unity2.0,所以请使用VS2010打开,如果没有安装VS2010,请将相关代码复制到相应的VS中运行既可) 转自:http://www.cnblogs.com/kyo-yo/archive/2010/11/10/Learning-EntLib-Tenth-Decoupling-Your-System-Using-The-Unity-PART2-Learn-To-Use-Unity-Two.html
View Details打开 .csproject 文件 把<ProjectTypeGuids>中的前半部分修改为: {F85E285D-A4E0-4152-9332-AB1D724D3325};{349c5851-65df-11da-9384-00065b846f21}; 后面还有一个 GUID 是你项目的GUID 。 三个GUID 。
View Details在上一篇文章中,我简单介绍Unity的一些背景知识、为什么要使用Unity和使用Unity有什么好处。今天将继续学习Unity,主要是了解Unity的一些常见的使用方法。 本篇文章将主要介绍: Unity中的UnityContainer基本使用介绍,包括UnityContainer类的常用方法介绍,并且通过几个小例子来介绍具体的使用方法,这些例子都会以代码及配置文件两种方式来实现。 从UnityContainer(Unity容器)这个类的名称中我们就可以看出其是Unity最重要的类,UnityContainer就像Unity的一个司令部,而一个个对象就像一名名士兵,每个士兵来去都需要通过UnityContainer来管理,其掌管了所有对象的依赖关系,所有对象的创建都会通过这个Unity容器来创建,,同时也可以说是一个外交部,对于我们开发者,我们不需要关心内部是怎么实现的,我们只需要事先设置好对象之间的关系,然后在需要的时候告诉UnityContainer我需要什么,UnityContainer就会将我们需要的直接发给我们。(这些比喻可能不正确,但是是我能想到的最好的比喻了) 使用代码实现对象关联注册: 首先来看一个简单的例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
public interface IClass { void ShowInfo(); } public class MyClass : IClass { public MyClass() { } public void ShowInfo() { Console.WriteLine("这个是我的班级"); } } 具体调用: |
1 2 3 4 5 |
static void Main(string[] args) { IClass classInfo = new MyClass(); classInfo.ShowInfo(); } |
这个是最常见的接口及其实现类的使用方法,定义一个接口,然后再定义一个类来实现这个接口,然后在具体使用的过程中,可以通过new关键字来实例化具体来实现接口,虽然没有语法上的问题,但是这样会造成紧耦合,如果一旦具体的实现类发生了改变,则就需要修改代码,而且如果这种类似的代码很多,则会导致整个项目的变动,甚至出现异常,所以我们需要使用IOC来解耦,具体代码如下:
1 2 3 4 5 6 7 8 9 10 11 |
public static void ContainerCode() { IUnityContainer container = new UnityContainer(); container.RegisterType<IClass, MyClass>(); //另一种注册方法,不过没有RegisterType<>()方法来的方便 //container.RegisterType(typeof(IClass), typeof(MyClass)); IClass classInfo = container.Resolve<IClass>(); //另一种通过container获取具体对象的方法 //IClass classInfo = container.Resolve(typeof(IClass)); classInfo.ShowInfo(); } |
使用Unity来管理对象与对象之间的关系可以分为以下几步: 1、创建一个UnityContainer对象。 2、通过UnityContainer对象的RegisterType方法来注册对象与对象之间的关系。 3、通过UnityContainer对象的Resolve方法来获取指定对象关联的对象。 使用配置文件来实现对象关系注册: 以上是通过代码的方式来注册对象之间的关系,但是对于一个项目来说,正式部署后,由于代码都编译成DLL,如果要修改依赖关系则修改代码并重新编译,相对来说太麻烦了,所以Unity还提供配置文件配置的方式来配置对象之间的关系,配置如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
<?xml version="1.0" encoding="utf-8" ?> <configuration> <configSections> <section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Microsoft.Practices.Unity.Configuration"/> </configSections> <unity xmlns="http://schemas.microsoft.com/practices/2010/unity"> <alias alias="IClass" type="UnityStudyConsole.IDemo.IClass, UnityStudyConsole" /> <alias alias="MyClass" type="UnityStudyConsole.Demo.MyClass, UnityStudyConsole" /> <container> <register type="IClass" name="ConfigClass" mapTo="MyClass" /> </container> </unity> </configuration> |
具体代码调用如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
public static void ContainerConfiguration() { IUnityContainer container = new UnityContainer(); //获取指定名称的配置节 UnityConfigurationSection section = (UnityConfigurationSection)ConfigurationManager.GetSection("unity"); //默认方法,默认获取名称为"unity"配置节下配置信息 container.LoadConfiguration(); //获取已命名的配置节<container name="FirstClass">下的配置信息 container.LoadConfiguration("FirstClass"); //获取特定配置节下配置信息 container.LoadConfiguration(section); //获取特定配置节下已命名的配置节<container name="FirstClass">下的配置信息 container.LoadConfiguration(section, "FirstClass"); IClass classInfo = container.Resolve<IClass>("ConfigClass"); classInfo.ShowInfo(); } |
通过配置文件配置Unity信息需要有以下几个步骤: 1、在配置文件中<configSections> 配置节下unity注册。 2、在<configuration> 配置节下添加Unity配置信息。 3、在代码中读取配置信息,并将配置载入到UnityContainer中。 使用配置文件来配置对象信息虽然可以在部署的时候更改对象之间的依赖关系,但是当系统过于复杂,则就会导致配置文件的增大,所以我们需要将Unity的配置信息从App.config或web.config中分离出来,但是这样的话前面的代码中调用方法就无效了,我们现在需要修改一下现有的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
public static void ContainerConfigurationFromFile(string configFile) { //根据文件名获取指定config文件 var fileMap = new ExeConfigurationFileMap { ExeConfigFilename = configFile }; //从config文件中读取配置信息 Configuration configuration = ConfigurationManager.OpenMappedExeConfiguration(fileMap, ConfigurationUserLevel.None); var unitySection = (UnityConfigurationSection)configuration.GetSection("unity"); var container = new UnityContainer() .LoadConfiguration(unitySection, "FirstClass"); IClass classInfo = container.Resolve<IClass>("ConfigClass"); classInfo.ShowInfo(); } |
由于Unity配置相对来说比较复杂,一下子难以上手,而且不像企业库的其他模块可以通过配置工具进行配置,所以p&p小组为了方便通过配置文件进行Unity配置,已经在企业库5.0的安装包中内置了UnityConfiguration20.xsd,你可以在X:\Program Files\Microsoft Visual Studio X.0\Xml\Schemas\下找到,这样就可以在配置文件中进行Unity配置时出现只能提示了,如下图: 需要注意的是,如果想出现这种提示,需要在新增的unity配置节中增加一个xmlns为http://schemas.microsoft.com/practices/2010/unity。(如果没有安装过EntLib5,所以没有UnityConfiguration20.xsd,可以在下面的示例代码中找到这个xsd。) 以上就是本文的所有内容了,主要是简单介绍一下UnityContainer的基本使用方法——如何通过代码及配置文件来实现对象之间关系,如有不对欢迎指出! 示例代码下载:点我下载 转自:http://www.cnblogs.com/kyo-yo/archive/2010/11/08/Learning-EntLib-Tenth-Decoupling-Your-System-Using-The-Unity-PART2-Learn-To-Use-Unity-One.html
View Details在前面的Part3中,我介绍Policy Injection模块中内置的Call Handler的使用方法,今天则继续介绍Call Handler——Custom Call Handler,通过建立Custom Call Handler来实现项目中的用户操作日志的记录,具体的代码可以在项目中EntLib.Helper项目下找到,如下图: 本文将从Custom Call Handler两种方式来介绍:Attribute方式和Configuration方式。 一、核心代码 建立Custom Call Handler则需要有以下几个步骤: 1、建立一个类实现接口ICallHandler。 2、根据具体需求建立对应Attribute类或为Custom Call Handler实现特性[ConfigurationElementType(typeof(CustomCallHandlerData))] 首先来介绍下具体的核心代码,由于我是要实现用户的操作日志,则需要对用户的对数据的增删改以及一些特殊的操作进行记录,如:登录, 1、首先需要建立一张表用于存放用户操作记录:
1 2 3 4 5 6 7 8 9 10 |
CREATE TABLE [dbo].[UserLog]( [ID] [int] IDENTITY(1,1) NOT NULL,--主键 [StudentId] [int] NOT NULL,--对应学生ID [Message] [nvarchar](256) NOT NULL,--操作消息 [LogDate] [datetime] NOT NULL,--记录时间 CONSTRAINT [PK_UserLog] PRIMARY KEY CLUSTERED ( [ID] ASC )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] ) ON [PRIMARY] |
2、建立一个名为UserLogCallHandler的类来实现接口ICallHandler,实现其中的方法Invoke(具体的拦截操作方法)和属性Order,具体代码如下(关键处我都写好注释了)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 |
using System; using System.Collections.Generic; using System.Collections.Specialized; using System.Data; using System.Data.Common; using System.Linq; using System.Text; using Microsoft.Practices.EnterpriseLibrary.Common.Configuration; using Microsoft.Practices.EnterpriseLibrary.Data; using Microsoft.Practices.Unity.InterceptionExtension; namespace EntLibStudy.Helper.EntLibExtension.PolicyInjectionExtension { [ConfigurationElementType(typeof(CustomCallHandlerData))] public class UserLogCallHandler : ICallHandler { /// <summary> /// 构造函数,此处不可省略,否则会导致异常 /// </summary> /// <param name="attributes">配置文件中所配置的参数</param> public UserLogCallHandler(NameValueCollection attributes) { //从配置文件中获取key,如不存在则指定默认key this.Message = String.IsNullOrEmpty(attributes["Message"]) ? "" : attributes["Message"]; this.ParameterName = String.IsNullOrEmpty(attributes["ParameterName"]) ? "" : attributes["ParameterName"]; } /// <summary> /// 构造函数,此构造函数是用于Attribute调用 /// </summary> /// <param name="message">消息</param> /// <param name="parameterName">参数名</param> public UserLogCallHandler(string message, string parameterName) { this.Message = message; this.ParameterName = parameterName; } /// <summary> /// 实现ICallHandler.Invoke方法,用于对具体拦截方法做相应的处理 /// </summary> /// <param name="input"></param> /// <param name="getNext"></param> /// <returns></returns> public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext) { //检查参数是否存在 if (input == null) throw new ArgumentNullException("input"); if (getNext == null) throw new ArgumentNullException("getNext"); //开始拦截,此处可以根据需求编写具体业务逻辑代码 //调用具体方法 var result = getNext()(input, getNext); //判断所拦截的方法返回值是否是bool类型, //如果是bool则判断返回值是否为false,false:表示调用不成功,则直接返回方法不记录日志 if (result.ReturnValue.GetType() == typeof(bool)) { if (Convert.ToBoolean(result.ReturnValue) == false) { return result; } } //如果调用方法没有出现异常则记录操作日志 if (result.Exception == null) { //获取当前登录的用户名,从cookies中获取,如果采用的session记录,则更改为从session中获取 var uid = Utils.GetCookies("sid"); //如果未登录则抛出异常 if (String.IsNullOrEmpty(uid)) throw new Exception("用户未登录!"); //操作附加消息,用于获取操作的记录相关标识 var actionMessage = ""; object para = null; //判断调用方法的主要参数名是否为空,不为空则从拦截的方法中获取参数对象 if (String.IsNullOrEmpty(this.ParameterName) == false) { para = input.Inputs[this.ParameterName]; } //判断参数对象是否为null,不为null时则获取参数标识 //此处对应着具体参数的ToString方法,我已经在具体类中override了ToString方法 if (para != null) { actionMessage = " 编号:[" + para.ToString() + "]"; } //插入操作日志 Database db = DBHelper.CreateDataBase(); StringBuilder sb = new StringBuilder(); sb.Append("insert into UserLog(StudentId,Message,LogDate) values(@StudentId,@Message,@LogDate);"); DbCommand cmd = db.GetSqlStringCommand(sb.ToString()); db.AddInParameter(cmd, "@StudentId", DbType.Int32, uid); db.AddInParameter(cmd, "@Message", DbType.String, this.Message + actionMessage); db.AddInParameter(cmd, "@LogDate", DbType.DateTime, DateTime.Now); db.ExecuteNonQuery(cmd); } //返回方法,拦截结束 return result; } public string Message { get; set; } public string ParameterName { get; set; } private int _order = 0; public int Order { get { return _order; } set { _order = value; } } } } |
这段代码主要部分就是具体的Invoke方法实现,这个方法有2个参数: input,这个参数中封装了已拦截的方法、方法的参数等有用的信息 getNext,一个委托,用于调用拦截的方法,通过这个委托我们可以很好的控制我们需要在拦截了具体方法后如何进行具体的业务逻辑操作。 通过getNext()(input, getNext); 这段代码即可完成对方法的调用,这样可以根据具体需求决定在调用方法前还是方法后进行具体操作。 由于我这边是要实现一个用户操作记录,那么我要知道一些具体的信息:是谁在什么时候对什么数据做了操作,这边我需要获取3个参数:具体的操作人、操作的数据及具体描述。 首先来看下第一个参数: ◆具体的操作人,由于这个项目采用的是cookies来记录当前的登录用户,所以我可以直接从cookies中获取当前登录的人,具体可以查看代码69-71行。 ◆操作的数据,这边我在这个Call Handler中建立了一个ParameterName属性用来指定记录所拦截的方法中存放所操作数据的参数名,具体可以查看代码74-86行。 由于指定了具体的参数名,我们则需要根据参数获取具体数据值,我们来看下增删改的方法签名: int Add(Student student); bool Update(Student student); bool Delete(int id); 可以看到,我们都可以从这3个方法获取到用户具体操作的数据标识,如Student.Id和id,这样我们只需变通一下,在具体的类中,如Student中,重写ToString方法,返回具体的ID即可,代码如下:
1 2 3 4 |
public override string ToString() { return this.Id.ToString(); } |
这样,我们在Call Handler中我们就可以根据参数名获取到具体操作的数据了(如果需要详细描述具体的数据的话则需要更复杂的设计了,这边就不深入了),代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
//操作附加消息,用于获取操作的记录相关标识 var actionMessage = ""; object para = null; //判断调用方法的主要参数名是否为空,不为空则从拦截的方法中获取参数对象 if (String.IsNullOrEmpty(this.ParameterName) == false) { para = input.Inputs[this.ParameterName]; } //判断参数对象是否为null,不为null时则获取参数标识 //此处对应着具体参数的ToString方法,我已经在具体类中override了ToString方法 if (para != null) { actionMessage = " 编号:[" + para.ToString() + "]"; } |
◆具体描述,这个我也是建立一个Message数据,用于存放操作的具体描述。 ◆特殊情况,当然操作日志也不可能就仅仅增删改3种情况,就比如登录,注销,这种情况则只需指定具体的消息即可,参数名无需指定,如果还有更加特殊的情况则需要根据具体需求来更改这边的设计,我这边只是给出个最基本的。 二、Attribute实现 在完成了核心代码后,我们则可以根据需求建立Attribute拦截还是Configuration拦截了。 实现Attribute拦截,需要建立一个类,实现HandlerAttribute类,实现其中的CreateHandler方法,用于调用具体的Call Handler方法,具体代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
using System; using System.Collections.Generic; using System.Linq; using System.Text; using Microsoft.Practices.Unity; using Microsoft.Practices.Unity.InterceptionExtension; namespace EntLibStudy.Helper.EntLibExtension.PolicyInjectionExtension { [AttributeUsage(AttributeTargets.Method)] public class UserLogCallHandlerAttribute : HandlerAttribute { public UserLogCallHandlerAttribute(string message, string ParameterName) { this.Message = message; this.ParameterName = ParameterName; } public string Message { get; set; } public string ParameterName { get; set; } public override ICallHandler CreateHandler(IUnityContainer container) { //创建具体Call Handler,并调用 UserLogCallHandler handler = new UserLogCallHandler(this.Message, this.ParameterName); return handler; } } } |
这个特性类就比较简单了,不过还需要在Call Handler中进行处理,增加一个构造函数,接收从Attribute中传递过来的参数:
1 2 3 4 5 6 7 8 9 10 |
/// <summary> /// 构造函数,此构造函数是用于Attribute调用 /// </summary> /// <param name="message">消息</param> /// <param name="parameterName">参数名</param> public UserLogCallHandler(string message, string parameterName) { this.Message = message; this.ParameterName = parameterName; } |
三、Configuration方式 如果要实现可以通过企业库配置工具进行配置Custom Call Handler的话,则需要对Call Handler增加一个特性: [ConfigurationElementType(typeof(CustomCallHandlerData))] 然后新增一个构造函数
1 2 3 4 5 6 7 8 9 10 |
/// <summary> /// 构造函数,此处不可省略,否则会导致异常 /// </summary> /// <param name="attributes">配置文件中所配置的参数</param> public UserLogCallHandler(NameValueCollection attributes) { //从配置文件中获取key,如不存在则指定默认key this.Message = String.IsNullOrEmpty(attributes["Message"]) ? "" : attributes["Message"]; this.ParameterName = String.IsNullOrEmpty(attributes["ParameterName"]) ? "" : attributes["ParameterName"]; } |
完成以上2步我们就可以通过企业库配置工具进行配置了,见下图: 四、具体使用 在完成了Call Handler的代码编写和登录拦截配置后,我们就可以进行使用了,我这边更改了项目的结构,建立了一个IBLL的接口层,现有的BLL层的类则实现IBLL层中接口,而且由于Policy Injection模块要实现AOP,则具体类必须继承自MarshalByRefObject或实现一个接口(如果不清楚可以查看Part1),所以为了项目的各模块解耦、方便Policy Injection对具体类的创建和未来Unity介绍做铺垫则创建了IBLL层。(具体可以参看项目代码) 由于建立了IBLL层,则表示层的代码则需要发生变化,所有BLL层创建都需要通过PolicyInjection.Create方法来创建,具体代码如下:
1 |
IStudentManage studentBll = PolicyInjection.Create<StudentManage, IStudentManage>(); |
这样,当我们运行代码后,进入数据库查看就可以看,操作日志已经被记录下来了。 上面说的是通过Configuration方式来进行操作日志记录,如果我们想通过Attribute方式来记录日志消息,则需要到具体的BLL层进行操作,代码如下:
1 2 3 4 5 |
[UserLogCallHandler("更新学生信息","student")] public bool Update(Student student) { return studentService.Update(student); } |
注意:这边需要为项目引用Microsoft.Practices.Unity.Interception,因为Call Handler的Attribute是继承自HandlerAttribute,这个HandlerAttribute就是存放于Microsoft.Practices.Unity.Interception,否则自定义的Call Handler Attribute将无法显示出来。 这样,更新下学生信息后,我们可以就可以看到具体的操作日志了,见下图: 以上就是本文的所有内容,主要介绍了如何通过Custom Call Handler实现用户操作日志记录,如果有什么不对,欢迎大家指出,谢谢:) 至此,Policy Injection模块的介绍也结束了,下面将开始介绍企业库中使用最广泛的IOC容器——Unity,敬请期待! 转自:http://www.cnblogs.com/kyo-yo/archive/2010/10/13/Learning-EntLib-Ninth-Use-PolicyInjection-Module-AOP-PART4-Call-Handler-To-Build-User-Logging.html
View Details在前面的Part1和Part2中我已经介绍了PolicyInjection模块的内置的Matching Rule和自定义Matching Rule的基本信息及配置使用方法,不过光有Matching Rule进行验证匹配还不够,还必须要有相应的操作——Call Handler,所以今天继续介绍PolicyInjection模块内置的Call Handler。 一、PolicyInjection模块内置的Call Handler 通过将Call Handler和Part1、Part2中的Matching Rule组合起来,我们就可以实现AOP编程,如果说Matching Rule是为了匹配寻找需要进行AOP操作的对象的话,那Call Handler则是在找到对象后进行的AOP操作。在PolicyInjection模块中内置了以下6种Call Handler: 1、Authorization Call Handler——权限操作 2、Custom Call Handler——自定义操作 3、Exception Handling Call Handler——异常处理操作 4、Logging Call Handler——日志记录操作 5、Performance Counter Call Handler——性能计数器操作 6、Validation Call Handler——验证操作 在Part1中已经介绍过,PolicyInjection模块可以和企业库中的其他模块组合起来一起使用,而内置的Call Handler也基本上都是企业库所包含的且适用于AOP编程的模块,具体的Call Handler代码可以在每个模块源码下的PolicyInjection文件夹下找到,比如:Authorization Call Handler就是在Security Application Block\PolicyInjection,主要分为:AuthorizationCallHandler.cs和AuthorizationCallHandlerAttribute.cs。 下面我介绍下这6种内置Call Handler的使用及配置方式: 二、Authorization Call Handler——权限操作 Authorization Call Handler主要和Security模块进行关联,通过这个Call Handler来调用已经配置好的Authorization Provider,主要接受3个参数: 1、Authorization Provider,指向的是所配置的具体的Authorization Provider。 2、Operation Name,验证的对象,可以是{method},{type},{namespace},{assembly}和{appdomian}。 3、Order,这个是表示被调用的次序,默认为0(如果制定了次序则将从1开始,0表示不按照次序执行)。 具体配置图如下: 相对应的如果想通过代码来实现这样的效果,需要进行以下操作: 1、引用:Microsoft.Practices.EnterpriseLibrary.Security.PolicyInjection; 2、为指定方法添加特性:[AuthorizationCallHandler("{method}-具体的验证操作名")] 三、Custom Call Handler——自定义操作 这个将在下篇文章中重点介绍 四、Exception Handling Call Handler——异常处理操作 Exception Handling Call Handler对应的是Exception Handling模块,接受的参数很简单,只有2个: 1、Expection Policy Name,指向的是具体的Exception处理策略(有关Exception相关的文章可以看学习之路——第三步、为项目加上异常处理(采用自定义扩展方式记录到数据库中))。 2、Order,同上。 具体配置图如下: 相对应的如果想通过代码来实现这样的效果,需要进行以下操作: 1、引用:Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.PolicyInjection; 2、为指定方法添加特性:[ExceptionCallHandler("Policy-具体异常策略名")] 五、Logging Call Handler——日志记录操作 Logging Call Handler对应的是Logging模块,相比前几个Call Handler,Logging Call Handler接受的参数就比较多了,有11个: 1、After/Before Message,类似于Logging模块中的消息配置中的Message Header/Footer,主要是对消息的开始和结尾处加上额外的信息,如:“--Call Handler开始—”。 2、Categories,指向的hi具体的Logging配置,(有关Logging相关的文章可以看学习之路——第三步、为项目加上异常处理(采用自定义扩展方式记录到数据库中))。 3、Event Id,事件ID,默认为0,如果有需要可以指定。 4、Include Call Stack,表示是否在日志记录消息中包含调用栈信息,默认为false,不添加调用栈信息。 5、Include Call Time,表示是否在日志记录消息中包含调用的时间,默认为true,在日志消息中添加调用时间。 6、Include Paramters Values,表示是否在日志记录消息中包含调用的方法中所传递的参数值,默认为true,在日志消息中添加参数值。 7、Log Behavior,日志行为,主要用于设置日志记录的位置,有3个枚举,BeforeAndAfter(之前和之后),Before(之前)和After(之后)。 8、Order,同上。 9、Prority,表示日志消息中异常记录的优先级,默认为-1。 10、Severity,日志级别。 具体配置图如下: 相对应的如果想通过代码来实现这样的效果,需要进行以下操作: 1、引用:Microsoft.Practices.EnterpriseLibrary.Logging.PolicyInjection; 2、为指定方法添加特性:[LogCallHandler(Categories = new string[] { "Category" })],在LogCallHandler里面还对应着许多可选参数,都可以自定义指定,如不指定则为默认值。 六、Performance Counter Call Handler——性能计数器操作 Performance Counter Call Handler与其他几个Call Handler不同,其不对应企业库的其他模块(具体代码可以在PolicyInjection\CallHandlers下找到,分别是PerformanceCounterCallHandler.cs和PerformanceCounterCallHandlerAttribute.cs) 这个Performance Counter Call Handler主要是对所拦截的策略进行性能检测,如果想使用需要以下步骤: 1、企业库配置图: 主要设置2个属性: 1)Category Name:计数器名——abc 2)Instance Name:实例名——Default 至于其他属性可以不用更改,主要是对性能监视器的一些配置。 2、安装性能计数器,需要在项目中引入:System.Configuration.Install 代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 |
private static void InstallCounter() { PerformanceCountersInstaller installer = new PerformanceCountersInstaller( new SystemConfigurationSource()); IDictionary state = new System.Collections.Hashtable(); installer.Context = new InstallContext(); installer.Install(state); installer.Commit(state); Console.WriteLine("性能计数器已安装完成,输入【enter】继续!"); Console.ReadLine(); } |
3、调用具体的方法,同时查看其性能:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
static void Main(string[] args) { InstallCounter();//安装性能计数器 Demo aop = PolicyInjection.Create<Demo>(); for (int i = 0; i < 50000; i++)//循环50000次,主要是为了方便监视性能 { System.Threading.Thread.Sleep(50); aop.Test("wozhidao");//此处使用的是Member Name Matching Rule,具体可看前一篇文章 } Console.ReadLine(); } public class Demo : MarshalByRefObject { public void Test(string aa) { string a = aa; } } |
在调用到InstallCounter();时在Console中会出现:这时,打开计算机管理-性能-监视工具-添加,可以看到我们刚才创建的性能计数器已经存在列表当中:添加完成后,我们就可以在性能计数器中看到我们现在所正在运行的方法的性能了:4、卸载性能计数器:
1 2 3 4 5 6 7 8 9 |
private static void RemoveCounters() { PerformanceCountersInstaller installer = new PerformanceCountersInstaller( new SystemConfigurationSource()); installer.Context = new InstallContext(); installer.Uninstall(null); Console.WriteLine("性能计数器已移除完成,输入【enter】继续!"); Console.ReadLine(); } |
卸载后,刚才我们所安装的计数器就移除了。 以上性能计数器使用方法参考:Getting started with Enterprise Library Performance Counters 七、Validation Call Handler——验证操作 Validation Call Handler对应的是Validation模块,这个Call Handler主要接收3个参数: 1、Order,同上。 2、Rule Source,规则来源,有4个枚举可选,分别是:Both(所有来源),Attributes(特性),Configuration(配置)和ParameterAttributesOnly(仅参数特性)。 3、RuleSet,规则集,指向具体的验证配置规则集(有关Validation相关的文章可以看学习之路——第五步、介绍EntLib.Validation模块信息、验证器的实现层级及内置的各种验证器的使用方法——上篇、中篇、下篇和学习之路——第六步、使用Validation模块进行服务器端数据验证)。 具体配置图如下: 同样的Vadidation Call Handler可以不通过配置进行操作,可以直接在代码中通过特性来进行AOP。 1、首先需要引用命名空间: Microsoft.Practices.EnterpriseLibrary.PolicyInjection; Microsoft.Practices.EnterpriseLibrary.PolicyInjection.CallHandlers; Microsoft.Practices.EnterpriseLibrary.Validation.Validators; Microsoft.Practices.EnterpriseLibrary.Validation.PolicyInjection; 2、为方法添加特性:[ValidationCallHandler],然后为方法参数指定验证特性:[StringLengthValidator(6,16,MessageTemplate="输出的消息长度必须在6-16之间!")] 代码如下:
1 2 3 4 5 6 |
[ValidationCallHandler] public void abc([StringLengthValidator(6,16,MessageTemplate="输出的消息长度必须在6-16之间!")] string cw) { Console.WriteLine(cw); } |
注意:如果需要在代码中使用企业库的验证模块的特性类,需要为项目添加System.ComponentModel.DataAnnotations引用。 以上就是本文的所有内容了,本文主要是对Policy Injection模块内置的Call Handler的一些配置进行简单的介绍,如果不对欢迎大家指出,下篇将主要介绍Custom Call Handler的创建及使用方法。 […]
View Details这段时间公司有新项目需要开发,所以这个企业库学习之路有点耽误了,今天继续接着上一篇文章,在上一篇文章中,我介绍了企业库的自带Matching Rule(匹配规则)的基本信息及使用方法,但是在PolicyInjection模块中的Matching Rule(匹配规则)还有2个规则没有介绍,分别是: 1、Custom Attribute Matching Rule——自定义特性匹配规则 2、Custom Matching Rule——自定义匹配规则 今天就主要对这2个匹配规则进行介绍,及有关这2个匹配规则的一些应用例子。 一、Custom Attribute Matching Rule——自定义特性匹配规则 这个匹配规则有点类似于Tag Attribute Matching Rule,只不过Tag Attribute Matching Rule已经定死了特性类——TagAttribute,具体匹配需要靠TagAttribute特性类的tag字符串来标识具体的策略名,使用起来不怎么方便,而且使用字符串来标识,可能会写错,不过企业库同样为我们预留了扩展接口,就是这个自定义特性匹配规则——Custom Attribute Matching Rule。 这个匹配规则使用起来很简单,只需自己定义个特性类,然后在企业库配置工具中进行配置既可。 这个匹配规则接收2个参数:Attribute Type Name和Search Inheritance Chain 1、Attribute Type Name,这个意思很明确了,就是特性类的名称,只需点击旁边的选择按钮即可选择到自己定义的特性类。 2、Search Inheritance Chain,这个是表示是否要查询继承关系,默认为false,表示不查询,看下面的代码就可以理解了: 我首先定义了一个自定义的特性类:
1 2 3 4 |
[AttributeUsage(AttributeTargets.Method)] public class MyCustomAttribute:System.Attribute { } |
然后在应用到代码中:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
public abstract class CustomAttrTest : MarshalByRefObject { [MyCustom] public abstract void CwInfo(string aa); } public class ChildCustomerAttrTest : CustomAttrTest { public override void CwInfo(string aa) { Console.WriteLine(aa); } } |
可以看到我将MyCustomAttribute应用到了抽象类CustomAttrTest中的抽象方法CwInfo上,然后由子类ChildCustomerAttrTest来实现,如果将Search Inheritance Chain设置为false,则在方法调用的过程中将不会被拦截,因为MyCustomAttribute是应用在抽象类中,而不是应用在实现类中,所以如果想依靠抽象类来定义拦截,则需要将Search Inheritance Chain设置为true。 具体配置图如下: 注:由于PolicyInjection模块的一个BUG问题,在通过PolicyInjection创建对象的时候,如果创建的对象需要指向一个抽象类将会报错,代码如下:
1 |
CustomAttrTest customAttr = PolicyInjection.Create<ChildCustomerAttrTest, CustomAttrTest>(); |
具体的问题,可以参考Artech写的这篇文章:这是EnterLib PIAB的BUG吗?,里面介绍了这个问题的产生原因及解决办法。 通过Custom Attribute Matching Rule我们可以根据我们自己的具体需求自由建立特性类,比如:异常、权限、日志等等,通过自己来建立特性类,这样我们可以很好的分隔各个功能,在具体的时候过程中我们可以自由组合所需要的功能,如下代码: 我首先定义几个需要使用的特性类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
[AttributeUsage(AttributeTargets.Method)] public class ExceptionAttribute : System.Attribute { } [AttributeUsage(AttributeTargets.Method)] public class LoggingAttribute : System.Attribute { } [AttributeUsage(AttributeTargets.Method)] public class SecurityAttribute : System.Attribute { } public class MyCommonAttribute : System.Attribute { } |
然后为每个特性类关联不同处理操作: 最后使用的时候只需根据需要自己为方法添加特性既可:
1 2 3 4 5 6 |
[MyCustom] [Logging] [Exception] public void Delete(int id) { } |
二、Custom Matching Rule——自定义匹配规则 如果企业库内置的那么多匹配规则都无法达到你想要的话,你可以尝试着自己编写匹配规则,当然企业库也预留了这个接口,就是Custom Matching Rule。如果要自己实现一个Custom Matching Rule,主要分为2步: 1、建立一个类继承自接口IMatchingRule(这个接口是属于Microsoft.Practices.Unity.InterceptionExtension,需要引用Unity.dll),并实现方法bool Matches(System.Reflection.MethodBase member)。 2、为类加上特性[ConfigurationElementType(typeof(CustomMatchingRuleData))](需要引用命名空间Microsoft.Practices.EnterpriseLibrary.Common.Configuration和Microsoft.Practices.EnterpriseLibrary.PolicyInjection.Configuration),标识其继承自CustomMatchingRuleData,可以被企业库配置工具识别到。 代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
using Microsoft.Practices.Unity.InterceptionExtension; using Microsoft.Practices.EnterpriseLibrary.Common.Configuration; using Microsoft.Practices.EnterpriseLibrary.PolicyInjection.Configuration; namespace AOPAssembly { [ConfigurationElementType(typeof(CustomMatchingRuleData))] public class MyCustomMatchRule : IMatchingRule { NameValueCollection attributes = null; public MyCustomMatchRule(NameValueCollection attributes) { this.attributes = attributes; } public bool Matches(System.Reflection.MethodBase member) { bool result = false; if (this.attributes["matchname"] != null) { if (this.attributes["matchname"] == member.Name) { result = true; } } return result; } } } |
配置图如下: 这样就可以在Matches方法中编写匹配方法,我这边写了个简单匹配,对根据方法名进行匹配。 这个匹配是通过配置工具中配置的方法名进行匹配,如果配置的方法名和所调用的方法名相同则返回匹配成功。这里需要注意的是,如果想要在自己实现的自定义匹配类获取到配置工具中所定义的配置,需要定义一个构造函数,接收类型为NameValueCollection的参数,我这边就通过在类中定义一个attributes参数来接收配置工具中所配置的参数,然后在Matches方法中进行处理。 以上就是本文的所有内容,本文主要介绍了自定义特性匹配规则和自定义匹配规则的创建及使用方法,如有不对之处欢迎大家指出:) 转自:http://www.cnblogs.com/kyo-yo/archive/2010/09/20/Learning-EntLib-Ninth-Use-PolicyInjection-Module-AOP-PART2-Custom-Matching-Rule.html
View Details