当前位置:  开发笔记 > 编程语言 > 正文

如何进行方法签名缓存?

如何解决《如何进行方法签名缓存?》经验,为你挑选了1个好方法。

我正在使用.NET和C#构建一个应用程序,我想通过在方法中使用属性/注释而不是显式代码来缓存一些结果.

我想要一个看起来有点像这样的方法签名:

[Cache, timeToLive=60]
String getName(string id, string location)

它应该根据输入创建一个哈希值,并将其用作结果的键.当然,有一些配置文件告诉它如何实际放入memcached,本地字典或其他东西.

你知道这样的框架吗?

我甚至对Java也感兴趣



1> Yrlec..:

使用Microsoft Enterprise Library中的 CacheHandler, 您可以轻松实现此目的.例如:

[CacheHandler(0, 30, 0)]
public Object GetData(Object input)
{
}

会使对该方法的所有调用都缓存30分钟.所有调用都根据输入数据和方法名称获取一个唯一的缓存键,因此如果您使用不同的输入调用该方法两次,它将不会被缓存,但如果您在输出相同的时间间隔内调用它> 1次,则方法只执行一次.

我在微软的代码中添加了一些额外的功能:

我的修改版本看起来像这样:

using System;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.Remoting.Contexts;
using System.Text;
using System.Web;
using System.Web.Caching;
using System.Web.UI;
using Microsoft.Practices.EnterpriseLibrary.Common.Configuration;
using Microsoft.Practices.Unity.InterceptionExtension;


namespace Middleware.Cache
{
    /// 
    /// An  that implements caching of the return values of
    /// methods. This handler stores the return value in the ASP.NET cache or the Items object of the current request.
    /// 
    [ConfigurationElementType(typeof (CacheHandler)), Synchronization]
    public class CacheHandler : ICallHandler
    {
        /// 
        /// The default expiration time for the cached entries: 5 minutes
        /// 
        public static readonly TimeSpan DefaultExpirationTime = new TimeSpan(0, 5, 0);

        private readonly object cachedData;

        private readonly DefaultCacheKeyGenerator keyGenerator;
        private readonly bool storeOnlyForThisRequest = true;
        private TimeSpan expirationTime;
        private GetNextHandlerDelegate getNext;
        private IMethodInvocation input;


        public CacheHandler(TimeSpan expirationTime, bool storeOnlyForThisRequest)
        {
            keyGenerator = new DefaultCacheKeyGenerator();
            this.expirationTime = expirationTime;
            this.storeOnlyForThisRequest = storeOnlyForThisRequest;
        }

        /// 
        /// This constructor is used when we wrap cached data in a CacheHandler so that 
        /// we can reload the object after it has been removed from the cache.
        /// 
        /// 
        /// 
        /// 
        /// 
        /// 
        public CacheHandler(TimeSpan expirationTime, bool storeOnlyForThisRequest,
                            IMethodInvocation input, GetNextHandlerDelegate getNext,
                            object cachedData)
            : this(expirationTime, storeOnlyForThisRequest)
        {
            this.input = input;
            this.getNext = getNext;
            this.cachedData = cachedData;
        }


        /// 
        /// Gets or sets the expiration time for cache data.
        /// 
        /// The expiration time.
        public TimeSpan ExpirationTime
        {
            get { return expirationTime; }
            set { expirationTime = value; }
        }

        #region ICallHandler Members

        /// 
        /// Implements the caching behavior of this handler.
        /// 
        ///  object describing the current call.
        /// delegate used to get the next handler in the current pipeline.
        /// Return value from target method, or cached result if previous inputs have been seen.
        public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext)
        {
            lock (input.MethodBase)
            {
                this.input = input;
                this.getNext = getNext;

                return loadUsingCache();
            }
        }

        public int Order
        {
            get { return 0; }
            set { }
        }

        #endregion

