在一些对系统中,每每可能须要对一些核心业务作相应的监测。如:记录调用参数,返回值,方法执行耗时等等。若是直接在方法的先后加入代码,以下: app
1 public int F(int a, string s) 2 { 3 var now = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:fff"); 4 var sw = Stopwatch.StartNew(); 5 6 int ret = 1; 7 8 long second = sw.ElapsedMilliseconds; 9 var end = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:fff"); 10 log.Write(now, end, second, a, s, ret); 11 }
这样有如下几点问题:ide
1.将业务逻辑和方法执行监测混在一块儿。性能
2.会有大量重复代码,哪怕你作了一层封装,也依然不够优雅。spa
因为有了以上的问题,咱们会思考,若是能够将方法的调用过程抽象出来,在方法开始调用和方法调用结束时,植入代码,这样就能够很好的解决咱们的问题。pwa
.NET中,咱们能够经过RealProxy来实现咱们想要的功能。RealProxy(https://msdn.microsoft.com/zh-cn/library/vstudio/system.runtime.remoting.proxies.proxyattribute(v=vs.100).aspx)MSDN的定义。在Invoke方法中,能够拦截方法的执行,那么咱们就能够在方法的执行先后作一些事情啦。代理
那么怎么更优雅的应用在方法上呢,咱们能够用ProxyAttribute使用Attribute的方式。能够在对象初始化的时候(new关键字)建立相应的代理类。具体代码以下:code
/// <summary> /// 标记要性能监测的类 /// </summary> [AttributeUsage(AttributeTargets.Class)] public class MonitorServiceAttribute : ProxyAttribute { public override MarshalByRefObject CreateInstance(Type serverType) { var instance = base.CreateInstance(serverType); var proxy = new InjectProxy(serverType, instance).GetTransparentProxy(); return proxy as MarshalByRefObject; } }
InjectProxy代码以下:server
public override IMessage Invoke(IMessage msg) { var call = (IMethodCallMessage) msg; var ctr = call as IConstructionCallMessage; IMethodReturnMessage back; if (ctr != null) { RealProxy defaultProxy = RemotingServices.GetRealProxy(_instance); defaultProxy.InitializeServerObject(ctr); back = EnterpriseServicesHelper.CreateConstructionReturnMessage(ctr, (MarshalByRefObject) GetTransparentProxy()); } else { var now = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:fff"); var sw = Stopwatch.StartNew(); back = RemotingServices.ExecuteMessage(_instance, call); long second = sw.ElapsedMilliseconds; var attr = MethodAttributeCache.GetAttribute<MonitorMethodAttribute>(_instance.GetType().TypeHandle, call.MethodBase); if (attr != null) { attr.Value = second.ToString(); attr.ExecuteTime = now; Queue.Enqueue(Mapper(attr)); } } return back; }
我在这里只记录了方法执行耗时,能够从call里拿到参数,从back里获取到返回值。对象
具体调用地方以下,注意类须要继承ContextBoundObject:blog
internal class Program { private static void Main(string[] args) { var t = new Test(); t.F(1,"aaa"); } } [MonitorService] internal class Test : ContextBoundObject { [MonitorMethod("A")] public int F(int a,string s) { return 333; } }