AOP 横向切面-热插拔缓存


PetShop 部分代码,可以看到每个函数都需要追加HttpRuntime.Cache.Add,都需要在函数体内进行操作。这样子也搞不清楚哪些函数追加了缓存。项目一大就不好控制

public static IList<CategoryInfo> GetCategories() {

    Category cat = new Category();

    if (!enableCaching)
        return cat.GetCategories();

    string key = "category_all";
    IList<CategoryInfo> data = (IList<CategoryInfo>)HttpRuntime.Cache[key];

    // Check if the data exists in the data cache
    if (data == null) {
        // If the data is not in the cache then fetch the data from the business logic tier
        data = cat.GetCategories();

        // Create a AggregateCacheDependency object from the factory
        AggregateCacheDependency cd = DependencyFacade.GetCategoryDependency();

        // Store the output in the data cache, and Add the necessary AggregateCacheDependency object
        HttpRuntime.Cache.Add(key, data, cd, DateTime.Now.AddHours(categoryTimeout), Cache.NoSlidingExpiration, CacheItemPriority.High, null);
    }

    return data;
}


vs2012 mvc4
解决方法 AOP
image

需要引用微软企业库.
Microsoft.Practices.EnterpriseLibrary.Caching.dll
Microsoft.Practices.ServiceLocation.dll
Microsoft.Practices.Unity.dll
Microsoft.Practices.Unity.Configuration.dll
Microsoft.Practices.Unity.Interception.dll
Microsoft.Practices.Unity.Interception.Configuration.dll

配置文件


<?
xml version="1.0" encoding="utf-8" ?> <configuration> <configSections> <section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Microsoft.Practices.Unity.Configuration"/> <section name="cachingConfiguration" type="Microsoft.Practices.EnterpriseLibrary.Caching.Configuration.CacheManagerSettings, Microsoft.Practices.EnterpriseLibrary.Caching, Version=5.0.414.0, Culture=neutral, PublicKeyToken=null" /> </configSections> <startup> <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" /> </startup> <cachingConfiguration defaultCacheManager="ByteartRetailCacheManager"> <cacheManagers> <add name="ByteartRetailCacheManager" type="Microsoft.Practices.EnterpriseLibrary.Caching.CacheManager, Microsoft.Practices.EnterpriseLibrary.Caching, Version=5.0.414.0, Culture=neutral, PublicKeyToken=null" expirationPollFrequencyInSeconds="60" maximumElementsInCacheBeforeScavenging="1000" numberToRemoveWhenScavenging="10" backingStoreName="NullBackingStore" /> <!-- expirationPollFrequencyInSeconds:多少秒进行一次过期检查,缺省值60秒 是检查,跟过期时间没有关系 maximumElementsInCacheBeforeScavenging:缓冲中的最大元素数量 numberToRemoveWhenScavenging:一次移除的数量 --> </cacheManagers> <backingStores> <add type="Microsoft.Practices.EnterpriseLibrary.Caching.BackingStoreImplementations.NullBackingStore, Microsoft.Practices.EnterpriseLibrary.Caching, Version=5.0.414.0, Culture=neutral, PublicKeyToken=null" name="NullBackingStore" /> </backingStores> </cachingConfiguration> <!--BEGIN: Unity--> <unity xmlns="http://schemas.microsoft.com/practices/2010/unity"> <sectionExtension type="Microsoft.Practices.Unity.InterceptionExtension.Configuration.InterceptionConfigurationExtension, Microsoft.Practices.Unity.Interception.Configuration"/> <container> <extension type="Interception"/> <!--type=命名空间.接口,命名空间 mapTo=命名空间.对象,命名空间--> <register type="MvcApplication1.ITalk, MvcApplication1" mapTo="MvcApplication1.Talk, MvcApplication1"> <interceptor type="InterfaceInterceptor"/> <interceptionBehavior type="MvcApplication1.CachingBehavior, MvcApplication1"/> <interceptionBehavior type="MvcApplication1.ExceptionLoggingBehavior, MvcApplication1"/> </register> </container> </unity> <!--END: Unity--> </configuration>


CachingAttribute 特性设置


