[C#] Cache with Atrribute

  • 173
  • 0

希望能在某個 Method前面加上 [AddCache("cacheKey", new TimeSpan(0, 10, 0))],就能把 ReadMethod回傳的值放進快取 10分鐘,最好順便有 [RefreshCache("cacheKey")]能用在 UpdateMethod;當然,儘量不要動到原本 Method內容。

先說目前狀況:待解決。

最像的是這個:https://github.com/agbell/attribute-based-caching,用 PostSharp弄出像 event (OnEntry、OnSuccess)的東東可以在 method執行前、後加入自訂邏輯(我突然懷念 JavaScript可以隨便挾持方法亂加料),但是 PostSharp好像原本免費,現在要收錢…編譯過不了就跳過。

接著試著 google "C# AOP",看到幾種方式,但都…工程不小:

  1. 原本有用 DI的話,很多 DI framework有 Interceptor,可以在呼叫方法前後加入自訂邏輯
  2. 沒有 DI可以直接用 DynamicProxy

在不改 method內容的前提下增加自訂邏輯(拼拼湊湊的程式碼):

using Castle.Core;
using Castle.DynamicProxy;
using Castle.MicroKernel.Registration;
using Castle.Windsor;
using System;
using System.Reflection;
using System.Runtime.Remoting.Messaging;
using System.Runtime.Remoting.Proxies;

namespace testCastle
{
    class Program
    {
        static void Main(string[] args)
        {
            Imethod method;

            // Castle.Windsor;
            CastleConfig.Initialized();
            method = CastleConfig.Container.Resolve<Imethod>();
            method.echo1("echo1");
            method.echo2("echo2");

            // Castle.Core
            method = new ProxyGenerator().CreateClassProxy<method>(
                new Interceptor1()
            );
            method.echo1("echo1");
            method.echo2("echo2");

            // System.Runtime.Remoting
            method = new DynamicProxy<Imethod>(
                new method()
            ).GetTransparentProxy() as Imethod;
            method.echo1("echo1");
            method.echo2("echo2");
        }
    }

    public interface Imethod
    {
        string echo1(string input);

        string echo2(string input);
    }

    [Interceptor(typeof(Interceptor1))]
    public class method : Imethod
    {
        public virtual string echo1(string input)
        {
            Console.WriteLine("echo1 execute.");
            return input;
        }

        public string echo2(string input)
        {
            Console.WriteLine("echo2 execute.");
            return input;
        }
    }

    static class CastleConfig
    {
        public static IWindsorContainer Container;

        internal static void Initialized()
        {
            Container = new WindsorContainer();

            // 註冊攔截器的型別與物件供 Interceptor attribute 使用
            Container.Register(
                Component.For<IInterceptor>().ImplementedBy<Interceptor1>().LifestyleTransient()
            );

            Container.Register(
                Component.For<Imethod>()
                .ImplementedBy<method>().LifestyleTransient());
        }
    }

    class Interceptor1 : IInterceptor
    {
        public void Intercept(IInvocation invocation)
        {
            var typeName = invocation.TargetType.FullName;
            var methodName = invocation.Method.Name;

            Console.WriteLine("====Interceptor1 before", typeName, methodName);

            //foreach (var arg in invocation.Arguments)
            //{
            //    Console.WriteLine("argument:{0}", arg);
            //}

            invocation.Proceed();

            Console.WriteLine("====Interceptor1 after", typeName, methodName);
            //Console.WriteLine();
        }
    }

    class DynamicProxy<T> : RealProxy
    {
        private readonly T _decorated;
        public DynamicProxy(T decorated)
            : base(typeof(T))
        {
            _decorated = decorated;
        }
        private void Log(string msg, object arg = null)
        {
            //Console.ForegroundColor = ConsoleColor.Red;
            Console.WriteLine(msg, arg);
            //Console.ResetColor();
        }
        public override IMessage Invoke(IMessage msg)
        {
            var methodCall = msg as IMethodCallMessage;
            var methodInfo = methodCall.MethodBase as MethodInfo;
            Log("====RealProxy before", methodCall.MethodName);
            try
            {
                var result = methodInfo.Invoke(_decorated, methodCall.InArgs);
                Log("====RealProxy after",
                  methodCall.MethodName);
                return new ReturnMessage(result, null, 0,
                  methodCall.LogicalCallContext, methodCall);
            }
            catch (Exception e)
            {
                Log(string.Format(
                  "In Dynamic Proxy- Exception {0} executing '{1}'", e),
                  methodCall.MethodName);
                return new ReturnMessage(e, methodCall);
            }
        }
    }
}

剛剛做到這裡覺得有點亂,寫下來整理一下…繼續找 orz