前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >AOP缓存实现

AOP缓存实现

作者头像
少羽大怪兽
发布2018-12-11 16:34:04
6310
发布2018-12-11 16:34:04
举报
文章被收录于专栏:架构技术架构技术

输入参数索引作为缓存键的实现

using MJD.Framework.CrossCutting;
using MJD.Framework.ICache;
using System;
using System.Collections.Generic;
using System.Linq;

namespace MJD.Framework.Aop.Cache
{
    /// <summary>
    /// 方法的缓存属性(线性不安全)
    /// </summary>
    [AttributeUsage(AttributeTargets.Method, Inherited = true, AllowMultiple = false)]
    public class CacheAttribute : AopAttribute
    {
        #region fields
        private short _index1 = -1;
        private short _index2 = -1;
        private short _index3 = -1;
        #endregion

        #region protected fields
        protected string Prefix = string.Empty;//缓存的前缀,避免使用ID作为缓存键时与其他缓存冲突,必须是独一无二的前缀
        protected string Key = string.Empty;
        protected string BucketName = string.Empty;
        #endregion

        #region Otors
        /// <summary>
        /// 定义缓存关键字来缓存
        /// </summary>
        /// <param name="bucketName">缓存bucket</param>
        /// <param name="prefix">缓存的前缀,避免使用ID作为缓存键时与其他缓存冲突,必须是独一无二的前缀</param>
        protected CacheAttribute(string bucketName, string prefix)
        {
            BucketName = bucketName;
            Prefix = prefix;
        }
        /// <summary>
        /// 定义缓存关键字来缓存
        /// </summary>
        /// <param name="bucketName">缓存bucket</param>
        /// <param name="prefix">缓存的前缀,避免使用ID作为缓存键时与其他缓存冲突,必须是独一无二的前缀</param>
        /// <param name="key">缓存键</param>
        public CacheAttribute(string bucketName, string prefix, string key)
            : this(bucketName, prefix)
        {
            if (string.IsNullOrEmpty(key)) throw new ArgumentException("缓存键不能为空");
            Key = string.Format("{0}:{1}", Prefix, key);
        }

        /// <summary>
        /// 使用当前参数来缓存,索引位置从0开始
        /// </summary>
        /// <param name="bucketName">缓存bucket</param>
        /// <param name="prefix">缓存的前缀,避免使用ID作为缓存键时与其他缓存冲突,必须是独一无二的前缀</param>
        /// <param name="keyIndex">对应的当前参数的缓存键所在的参数索引位置</param>
        public CacheAttribute(string bucketName, string prefix, short keyIndex)
            : this(bucketName, prefix)
        {
            if (keyIndex < 0) throw new ArgumentException("关键值的参数索引需大于0");
            _index1 = keyIndex;
        }

        /// <summary>
        /// 使用多个当前参数来缓存,索引位置从0开始
        /// </summary>
        /// <param name="bucketName">缓存bucket</param>
        /// <param name="prefix">缓存的前缀,避免使用ID作为缓存键时与其他缓存冲突,必须是独一无二的前缀</param>
        /// <param name="keyIndex1">对应的当前参数的缓存键所在的参数索引位置1</param>
        /// <param name="keyIndex2">对应的当前参数的缓存键所在的参数索引位置2</param>
        public CacheAttribute(string bucketName, string prefix, short keyIndex1, short keyIndex2)
            : this(bucketName, prefix)
        {
            if (keyIndex1 < 0 || keyIndex2 < 0) throw new ArgumentException("关键值的参数索引需大于0");
            _index1 = keyIndex1;
            _index2 = keyIndex2;
        }

