Unity实现AOP(用于实现缓存)

先下载这个NUGET包。

个人理解UINITY是在IOC上实现的AOP(自己试验了好多次),所以先定义接口跟实现类。

namespace Cache
{
    public class Talk : ITalk
    {
        [Caching(CachingMethod.Get)]
        public System.Collections.Generic.List<string> GetData()
        {
            Data.UpData();
            return Data.GetData();
        }
    }
}


namespace Cache
{
     public interface ITalk
     {
        [Caching(CachingMethod.Get)]
         List<string> GetData();
     }
}
View Code

然后写CachingAttribute特性类。实际上所有ITalk的实现都会被拦截,所以写了一个特性来筛选。

    [AttributeUsage(AttributeTargets.Method,AllowMultiple = false,Inherited = false)]
    public class CachingAttribute:Attribute
    {

        /// <summary>
        /// 初始化一个新的<c>CachingAttribute</c>类型。
        /// </summary>
        /// <param name="method">缓存方式。</param>
        public CachingAttribute(CachingMethod method)
        {
            Method = method;
        }
        /// <summary>
        /// 初始化一个新的<c>CachingAttribute</c>类型。
        /// </summary>
        /// <param name="method">缓存方式。</param>
        /// <param name="correspondingMethodNames">
        /// 与当前缓存方式相关的方法名称。注:此参数仅在缓存方式为Remove时起作用。
        /// </param>
        public CachingAttribute(CachingMethod method, params string[] correspondingMethodNames)
            : this(method)
        {
            CorrespondingMethodNames = correspondingMethodNames;
        }
        #region Public Properties
        /// <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
    }
View Code

枚举

 public enum CachingMethod
    {
        Get,
        Put,
        Remove
    }
CachingMethod

缓存机制接口

    /// <summary>
    /// 表示实现该接口的类型是能够为应用程序提供缓存机制的类型。
    /// </summary>
    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);
        /// <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
    }
ICacheProvider

两种方式的缓存接口实现

    /// <summary>
    /// 表示基于AppFabric的缓存机制的实现。
    /// </summary>
    public class AppfabricCacheProvider : ICacheProvider
    {
        private readonly DataCacheFactory factory = new DataCacheFactory();
        private readonly DataCache cache;

        public AppfabricCacheProvider()
        {
            cache = factory.GetDefaultCache();
        }

        #region ICacheProvider Members

        public void Add(string key, string valKey, object value)
        {
            Dictionary<string, object> val = (Dictionary<string, object>)cache.Get(key);
            if (val == null)
            {
                val = new Dictionary<string, object>();
                val.Add(valKey, value);
                cache.Add(key, val);
            }
            else
            {
                if (!val.ContainsKey(valKey))
                    val.Add(valKey, value);
                else
                    val[valKey] = value;
                cache.Put(key, val);
            }
        }

        public void Put(string key, string valKey, object value)
        {
            Add(key, valKey, value);
        }

        public object Get(string key, string valKey)
        {
            if (Exists(key, valKey))
            {
                return ((Dictionary<string, object>)cache.Get(key))[valKey];
            }
            return null;
        }

        public void Remove(string key)
        {
            cache.Remove(key);
        }

        public bool Exists(string key)
        {
            return cache.Get(key) != null;
        }

        public bool Exists(string key, string valKey)
        {
            var val = cache.Get(key);
            if (val == null)
                return false;
            return ((Dictionary<string, object>)val).ContainsKey(valKey);
        }

        #endregion
    }
AppfabricCacheProvider
    /// <summary>
    /// 表示基于Microsoft Patterns & Practices - Enterprise Library Caching Application Block的缓存机制的实现。
    /// </summary>
    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)
        {
            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);
            }
            cacheManager.Add(key, dict);
        }
        /// <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);
        }
        /// <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)
        {
            return cacheManager.Contains(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 cacheManager.Contains(key) &&
                ((Dictionary<string, object>)cacheManager[key]).ContainsKey(valKey);
        }
        #endregion
    }
EntLibCacheProvider

