标签:call ons ODB dbase gets 扩展 console 顺序 lazy
应先阅读:《AOP概念,拦截,与UnityInterception基础应用》
https://www.cnblogs.com/xinpingqiyouhe/p/14482213.html
代码基于net472
/* * 附着InterfaceInterceptor,并通过ICallHandler处理拦截 * * 应先阅读:《AOP概念,拦截,与UnityInterception基础应用》 * https://www.cnblogs.com/xinpingqiyouhe/p/14482213.html * * 对ICallHandler做了一些抽象和凝练,以及进一步测试和验证。 * * * 使用这种方式需要: * 1、实现ICallHandler作为拦截处理类 * 2、实现抽象HandlerAttribute的特性,并将其标注到要拦截的目标(或其成员)上——用于实例化ICallHandler,同时表明使用这个Handler处理拦截 * 3、为Unity容器配置拦截扩展,以及为目标对象配置拦截器 * * * 这种方式可以支持“单次拦截”对“多拦截处理函数”,并按ICallHandler.Order有小到大(支持负数)的顺序执行: * 比如有两个处理函数CH1、CH2(有序),则实际效果为:CH1前 -> CH2前 -> 真实调用 -> CH2后 -> CH1后 * 【但为Order = 0的永远排在最后!!!】 * * 另外,如果真实调用发生了未处理异常,不会打断后续处理链,即一切“后”照常执行(下方ExceptionCallHandler); * 而如果拦截处理链中,想主动【生成】异常,希望打断处理链,参见下方VerificationCallHandler * * * 这里的“多拦截处理函数”实际有两种情况: * 1、不同的处理函数(通过不同特性进行标注) * 由于特性的不可重叠,这种是最为合理的——同一个目标想拥有多个处理函数,给他挂上不同特性就好 * 比如下方的ExceptionCallHandler(真实函数抛出异常)和VerificationCallHandler(拦截处理链主动【生成】异常) * * 2、相同特性的处理函数 * 由于特性挂在interface上、class上、和各自的member上,都可以正确标识拦截,所以存在同特性多次处理拦截的情况 * 此时若Order相同,则调用顺序为:接口前 -> 接口成员前 -> 类前 -> 类成员前 -> 真实调用 -> 类成员后 -> 类后 -> 接口成员后 -> 接口后 * 例子为下方LogCallHandler * * * 本例使用InterfaceInterceptor类型的拦截器,这种拦截器同样可以拦截对目标的一切访问,但目标必须是个接口。 */ namespace ConsoleApp2 { #region 被拦截的接口,与对其实例化的类 [LogInterception(Order = 60, ZiDingYi = "接口")]//特性放在接口上,对内部所有内容(包括属性)均生效 [HandleExceptionInterception(Order = 55)] public interface IProductDao { [LogInterception(Order = 20, ZiDingYi = "接口属性")]//放在成员只对这个成员生效,这里导致处理函数叠加 int Id { get; [VerificationInterception(Order = 15)]//仅处理对Id属性的Set调用 set; } [LogInterception(Order = 30, ZiDingYi = "接口方法")] void Get(); void ThrowExpectionMethod(); } [LogInterception(Order = 40, ZiDingYi = "类")]//特性放到类上也是OK,同样对内部所有内容生效 public class ProductDao : IProductDao { int _id; [LogInterception(Order = -10, ZiDingYi = "类属性")]//与放在接口成员上效果一致,Order支持负数 public int Id { get { return _id; } set { _id = value; Console.WriteLine("设置了Id属性"); }//本例中赋值为负数时会被VerificationInterception打断,不会执行 } [LogInterception(Order = 50, ZiDingYi = "类方法")] public void Get() { Console.WriteLine("真实调用"); } public void ThrowExpectionMethod() { Console.WriteLine("真实抛出异常"); throw new Exception("错了"); } } #endregion #region 日志拦截,演示“同Handler多次注册”——Attribute挂哪都有效;Order也有效;以及不设置Order时的顺序 public class LogHandler : ICallHandler { public int Order { get; set; } public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext) { Console.WriteLine($"Log: Order={Order}, {ZiDingYi}_前");//拦截器将输出自定义的语句 var r = getNext()(input, getNext); Console.WriteLine($"Log: Order={Order}, {ZiDingYi}_后"); return r; } //演示自定义参数的使用 public string ZiDingYi { get; set; } } //以特性的方式,把拦截处理函数挂到目标上。它与ICallHandler一一对应 public class LogInterceptionAttribute : HandlerAttribute { //演示自定义参数 public string ZiDingYi; public override ICallHandler CreateHandler(IUnityContainer container) { //return new LogCallHandler { ZiDingYi = ZiDingYi};//这样就是不为Order赋值,所有Order相同 return new LogHandler { ZiDingYi = ZiDingYi, Order = Order };//不要忘记赋值,Attribute和Handler没有继承关系 } } #endregion #region 异常处理拦截,演示真实函数发生异常的情形——不会打断拦截处理链 public class HandleExceptionHandler : ICallHandler { public int Order { get; set; } public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext) { Console.WriteLine($"Ex: Order={Order}, 前"); var r = getNext()(input, getNext); if (r.Exception != null)//真实函数发生的未处理异常,它不会打断后续拦截处理链。 { //这里可以直接获取反射信息 Console.WriteLine($"Ex: Order={Order}, 后。{input.Target}->{input.MethodBase.Name} Exception:{r.Exception.Message}"); } else Console.WriteLine($"Ex: Order={Order}, 后"); return r; } } /// <summary> /// 模拟处理真实函数异常 /// </summary> public class HandleExceptionInterceptionAttribute : HandlerAttribute { public override ICallHandler CreateHandler(IUnityContainer container) { return new HandleExceptionHandler { Order = Order }; //return new HandleExceptionHandler { };用这句话试出了,为Order=0的(相当于没赋值过)的处理函数会排在最后一个 } } #endregion #region 校验拦截,演示如何在拦截处理链中主动【生成】异常,并打断处理链 public class VerificationCallHandler : ICallHandler { public int Order { get; set; } public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext) { Console.WriteLine($"Vrf: Order={Order}, 前"); //由于本处理函数仅挂在Id.Set()方法上,所以必然会有这个参数值 var value = (int)input.Arguments[0]; if (value < 0) //这个return会打断整个拦截链,并在主调语句抛出本异常 return input.CreateExceptionMethodReturn(new ArgumentException("Id不能为负数")); var r = getNext()(input, getNext); Console.WriteLine($"Vrf: Order={Order}, 后"); return r; } } /// <summary> /// 校验参数是否非负 /// </summary> public class VerificationInterceptionAttribute : HandlerAttribute { public override ICallHandler CreateHandler(IUnityContainer container) { return new VerificationCallHandler { Order = Order }; } } #endregion class Program { static void Main(string[] args) { IUnityContainer container = new UnityContainer(); UnityConfigurationSection unitySection = (UnityConfigurationSection)ConfigurationManager.GetSection("unity"); unitySection.Configure(container); //流式配置写法,不需要配置文件 //container // .AddNewExtension<Interception>() // .RegisterType<IProductDao, ProductDao>() // .Configure<Interception>() // .SetInterceptorFor<IProductDao>(new InterfaceInterceptor()); //IProductDao dao = new ProductDao();//这么写无法触发拦截。哪怕用的也是IProductDao IProductDao dao = container.Resolve<IProductDao>();//构造函数不会触发拦截,但必须从容器中解析,实际生成拦截器代理对象 Console.WriteLine("------调用方法------"); dao.Get();//触发拦截 Console.WriteLine(); Console.WriteLine("------调用属性------"); Console.WriteLine($"ID={dao.Id}");//会出现在本次调用的最后,因为拦截发生在读Id属性时,而不是这条输出语句 Console.WriteLine(); Console.WriteLine("------真实方法异常,不打断拦截处理链------"); try { dao.ThrowExpectionMethod();//尽管真实函数的未处理异常不会打断后续处理链,但这个异常还是会抛出的 } catch { Console.WriteLine("主调方进行了捕获");//会出现在本次调用的最后——即整个拦截处理链完成后,在主调语句抛出了获异常 } Console.WriteLine(); Console.WriteLine("------拦截中生成异常,打断拦截处理链------"); try { dao.Id = -3;//尽管真实函数的未处理异常不会打断后续处理链,但这个异常还是会抛出的 } catch(Exception ex) { Console.WriteLine($"主调方捕获了异常:{ex.Message}");//会出现在本次调用的最后——即整个拦截处理链完成后,在主调语句抛出了获异常 } Console.Read(); } } }
配置文件(可选)
<?xml version="1.0" encoding="utf-8" ?> <configuration> <configSections> <section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Microsoft.Practices.Unity.Configuration"/> </configSections> <unity> <!--通过别名的方式简写命名空间与程序集--> <alias alias="MyClass" type="ConsoleApp1.MyClassForProxyInterception, ConsoleApp1"/> <alias alias="MyInterceptionBehavior" type="ConsoleApp1.MyInterceptionBehavior, ConsoleApp1"/> <sectionExtension type="Microsoft.Practices.Unity.InterceptionExtension.Configuration.InterceptionConfigurationExtension, Microsoft.Practices.Unity.Interception.Configuration"/> <container name="MyInterception"> <extension type="Interception"/> <!--这个类型注册到了容器,但并不会Resolve,也就不需要写mapTo--> <register type="MyClass"> <!--这个类型的拦截器可以接受类,写在这里标识它要对“MyClass(上边alias进行指代)”类进行拦截--> <interceptor type="TransparentProxyInterceptor"/> <interceptionBehavior name="MyBehavior" type="MyInterceptionBehavior"/> <policyInjection/> </register> </container> </unity> <startup> <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2" /> </startup> </configuration>
执行结果
UnityInterception_With_ICallHandler
标签:call ons ODB dbase gets 扩展 console 顺序 lazy
原文地址:https://www.cnblogs.com/xinpingqiyouhe/p/14489705.html