        /// <summary>
        /// 使用多个当前参数来缓存,索引位置从0开始
        /// </summary>
        /// <param name="bucketName">缓存bucket</param>
        /// <param name="prefix">缓存的前缀,避免使用ID作为缓存键时与其他缓存冲突,必须是独一无二的前缀</param>
        /// <param name="keyIndex1">对应的当前参数的缓存键所在的参数索引位置1</param>
        /// <param name="keyIndex2">对应的当前参数的缓存键所在的参数索引位置2</param>
        /// <param name="keyIndex3">对应的当前参数的缓存键所在的参数索引位置3</param>
        public CacheAttribute(string bucketName, string prefix, short keyIndex1, short keyIndex2, short keyIndex3)
            : this(bucketName, prefix)
        {
            if (keyIndex1 < 0 || keyIndex2 < 0 || keyIndex3 < 0) throw new ArgumentException("关键值的参数索引不能小于零");
            _index1 = keyIndex1;
            _index2 = keyIndex2;
            _index3 = keyIndex3;
        }
        #endregion

        #region override

        public override object PreCall(object[] inputArgs, out object[] outputs)
        {
            outputs = new object[0];

            var result = IocContainer.Resolve<ICacheService>(BucketName).Get<object>(GetKey(inputArgs));
            return result.Success ? result.Value : null;
        }

        public override void Called(object resultValue, object[] inputArgs, object[] outputs)
        {
            IocContainer.Resolve<ICacheService>(BucketName).Upsert<object>(GetKey(inputArgs), resultValue);
        }

        public override void OnException(Exception e, Dictionary<string, object> inputArgs)
        {
        }

        protected virtual string GetKey(object[] inputArgs)
        {
            if (string.IsNullOrEmpty(Key))
            {
                if (Math.Max(Math.Max(_index1, _index2), _index3) >= inputArgs.Count()) throw new ArgumentException("关键值的参数索引不能大于参数总个数");
                string prefix1 = _index1 >= 0 ? inputArgs[_index1].ToJson() : "";
                string prefix2 = _index2 >= 0 ? "-" + inputArgs[_index2].ToJson() : "";
                string prefix3 = _index3 >= 0 ? "-" + inputArgs[_index3].ToJson() : "";
                Key = string.Format("{0}:{1}{2}{3}",Prefix, prefix1, prefix2, prefix3);
            }
            return Key;
        }

        #endregion
    }
}

输入参数是对象,通过输入参数对象的属性的值来作为缓存键

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace MJD.Framework.Aop.Cache
{
    /// <summary>
    /// 根据属性来设置缓存的关键字
    /// </summary>
    public class CacheWithPropertyAttribute : CacheAttribute
    {
        #region fields
        private List<string> _properties = null;
        private byte _index = 0;
        #endregion

        #region Octors
        /// <summary>
        /// 根据方法的第一个参数的属性设置
        /// </summary>
        /// <param name="bucketName">缓存Bucket</param>
        /// <param name="prefix">缓存的前缀,避免使用ID作为缓存键时与其他缓存冲突,必须是独一无二的前缀</param>
        /// <param name="properties">属性名</param>
        public CacheWithPropertyAttribute(string bucketName, string prefix, params string[] properties)
            : base(bucketName, prefix)
        {
            if (properties == null || properties.Length == 0) throw new ArgumentException("设置的properties个数必须大于1");
            _properties = properties.ToList();
        }
        /// <summary>
        /// 根据方法的第一个参数的属性设置
        /// </summary>
        /// <param name="bucketName">缓存bucket</param>
        /// <param name="prefix">缓存的前缀,避免使用ID作为缓存键时与其他缓存冲突,必须是独一无二的前缀</param>
        /// <param name="index">要设置为缓存的方法参数的索引,索引下标从0开始</param>
        /// <param name="properties">属性名</param>
        public CacheWithPropertyAttribute(string bucketName, string prefix, byte index, params string[] properties)
            : base(bucketName, prefix)
        {
            if (index < 0) throw new ArgumentException("关键值的参数索引不能小于零");
            if (properties == null || properties.Length == 0) throw new ArgumentException("设置的properties个数必须大于1");
            _properties = properties.ToList();
            _index = index;
        }
        #endregion

        #region override
        public override object PreCall(object[] inputArgs, out object[] outputs)
        {
            return base.PreCall(inputArgs, out outputs);
        }
        #endregion

        protected override string GetKey(object[] inputArgs)
        {
            if (string.IsNullOrEmpty(Key))
            {
                Key += base.Prefix + ":";
                object instance = inputArgs[_index];
                foreach (var property in _properties)
                {
                    Key += GetPropertyValue(instance, property) + "-";
                }
            }
            return Key.TrimEnd('-');
        }

        private object GetPropertyValue(object instance, string propertyName)
        {
            BindingFlags flag = BindingFlags.Instance | BindingFlags.GetProperty | BindingFlags.IgnoreCase | BindingFlags.Public;
            Type type = instance.GetType();
            var property = type.GetProperty(propertyName, flag);
            if (property == null) throw new ArgumentException(string.Format("获取缓存出错,类型{0}中没有找到属性{1}", type, propertyName));
            return property.GetValue(instance, null);
        }
    }
}