using
System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace MvcApplication1 { /// <summary> /// 表示由此特性所描述的方法,能够获得来自基础结构层所提供的缓存功能。 /// </summary> [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)] public class CachingAttribute : Attribute { #region Ctor /// <summary> /// 初始化一个新的<c>CachingAttribute</c>类型。 /// </summary> /// <param name="method">缓存方式。</param> public CachingAttribute(CachingMethod method) { this.Method = method; } /// <summary> /// 初始化一个新的<c>CachingAttribute</c>类型。 /// </summary> /// <param name="method">缓存方式</param> /// <param name="TimeOutSeconds">设置超时时间(秒)</param> public CachingAttribute(CachingMethod method, int TimeOutSeconds) : this(method) { this.TimeOutSeconds = TimeOutSeconds; } /// <summary> /// 初始化一个新的<c>CachingAttribute</c>类型。 /// </summary> /// <param name="method">缓存方式。</param> /// <param name="correspondingMethodNames"> /// 与当前缓存方式相关的方法名称。注:此参数仅在缓存方式为Remove时起作用。 /// </param> public CachingAttribute(CachingMethod method, params string[] correspondingMethodNames) : this(method) { this.CorrespondingMethodNames = correspondingMethodNames; } #endregion #region Public Properties /// <summary> /// 设置缓存的超时时间 /// </summary> public int TimeOutSeconds { get; set; } /// <summary> /// 获取或设置缓存方式。 /// </summary> public CachingMethod Method { get; set; } /// <summary> /// 获取或设置一个<see cref="Boolean"/>值,该值表示当缓存方式为Put时,是否强制将值写入缓存中。 /// </summary> public bool Force { get; set; } /// <summary> /// 获取或设置与当前缓存方式相关的方法名称。注:此参数仅在缓存方式为Remove时起作用。 /// </summary> public string[] CorrespondingMethodNames { get; set; } #endregion }
 
public enum CachingMethod
    {
        Remove=0,
        Get=1,
        Put = 2
    }
 
}

ICacheProvider 接口设置

