在一些對系統中,每每可能須要對一些核心業務作相應的監測。如:記錄調用參數,返回值,方法執行耗時等等。若是直接在方法的先後加入代碼,以下: 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; } }