通过输入参数索引位置移除缓存的aop属性

using MJD.Framework.CrossCutting;
using MJD.Framework.ICache;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MJD.Framework.Aop.Cache
{
    /// <summary>
    /// 移除缓存特性
    /// </summary>
    public class RemoveCacheAttribute : CacheAttribute
    {
        #region Otors
        /// <summary>
        /// 定义缓存关键字来缓存
        /// </summary>
        /// <param name="bucketName">缓存bucket</param>
        /// <param name="prefix">缓存的前缀,避免使用ID作为缓存键时与其他缓存冲突,必须是独一无二的前缀</param>
        protected RemoveCacheAttribute(string bucketName, string prefix)
            : base(bucketName, prefix)
        {
        }
        /// <summary>
        /// 定义缓存关键字来缓存
        /// </summary>
        /// <param name="bucketName">缓存bucket</param>
        /// <param name="prefix">缓存的前缀,避免使用ID作为缓存键时与其他缓存冲突,必须是独一无二的前缀</param>
        /// <param name="key">缓存键</param>
        public RemoveCacheAttribute(string bucketName, string prefix, string key)
            : base(bucketName, prefix, key)
        {
        }

        /// <summary>
        /// 使用当前参数来缓存,索引位置从0开始
        /// </summary>
        /// <param name="bucketName">缓存bucket</param>
        /// <param name="prefix">缓存的前缀,避免使用ID作为缓存键时与其他缓存冲突,必须是独一无二的前缀</param>
        /// <param name="keyIndex">对应的当前参数的缓存键所在的参数索引位置</param>
        public RemoveCacheAttribute(string bucketName, string prefix, short keyIndex)
            : base(bucketName, prefix, keyIndex)
        {
        }

        /// <summary>
        /// 使用多个当前参数来缓存,索引位置从0开始
        /// </summary>
        /// <param name="bucketName">缓存bucket</param>
        /// <param name="prefix">缓存的前缀,避免使用ID作为缓存键时与其他缓存冲突,必须是独一无二的前缀</param>
        /// <param name="keyIndex1">对应的当前参数的缓存键所在的参数索引位置1</param>
        /// <param name="keyIndex2">对应的当前参数的缓存键所在的参数索引位置2</param>
        public RemoveCacheAttribute(string bucketName, string prefix, short keyIndex1, short keyIndex2)
            : base(bucketName, prefix, keyIndex1, keyIndex2)
        {
        }

        /// <summary>
        /// 使用多个当前参数来缓存,索引位置从0开始
        /// </summary>
        /// <param name="bucketName">缓存bucket</param>
        /// <param name="prefix">缓存的前缀,避免使用ID作为缓存键时与其他缓存冲突,必须是独一无二的前缀</param>
        /// <param name="keyIndex1">对应的当前参数的缓存键所在的参数索引位置1</param>
        /// <param name="keyIndex2">对应的当前参数的缓存键所在的参数索引位置2</param>
        /// <param name="keyIndex3">对应的当前参数的缓存键所在的参数索引位置3</param>
        public RemoveCacheAttribute(string bucketName, string prefix, short keyIndex1, short keyIndex2, short keyIndex3)
            : base(bucketName, prefix, keyIndex1, keyIndex2, keyIndex3)
        {
        }
        #endregion

        #region override
        public override void Called(object resultValue, object[] inputArgs, object[] outputs)
        {
        }

        public override object PreCall(object[] inputArgs, out object[] outputs)
        {
            IocContainer.Resolve<ICacheService>(BucketName).Remove(GetKey(inputArgs));
            outputs = new object[0];
            return null;
        }
        #endregion
    }
}

 通过输入参数属性的值作为缓存键来移除缓存