using
System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace MvcApplication1 { public interface ICacheProvider { #region Methods /// <summary> /// 向缓存中添加一个对象。 /// </summary> /// <param name="key">缓存的键值,该值通常是使用缓存机制的方法的名称。</param> /// <param name="valKey">缓存值的键值,该值通常是由使用缓存机制的方法的参数值所产生。</param> /// <param name="value">需要缓存的对象。</param> void Add(string key, string valKey, object value, int TimeoutSeconds); /// <summary> /// 向缓存中更新一个对象。 /// </summary> /// <param name="key">缓存的键值,该值通常是使用缓存机制的方法的名称。</param> /// <param name="valKey">缓存值的键值,该值通常是由使用缓存机制的方法的参数值所产生。</param> /// <param name="value">需要缓存的对象。</param> void Put(string key, string valKey, object value); /// <summary> /// 从缓存中读取对象。 /// </summary> /// <param name="key">缓存的键值,该值通常是使用缓存机制的方法的名称。</param> /// <param name="valKey">缓存值的键值,该值通常是由使用缓存机制的方法的参数值所产生。</param> /// <returns>被缓存的对象。</returns> object Get(string key, string valKey); /// <summary> /// 从缓存中移除对象。 /// </summary> /// <param name="key">缓存的键值,该值通常是使用缓存机制的方法的名称。</param> void Remove(string key); /// <summary> /// 获取一个<see cref="Boolean"/>值,该值表示拥有指定键值的缓存是否存在。 /// </summary> /// <param name="key">指定的键值。</param> /// <returns>如果缓存存在,则返回true,否则返回false。</returns> bool Exists(string key); /// <summary> /// 获取一个<see cref="Boolean"/>值,该值表示拥有指定键值和缓存值键的缓存是否存在。 /// </summary> /// <param name="key">指定的键值。</param> /// <param name="valKey">缓存值键。</param> /// <returns>如果缓存存在,则返回true,否则返回false。</returns> bool Exists(string key, string valKey); #endregion } }



CachingBehavior


using
Microsoft.Practices.EnterpriseLibrary.Caching; using Microsoft.Practices.Unity.InterceptionExtension; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace MvcApplication1 { public class CachingBehavior : IInterceptionBehavior { public IEnumerable<Type> GetRequiredInterfaces() { return Type.EmptyTypes; } #region Private Methods /// <summary> /// 根据指定的<see cref="CachingAttribute"/>以及<see cref="IMethodInvocation"/>实例, /// 获取与某一特定参数值相关的键名。 /// </summary> /// <param name="cachingAttribute"><see cref="CachingAttribute"/>实例。</param> /// <param name="input"><see cref="IMethodInvocation"/>实例。</param> /// <returns>与某一特定参数值相关的键名。 /// <remarks> /// 例如:<see cref="ICacheProvider.Add"/> /// </remarks> /// </returns> private string GetValueKey(CachingAttribute cachingAttribute, IMethodInvocation input) { switch (cachingAttribute.Method) { // 如果是Remove,则不存在特定值键名,所有的以该方法名称相关的缓存都需要清除 case CachingMethod.Remove: return null; // 如果是Get或者Put,则需要产生一个针对特定参数值的键名 case CachingMethod.Get: case CachingMethod.Put: if (input.Arguments != null && input.Arguments.Count > 0) { var sb = new StringBuilder(); for (int i = 0; i < input.Arguments.Count; i++) { sb.Append(input.Arguments[i].ToString()); if (i != input.Arguments.Count - 1) sb.Append("_"); } return sb.ToString(); } else return "NULL"; default: throw new InvalidOperationException("无效的缓存方式。"); } } #endregion public IMethodReturn Invoke(IMethodInvocation input, GetNextInterceptionBehaviorDelegate getNext) { var method = input.MethodBase; var key = method.Name; if (method.IsDefined(typeof(CachingAttribute), false)) { var cachingAttribute = (CachingAttribute)method.GetCustomAttributes(typeof(CachingAttribute), false)[0]; var valKey = GetValueKey(cachingAttribute, input); switch (cachingAttribute.Method) { case CachingMethod.Get: try { if (CacheManager.Instance.Exists(key, valKey)) { var obj = CacheManager.Instance.Get(key, valKey); obj += "缓存"; var arguments = new object[input.Arguments.Count]; input.Arguments.CopyTo(arguments, 0); return new VirtualMethodReturn(input, obj, arguments); } else { var methodReturn = getNext().Invoke(input, getNext); if (cachingAttribute.TimeOutSeconds > 0) { CacheManager.Instance.Add(key, valKey, methodReturn.ReturnValue, cachingAttribute.TimeOutSeconds); } else { CacheManager.Instance.Add(key, valKey, methodReturn.ReturnValue); } return methodReturn; } } catch (Exception ex) { return new VirtualMethodReturn(input, ex); } case CachingMethod.Put: try { var methodReturn = getNext().Invoke(input, getNext); if (CacheManager.Instance.Exists(key)) { if (cachingAttribute.Force) { CacheManager.Instance.Remove(key); if (cachingAttribute.TimeOutSeconds > 0) { CacheManager.Instance.Add(key, valKey, methodReturn.ReturnValue, cachingAttribute.TimeOutSeconds); } else { CacheManager.Instance.Add(key, valKey, methodReturn.ReturnValue); } } else { if (cachingAttribute.TimeOutSeconds > 0) { CacheManager.Instance.Add(key, valKey, methodReturn.ReturnValue, cachingAttribute.TimeOutSeconds); } else { CacheManager.Instance.Add(key, valKey, methodReturn.ReturnValue); } } } else { if (cachingAttribute.TimeOutSeconds > 0) { CacheManager.Instance.Add(key, valKey, methodReturn.ReturnValue, cachingAttribute.TimeOutSeconds); } else { CacheManager.Instance.Add(key, valKey, methodReturn.ReturnValue); } } return methodReturn; } catch (Exception ex) { return new VirtualMethodReturn(input, ex); } case CachingMethod.Remove: try { var removeKeys = cachingAttribute.CorrespondingMethodNames; foreach (var removeKey in removeKeys) { if (CacheManager.Instance.Exists(removeKey)) CacheManager.Instance.Remove(removeKey); } var methodReturn = getNext().Invoke(input, getNext); return methodReturn; } catch (Exception ex) { return new VirtualMethodReturn(input, ex); } default: break; } } Console.WriteLine("CachingBehavior"); return getNext().Invoke(input, getNext); } public bool WillExecute { get { return true; } } } }
 


ExceptionLoggingBehavior


using
Microsoft.Practices.Unity.InterceptionExtension; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace MvcApplication1 { public class ExceptionLoggingBehavior : IInterceptionBehavior { public IEnumerable<Type> GetRequiredInterfaces() { return Type.EmptyTypes; } public IMethodReturn Invoke(IMethodInvocation input, GetNextInterceptionBehaviorDelegate getNext) { Console.WriteLine("ExceptionLoggingBehavior"); return getNext().Invoke(input, getNext); } public bool WillExecute { get { return true; } } } }


CacheManager


using
System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace MvcApplication1 { public sealed class CacheManager : ICacheProvider { #region Private Fields private readonly ICacheProvider _cacheProvider; private static readonly CacheManager _instance = new CacheManager(); #endregion #region Ctor static CacheManager() { } private CacheManager() { _cacheProvider = new EntLibCacheProvider(); } #endregion #region Public Properties /// <summary> /// 获取<c>CacheManager</c>类型的单件(Singleton)实例。 /// </summary> public static CacheManager Instance { get { return _instance; } } #endregion #region ICacheProvider Members /// <summary> /// 向缓存中添加一个对象。 /// </summary> /// <param name="key">缓存的键值,该值通常是使用缓存机制的方法的名称。</param> /// <param name="valKey">缓存值的键值,该值通常是由使用缓存机制的方法的参数值所产生。</param> /// <param name="value">需要缓存的对象。</param> public void Add(string key, string valKey, object value, int Timeoutseconds=60) { _cacheProvider.Add(key, valKey, value,Timeoutseconds); } /// <summary> /// 向缓存中更新一个对象。 /// </summary> /// <param name="key">缓存的键值,该值通常是使用缓存机制的方法的名称。</param> /// <param name="valKey">缓存值的键值,该值通常是由使用缓存机制的方法的参数值所产生。</param> /// <param name="value">需要缓存的对象。</param> public void Put(string key, string valKey, object value) { _cacheProvider.Put(key, valKey, value); } /// <summary> /// 从缓存中读取对象。 /// </summary> /// <param name="key">缓存的键值,该值通常是使用缓存机制的方法的名称。</param> /// <param name="valKey">缓存值的键值,该值通常是由使用缓存机制的方法的参数值所产生。</param> /// <returns>被缓存的对象。</returns> public object Get(string key, string valKey) { return _cacheProvider.Get(key, valKey); } /// <summary> /// 从缓存中移除对象。 /// </summary> /// <param name="key">缓存的键值,该值通常是使用缓存机制的方法的名称。</param> public void Remove(string key) { _cacheProvider.Remove(key); } /// <summary> /// 获取一个<see cref="Boolean"/>值,该值表示拥有指定键值的缓存是否存在。 /// </summary> /// <param name="key">指定的键值。</param> /// <returns>如果缓存存在,则返回true,否则返回false。</returns> public bool Exists(string key) { return _cacheProvider.Exists(key); } /// <summary> /// 获取一个<see cref="Boolean"/>值,该值表示拥有指定键值和缓存值键的缓存是否存在。 /// </summary> /// <param name="key">指定的键值。</param> /// <param name="valKey">缓存值键。</param> /// <returns>如果缓存存在,则返回true,否则返回false。</returns> public bool Exists(string key, string valKey) { return _cacheProvider.Exists(key, valKey); } #endregion } }

EntLibCacheProvider

using
Microsoft.Practices.EnterpriseLibrary.Caching; using Microsoft.Practices.EnterpriseLibrary.Caching.Expirations; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace MvcApplication1 { public class EntLibCacheProvider : ICacheProvider { #region Private Fields private readonly ICacheManager _cacheManager = CacheFactory.GetCacheManager(); #endregion #region ICacheProvider Members /// <summary> /// 向缓存中添加一个对象。 /// </summary> /// <param name="key">缓存的键值,该值通常是使用缓存机制的方法的名称。</param> /// <param name="valKey">缓存值的键值,该值通常是由使用缓存机制的方法的参数值所产生。</param> /// <param name="value">需要缓存的对象。</param> public void Add(string key, string valKey, object value,int Timeoutseconds) { Dictionary<string, object> dict = null; if (_cacheManager.Contains(key)) { dict = (Dictionary<string, object>)_cacheManager[key]; dict[valKey] = value; } else { dict = new Dictionary<string, object>(); dict.Add(valKey, value); } AbsoluteTime expiry = new AbsoluteTime(new TimeSpan(0, 0, 0, Timeoutseconds)); _cacheManager.Add(key, dict, CacheItemPriority.High,null, new ICacheItemExpiration[] { expiry }); } /// <summary> /// 向缓存中更新一个对象。 /// </summary> /// <param name="key">缓存的键值,该值通常是使用缓存机制的方法的名称。</param> /// <param name="valKey">缓存值的键值,该值通常是由使用缓存机制的方法的参数值所产生。</param> /// <param name="value">需要缓存的对象。</param> public void Put(string key, string valKey, object value) { Add(key, valKey, value,60); } /// <summary> /// 从缓存中读取对象。 /// </summary> /// <param name="key">缓存的键值,该值通常是使用缓存机制的方法的名称。</param> /// <param name="valKey">缓存值的键值,该值通常是由使用缓存机制的方法的参数值所产生。</param> /// <returns>被缓存的对象。</returns> public object Get(string key, string valKey) { if (_cacheManager.Contains(key)) { Dictionary<string, object> dict = (Dictionary<string, object>)_cacheManager[key]; if (dict != null && dict.ContainsKey(valKey)) return dict[valKey]; else return null; } return null; } /// <summary> /// 从缓存中移除对象。 /// </summary> /// <param name="key">缓存的键值,该值通常是使用缓存机制的方法的名称。</param> public void Remove(string key) { _cacheManager.Remove(key); } /// <summary> /// 获取一个<see cref="Boolean"/>值,该值表示拥有指定键值的缓存是否存在。 /// </summary> /// <param name="key">指定的键值。</param> /// <returns>如果缓存存在,则返回true,否则返回false。</returns> public bool Exists(string key) { if (!_cacheManager.Contains(key)) return false; if (_cacheManager[key] == null || (Dictionary<string, object>)_cacheManager[key] == null) return false; return true; } /// <summary> /// 获取一个<see cref="Boolean"/>值,该值表示拥有指定键值和缓存值键的缓存是否存在。 /// </summary> /// <param name="key">指定的键值。</param> /// <param name="valKey">缓存值键。</param> /// <returns>如果缓存存在,则返回true,否则返回false。</returns> public bool Exists(string key, string valKey) { if (!_cacheManager.Contains(key)) return false; if (_cacheManager[key] == null || (Dictionary<string, object>)_cacheManager[key] == null) return false; return _cacheManager.Contains(key) && ((Dictionary<string, object>)_cacheManager[key]).ContainsKey(valKey); } #endregion } }



ServiceLocator


using
Microsoft.Practices.Unity; using Microsoft.Practices.Unity.Configuration; using System; using System.Collections.Generic; using System.Configuration; using System.Linq; using System.Reflection; using System.Text; using System.Threading.Tasks; namespace MvcApplication1 { public sealed class ServiceLocator : IServiceProvider { #region Private Fields private readonly IUnityContainer container; #endregion #region Private Static Fields private static readonly ServiceLocator instance = new ServiceLocator(); #endregion #region Ctor /// <summary> /// Initializes a new instance of <c>ServiceLocator</c> class. /// </summary> private ServiceLocator() { UnityConfigurationSection section = (UnityConfigurationSection)ConfigurationManager.GetSection("unity"); container = new UnityContainer(); section.Configure(container); } #endregion #region Public Static Properties /// <summary> /// Gets the singleton instance of the <c>ServiceLocator</c> class. /// </summary> public static ServiceLocator Instance { get { return instance; } } #endregion #region Private Methods private IEnumerable<ParameterOverride> GetParameterOverrides(object overridedArguments) { List<ParameterOverride> overrides = new List<ParameterOverride>(); Type argumentsType = overridedArguments.GetType(); argumentsType.GetProperties(BindingFlags.Public | BindingFlags.Instance) .ToList() .ForEach(property => { var propertyValue = property.GetValue(overridedArguments, null); var propertyName = property.Name; overrides.Add(new ParameterOverride(propertyName, propertyValue)); }); return overrides; } #endregion #region Public Methods /// <summary> /// Gets the service instance with the given type. /// </summary> /// <typeparam name="T">The type of the service.</typeparam> /// <returns>The service instance.</returns> public T GetService<T>() { return container.Resolve<T>(); } /// <summary> /// Gets the service instance with the given type by using the overrided arguments. /// </summary> /// <typeparam name="T">The type of the service.</typeparam> /// <param name="overridedArguments">The overrided arguments.</param> /// <returns>The service instance.</returns> public T GetService<T>(object overridedArguments) { var overrides = GetParameterOverrides(overridedArguments); return container.Resolve<T>(overrides.ToArray()); } /// <summary> /// Gets the service instance with the given type by using the overrided arguments. /// </summary> /// <param name="serviceType">The type of the service.</param> /// <param name="overridedArguments">The overrided arguments.</param> /// <returns>The service instance.</returns> public object GetService(Type serviceType, object overridedArguments) { var overrides = GetParameterOverrides(overridedArguments); return container.Resolve(serviceType, overrides.ToArray()); } #endregion #region IServiceProvider Members /// <summary> /// Gets the service instance with the given type. /// </summary> /// <param name="serviceType">The type of the service.</param> /// <returns>The service instance.</returns> public object GetService(Type serviceType) { return container.Resolve(serviceType); } #endregion } }


使用方法


using
System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace MvcApplication1 { public class Talk : ITalk { public string Speak(string msg) { return msg; } } public interface ITalk { [Caching(CachingMethod.Get)] string Speak(string msg); } }
         ITalk talk = ServiceLocator.Instance.GetService<ITalk>();
            string s = talk.Speak("正常调用");
            string s1 = talk.Speak("正常调用");

            ViewBag.Message = s +"_"+ s1;


只需要在接口中追加[Caching(CachingMethod.Get,60)],缓存就可以使用了,无需写一堆代码。
第一次调用
image

60秒内刷新
image

原文地址:https://www.cnblogs.com/kfsmqoo/p/4242345.html