CacheManager用来管理缓存实现( public ICacheProvider CacheProvider = new EntLibCacheProvider();这里可以应该用依赖注入,因为测试懒得写了就直接实例化了)

    public class CacheManager : ICacheProvider
    {

        public ICacheProvider CacheProvider = new EntLibCacheProvider();
        // ReSharper disable once InconsistentNaming
        private CacheManager instance = new CacheManager();
        static CacheManager() { }

        #region 公共属性
        /// <summary>
        /// 获取<c>CacheManager</c>类型的单件(Singleton)实例。
        /// </summary>
        public  CacheManager Instance
        {
            get { return instance; }
        }
        #endregion


        public void Add(string key, string valKey, object value)
        {
            CacheProvider.Add(key, valKey, value);
        }

        public void Put(string key, string valKey, object value)
        {
            CacheProvider.Put(key, valKey, value);
        }

        public object Get(string key, string valKey)
        {
            return CacheProvider.Get(key, valKey);
        }

        public void Remove(string key)
        {
            CacheProvider.Remove(key);
        }

        public bool Exists(string key)
        {
            return CacheProvider.Exists(key);
        }

        public bool Exists(string key, string valKey)
        {
            return CacheProvider.Exists(key, valKey);
        }
    }
CacheManager

AOP拦截。getNext().Invoke(input, getNext)这一句相当于获取被拦截方法的返回值

    public class CachingBehavior : IInterceptionBehavior
    {
        /// <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)
            {
                case CachingMethod.Remove:
                    return null;
                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("无效的缓存方式。");
            }
        }

        /// <summary>
        /// 获取当前行为需要拦截的对象类型接口。
        /// </summary>
        /// <returns>所有需要拦截的对象类型接口。</returns>
        public IEnumerable<Type> GetRequiredInterfaces()
        {
            return Type.EmptyTypes;
        }

        /// <summary>
        /// 通过实现此方法来拦截调用并执行所需的拦截行为。
        /// </summary>
        /// <param name="input">调用拦截目标时的输入信息。</param>
        /// <param name="getNext">通过行为链来获取下一个拦截行为的委托。</param>
        /// <returns>从拦截目标获得的返回信息。</returns>
        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 (new CacheManager().Instance.Exists(key, valKey))
                            {
                                var obj = new CacheManager().Instance.Get(key, valKey);
                                var arguments = new object[input.Arguments.Count];
                                input.Arguments.CopyTo(arguments, 0);
                                return new VirtualMethodReturn(input, obj, arguments);
                            }
                            var methodReturn = getNext().Invoke(input, getNext);
                            if (methodReturn.Exception == null)
                            {
                                new 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 (new CacheManager().Instance.Exists(key))
                            {
                                if (cachingAttribute.Force)
                                {
                                    new CacheManager().Instance.Remove(key);
                                    new CacheManager().Instance.Add(key, valKey, methodReturn.ReturnValue);
                                }
                                else
                                    new CacheManager().Instance.Put(key, valKey, methodReturn.ReturnValue);
                            }
                            else
                                new 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 (new CacheManager().Instance.Exists(removeKey))
                                    new CacheManager().Instance.Remove(removeKey);
                            }
                            var methodReturn = getNext().Invoke(input, getNext);
                            return methodReturn;
                        }
                        catch (Exception ex)
                        {
                            return new VirtualMethodReturn(input, ex);
                        }
                }
            }

            return getNext().Invoke(input, getNext);
        }

        /// <summary>
        /// 获取一个<see cref="Boolean"/>值,该值表示当前拦截行为被调用时,是否真的需要执行
        /// 某些操作。
        /// </summary>
        public bool WillExecute
        {
            get { return true; }
        }
    }
CachingBehavior

Unity操作类

/// <summary>
    /// Represents the Service Locator.
    /// </summary>
    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
    }    
ServiceLocator

最后需要在配置文件中配置一下。

<configSections>
    <section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Microsoft.Practices.Unity.Configuration" />
</configSections>

  <!--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" />
      <!--Cache Provider-->
      <register type="Cache.ITalk, Cache" mapTo="Cache.Talk, Cache">
        <interceptor type="InterfaceInterceptor" />
        <interceptionBehavior type="Cache.CachingBehavior, Cache" />
      </register>
    </container>
  </unity>
  <!--END: Unity-->
WebConfig

 https://github.com/dxka8/AopWeb 代码

经测试AOP已经实现,但是两个缓存实现因为用的第三方还在报错(自己实现一个也可),正在踏坑(有懂的同学可以支援一下我啊)。

注明:非原创,在dax.net的APWORKS里面扒的。

原文地址:https://www.cnblogs.com/harpz/p/4201585.html