using MJD.Framework.CrossCutting;
using MJD.Framework.ICache;

namespace MJD.Framework.Aop.Cache
{
    /// <summary>
    /// 移除缓存特性(调用成功后移除)
    /// </summary>
    public class RemoveCacheWithPropertyAttribute : CacheWithPropertyAttribute
    {
        #region Octors
        /// <summary>
        /// 根据方法的第一个参数的属性设置
        /// </summary>
        /// <param name="bucketName">缓存Bucket</param>
        /// <param name="prefix">缓存的前缀,避免使用ID作为缓存键时与其他缓存冲突,必须是独一无二的前缀</param>
        /// <param name="properties">属性名</param>
        public RemoveCacheWithPropertyAttribute(string bucketName, string prefix, params string[] properties)
            : base(bucketName, prefix, properties)
        {
        }
        /// <summary>
        /// 根据方法的第一个参数的属性设置
        /// </summary>
        /// <param name="bucketName">缓存bucket</param>
        /// <param name="prefix">缓存的前缀,避免使用ID作为缓存键时与其他缓存冲突,必须是独一无二的前缀</param>
        /// <param name="index">要设置为缓存的方法参数的索引,索引下标从0开始</param>
        /// <param name="properties">属性名</param>
        public RemoveCacheWithPropertyAttribute(string bucketName, string prefix, byte index, params string[] properties)
            : base(bucketName, prefix, index, properties)
        {
        }
        #endregion

        #region override
        public override void Called(object resultValue, object[] inputArgs, object[] outputs)
        {
            IocContainer.Resolve<ICacheService>(BucketName).Remove(GetKey(inputArgs));
        }

        public override object PreCall(object[] inputArgs, out object[] outputs)
        {
            //IocContainer.Resolve<ICacheService>(BucketName).Remove(GetKey(inputArgs));
            outputs = new object[0];
            return null;
        }
        #endregion
    }
}

更新1

using MJD.Framework.CrossCutting;
using MJD.Framework.ICache;

namespace MJD.Framework.Aop.Cache
{
    /// <summary>
    /// 更新缓存特性
    /// </summary>
    public class UpdateCacheAttribute : CacheAttribute
    {
        #region Otors
        /// <summary>
        /// 定义缓存关键字来缓存
        /// </summary>
        /// <param name="bucketName">缓存bucket</param>
        /// <param name="prefix">缓存的前缀,避免使用ID作为缓存键时与其他缓存冲突,必须是独一无二的前缀</param>
        protected UpdateCacheAttribute(string bucketName, string prefix)
            : base(bucketName, prefix)
        {
        }
        /// <summary>
        /// 定义缓存关键字来缓存
        /// </summary>
        /// <param name="bucketName">缓存bucket</param>
        /// <param name="prefix">缓存的前缀,避免使用ID作为缓存键时与其他缓存冲突,必须是独一无二的前缀</param>
        /// <param name="key">缓存键</param>
        public UpdateCacheAttribute(string bucketName, string prefix, string key)
            : base(bucketName, prefix, key)
        {
        }

        /// <summary>
        /// 使用当前参数来缓存,索引位置从0开始
        /// </summary>
        /// <param name="bucketName">缓存bucket</param>
        /// <param name="prefix">缓存的前缀,避免使用ID作为缓存键时与其他缓存冲突,必须是独一无二的前缀</param>
        /// <param name="keyIndex">对应的当前参数的缓存键所在的参数索引位置</param>
        public UpdateCacheAttribute(string bucketName, string prefix, short keyIndex)
            : base(bucketName, prefix, keyIndex)
        {
        }