        private IMethodReturn loadUsingCache()
        {
            //We need to synchronize calls to the CacheHandler on method level
            //to prevent duplicate calls to methods that could be cached.
            lock (input.MethodBase)
            {
                if (TargetMethodReturnsVoid(input) || HttpContext.Current == null)
                {
                    return getNext()(input, getNext);
                }

                var inputs = new object[input.Inputs.Count];
                for (int i = 0; i < inputs.Length; ++i)
                {
                    inputs[i] = input.Inputs[i];
                }

                string cacheKey = keyGenerator.CreateCacheKey(input.MethodBase, inputs);
                object cachedResult = getCachedResult(cacheKey);

                if (cachedResult == null)
                {
                    var stopWatch = Stopwatch.StartNew();
                    var realReturn = getNext()(input, getNext);
                    stopWatch.Stop();
                    if (realReturn.Exception == null && realReturn.ReturnValue != null)
                    {
                        AddToCache(cacheKey, realReturn.ReturnValue);
                    }
                    return realReturn;
                }

                var cachedReturn = input.CreateMethodReturn(cachedResult, input.Arguments);

                return cachedReturn;
            }
        }

        private object getCachedResult(string cacheKey)
        {
            //When the method uses input that is not serializable 
            //we cannot create a cache key and can therefore not 
            //cache the data.
            if (cacheKey == null)
            {
                return null;
            }

            object cachedValue = !storeOnlyForThisRequest ? HttpRuntime.Cache.Get(cacheKey) : HttpContext.Current.Items[cacheKey];
            var cachedValueCast = cachedValue as CacheHandler;
            if (cachedValueCast != null)
            {
                //This is an object that is reloaded when it is being removed.
                //It is therefore wrapped in a CacheHandler-object and we must
                //unwrap it before returning it.
                return cachedValueCast.cachedData;
            }
            return cachedValue;
        }

        private static bool TargetMethodReturnsVoid(IMethodInvocation input)
        {
            var targetMethod = input.MethodBase as MethodInfo;
            return targetMethod != null && targetMethod.ReturnType == typeof (void);
        }

        private void AddToCache(string key, object valueToCache)
        {
            if (key == null)
            {
                //When the method uses input that is not serializable 
                //we cannot create a cache key and can therefore not 
                //cache the data.
                return;
            }

            if (!storeOnlyForThisRequest)
            {
                HttpRuntime.Cache.Insert(
                    key,
                    valueToCache,
                    null,
                    System.Web.Caching.Cache.NoAbsoluteExpiration,
                    expirationTime,
                    CacheItemPriority.Normal, null);
            }
            else
            {
                HttpContext.Current.Items[key] = valueToCache;
            }
        }
    }

    /// 
    /// This interface describes classes that can be used to generate cache key strings
    /// for the .
    /// 
    public interface ICacheKeyGenerator
    {
        /// 
        /// Creates a cache key for the given method and set of input arguments.
        /// 
        /// Method being called.
        /// Input arguments.
        /// A (hopefully) unique string to be used as a cache key.
        string CreateCacheKey(MethodBase method, object[] inputs);
    }

    /// 
    /// The default  used by the .
    /// 
    public class DefaultCacheKeyGenerator : ICacheKeyGenerator
    {
        private readonly LosFormatter serializer = new LosFormatter(false, "");

        #region ICacheKeyGenerator Members

        /// 
        /// Create a cache key for the given method and set of input arguments.
        /// 
        /// Method being called.
        /// Input arguments.
        /// A (hopefully) unique string to be used as a cache key.
        public string CreateCacheKey(MethodBase method, params object[] inputs)
        {
            try
            {
                var sb = new StringBuilder();

                if (method.DeclaringType != null)
                {
                    sb.Append(method.DeclaringType.FullName);
                }
                sb.Append(':');
                sb.Append(method.Name);

                TextWriter writer = new StringWriter(sb);

                if (inputs != null)
                {
                    foreach (var input in inputs)
                    {
                        sb.Append(':');
                        if (input != null)
                        {
                            //Diffrerent instances of DateTime which represents the same value
                            //sometimes serialize differently due to some internal variables which are different.
                            //We therefore serialize it using Ticks instead. instead.
                            var inputDateTime = input as DateTime?;
                            if (inputDateTime.HasValue)
                            {
                                sb.Append(inputDateTime.Value.Ticks);
                            }
                            else
                            {
                                //Serialize the input and write it to the key StringBuilder.
                                serializer.Serialize(writer, input);
                            }
                        }
                    }
                }

                return sb.ToString();
            }
            catch
            {
                //Something went wrong when generating the key (probably an input-value was not serializble.
                //Return a null key.
                return null;
            }
        }

        #endregion
    }
}

微软最值得赞扬的是这段代码.我们只在请求级别添加缓存等内容而不是跨请求(比您想象的更有用)并修复了一些错误(例如,将DateTime-objects序列化为不同的值).

推荐阅读
wangtao
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有