2014년 3월 13일 목요일

Performance Profiler by RealProxy

http://vunvulearadu.blogspot.kr/2014/02/aop-using-realproxy-custom.html
위에서 제시된 코드를 기반으로(사실 똑같다)
내 손으로 다시 쳐서 구현해본 코드이다.

PostSharp보다 좀 불편해보이는 방식이긴 한데
Pre-Processing되는 PostSharp의 경우
간혹 불안정하게 동작하는 경우가 있고
이를 해결할 방법이 뾰족하게 없으나
이렇게 직접 코드를 작성하는 경우는
결국 안정성은 본인의 몫이기 때문에 잘만 한다면
PostSharp보다 나을도 있겠다 싶다.

 물론 상용Tool인 PostSharp은 좋은 제품인 건 맞다.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.Remoting.Proxies;
using System.Runtime.Remoting.Messaging;

namespace AOPWithRealProxy {
    class Program {
        static void Main(string[] args) {
            PerfProfilingDynamicProxy<itest> testProfiling 
                 = new PerfProfilingDynamicProxy<itest>(new Test());
            ITest test = (ITest)testProfiling.GetTransparentProxy();

            test.TestMethod();

            Console.ReadLine();
        }
    }

    public class PerfProfilingDynamicProxy : RealProxy {
        public PerfProfilingDynamicProxy(T decorated)
            : base(typeof(T)) {
                _decorated = decorated;
        }

        public override IMessage Invoke(IMessage msg) {
            IMethodCallMessage methodcall = (IMethodCallMessage)msg;
            MethodInfo methodInfo = methodcall.MethodBase as MethodInfo;
            PerfProfilingAttribute profilingAttribute 
                 = (PerfProfilingAttribute)methodInfo.GetCustomAttributes(typeof(PerfProfilingAttribute)).FirstOrDefault();
            if(profilingAttribute == null) {
                return NormalInvoke(methodInfo, methodcall);
            }
            else {
                return ProfiledInvoke(methodInfo, 
                                      methodcall, 
                                      profilingAttribute.Message);
            }
        }

        private IMessage NormalInvoke(MethodInfo methodInfo, 
                                      IMethodCallMessage methodCall) {
            try {
                var result = methodInfo.Invoke(_decorated, methodCall.InArgs);
                return new ReturnMessage(result, 
                                         null, 
                                         0, 
                                         methodCall.LogicalCallContext, 
                                         methodCall);
            }
            catch(Exception err) {
                return new ReturnMessage(err, methodCall);
            }
        }

        private IMessage ProfiledInvoke(MethodInfo methodInfo, 
                                        IMethodCallMessage methodCall, 
                                        string attrMessage) {
            Stopwatch sw = null;
            try {
                sw = Stopwatch.StartNew();
                var result = methodInfo.Invoke(_decorated, methodCall.InArgs);
                sw.Stop();
                Console.WriteLine(sw.ElapsedMilliseconds);
                long tick = sw.ElapsedTicks;
                return new ReturnMessage(result, 
                                         new object[] { tick }, 
                                         1, 
                                         methodCall.LogicalCallContext, 
                                         methodCall);
            }
            catch(Exception err) {
                return new ReturnMessage(err, methodCall);
            }
            finally {
                if(sw != null && sw.IsRunning) {
                    sw.Stop();
                }
            }
        }

        private readonly T _decorated;
    }

    public class PerfProfilingAttribute : Attribute {
        public PerfProfilingAttribute(string message) {
            Message = message;
        }

        public PerfProfilingAttribute() {
            Message = string.Empty;
        }

        public string Message { get; set; }
    }

    public interface ITest {

        [PerfProfiling]
        DateTime TestMethod();
    }

    public class Test : ITest {
        public DateTime TestMethod() {
            double sumv = 0.0;
            for(int i = 0; i < 100000; i++) {
                Random rnd = new Random(575);
                sumv += rnd.NextDouble();
            }
            return DateTime.Now;
        }
    }
}