        /// <summary>
        /// 使用多个当前参数来缓存,索引位置从0开始
        /// </summary>
        /// <param name="bucketName">缓存bucket</param>
        /// <param name="prefix">缓存的前缀,避免使用ID作为缓存键时与其他缓存冲突,必须是独一无二的前缀</param>
        /// <param name="keyIndex1">对应的当前参数的缓存键所在的参数索引位置1</param>
        /// <param name="keyIndex2">对应的当前参数的缓存键所在的参数索引位置2</param>
        public UpdateCacheAttribute(string bucketName, string prefix, short keyIndex1, short keyIndex2)
            : base(bucketName, prefix, keyIndex1, keyIndex2)
        {
        }

        /// <summary>
        /// 使用多个当前参数来缓存,索引位置从0开始
        /// </summary>
        /// <param name="bucketName">缓存bucket</param>
        /// <param name="prefix">缓存的前缀,避免使用ID作为缓存键时与其他缓存冲突,必须是独一无二的前缀</param>
        /// <param name="keyIndex1">对应的当前参数的缓存键所在的参数索引位置1</param>
        /// <param name="keyIndex2">对应的当前参数的缓存键所在的参数索引位置2</param>
        /// <param name="keyIndex3">对应的当前参数的缓存键所在的参数索引位置3</param>
        public UpdateCacheAttribute(string bucketName, string prefix, short keyIndex1, short keyIndex2, short keyIndex3)
            : base(bucketName, prefix, keyIndex1, keyIndex2, keyIndex3)
        {
        }
        #endregion

        #region override
        public override void Called(object resultValue, object[] inputArgs, object[] outputs)
        {
            IocContainer.Resolve<ICacheService>(BucketName).Upsert<object>(GetKey(inputArgs), resultValue);
        }

        public override object PreCall(object[] inputArgs, out object[] outputs)
        {
            outputs = new object[0];
            return null;
        }
        #endregion
    }
}

更新2

using MJD.Framework.CrossCutting;
using MJD.Framework.ICache;

namespace MJD.Framework.Aop.Cache
{
    /// <summary>
    /// 更新缓存特性
    /// </summary>
    public class UpdateCacheWithPropertyAttribute : CacheWithPropertyAttribute
    {

        #region Octors
        /// <summary>
        /// 根据方法的第一个参数的属性设置
        /// </summary>
        /// <param name="bucketName">缓存Bucket</param>
        /// <param name="prefix">缓存的前缀,避免使用ID作为缓存键时与其他缓存冲突,必须是独一无二的前缀</param>
        /// <param name="properties">属性名</param>
        public UpdateCacheWithPropertyAttribute(string bucketName, string prefix, params string[] properties)
            : base(bucketName, prefix, properties)
        {
        }
        /// <summary>
        /// 根据方法的第一个参数的属性设置
        /// </summary>
        /// <param name="bucketName">缓存bucket</param>
        /// <param name="prefix">缓存的前缀,避免使用ID作为缓存键时与其他缓存冲突,必须是独一无二的前缀</param>
        /// <param name="index">要设置为缓存的方法参数的索引,索引下标从0开始</param>
        /// <param name="properties">属性名</param>
        public UpdateCacheWithPropertyAttribute(string bucketName, string prefix, byte index, params string[] properties)
            : base(bucketName, prefix, index, properties)
        {
        }
        #endregion
        
        #region override
        public override void Called(object resultValue, object[] inputArgs, object[] outputs)
        {
            IocContainer.Resolve<ICacheService>(BucketName).Upsert<object>(GetKey(inputArgs), resultValue);
        }

        public override object PreCall(object[] inputArgs, out object[] outputs)
        {
            outputs = new object[0];
            return null;
        }
        #endregion
    }
}
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2018-03-07 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档