.net core 中的MemoryCache的详细使用

打印 上一主题 下一主题

主题 1760|帖子 1760|积分 5280

项目搭建了一个基础的框架,实现缓存的AOP拦截,初次查询从数据库获取,再写入缓存,设置过期时间,再次查询数据时从缓存获取。

话不多说我们来上代码实现:

1.界说缓存的接口和实现类


界说缓存接口ICachingProvider和实现类MemoryCaching:
  1.         /// <summary>
  2.         /// 简单的缓存接口,只有查询和添加,以后会进行扩展
  3.         /// </summary>
  4.         public interface ICachingProvider
  5.         {
  6.                 object Get(string cacheKey);
  7.                 void Set(string cacheKey, object cacheValue, int timeSpan);
  8.         }
  9. /// <summary>
  10. /// 实例化缓存接口ICaching
  11. /// </summary>
  12. public class MemoryCaching : ICachingProvider
  13. {
  14.          //引用Microsoft.Extensions.Caching.Memory;这个和.net 还是不一样,没有了Httpruntime了
  15.          private readonly IMemoryCache _cache;
  16.          //还是通过构造函数的方法,获取
  17.          public MemoryCaching(IMemoryCache cache)
  18.          {
  19.                  _cache = cache;
  20.          }
  21.          public object Get(string cacheKey)
  22.          {
  23.                  return _cache.Get(cacheKey);
  24.          }
  25.          public void Set(string cacheKey, object cacheValue, int timeSpan)
  26.          {
  27.                  _cache.Set(cacheKey, cacheValue, TimeSpan.FromSeconds(timeSpan * 60));
  28.          }
  29. }
复制代码
2.界说缓存的特性CachingAttribute:
  1. /// <summary>
  2. /// 这个Attribute就是使用时候的验证,把它添加到要缓存数据的方法中,即可完成缓存的操作。
  3. /// </summary>
  4. [AttributeUsage(AttributeTargets.Method, Inherited = true)]
  5. public class CachingAttribute : System.Attribute
  6. {
  7.         /// <summary>
  8.         /// 缓存绝对过期时间(分钟)
  9.         /// </summary>
  10.         public int AbsoluteExpiration { get; set; } = 30;
  11. }
复制代码
3.界说MemoryCacheSetup 启动服务注册
  1. /// <summary>
  2. /// Memory缓存 启动服务
  3. /// </summary>
  4. public static class MemoryCacheSetup
  5. {
  6.         public static void AddMemoryCacheSetup(this IServiceCollection services)
  7.         {
  8.                 if (services == null) throw new ArgumentNullException(nameof(services));
  9.                 services.AddScoped<ICachingProvider, MemoryCaching>();
  10.                 services.AddSingleton<IMemoryCache>(factory =>
  11.                 {
  12.                         var cache = new MemoryCache(new MemoryCacheOptions());
  13.                         return cache;
  14.                 });
  15.         }
  16. }
复制代码
4.CacheAopBase缓存aop基础类,面向切换的内存缓存使用

界说缓存aop基类:
  1. public abstract class CacheAopBase : IInterceptor
  2. {
  3.         /// <summary>
  4.         /// AOP的拦截方法
  5.         /// </summary>
  6.         /// <param name="invocation"></param>
  7.         public abstract void Intercept(IInvocation invocation);
  8.         /// <summary>
  9.         /// 自定义缓存的key
  10.         /// </summary>
  11.         /// <param name="invocation"></param>
  12.         /// <returns></returns>
  13.         protected string CustomCacheKey(IInvocation invocation)
  14.         {
  15.                 var typeName = invocation.TargetType.Name;
  16.                 var methodName = invocation.Method.Name;
  17.                 var methodArguments = invocation.Arguments.Select(GetArgumentValue).Take(3).ToList();//获取参数列表,最多三个
  18.                 string key = $"{typeName}:{methodName}:";
  19.                 foreach (var param in methodArguments)
  20.                 {
  21.                         key = $"{key}{param}:";
  22.                 }
  23.                 return key.TrimEnd(':');
  24.         }
  25.         /// <summary>
  26.         /// object 转 string
  27.         /// </summary>
  28.         /// <param name="arg"></param>
  29.         /// <returns></returns>
  30.         protected static string GetArgumentValue(object arg)
  31.         {
  32.                 if (arg is DateTime)
  33.                         return ((DateTime)arg).ToString("yyyyMMddHHmmss");
  34.                 if (arg is string || arg is ValueType)
  35.                         return arg.ToString();
  36.                 if (arg != null)
  37.                 {
  38.                         if (arg is Expression)
  39.                         {
  40.                                 var obj = arg as Expression;
  41.                                 var result = Resolve(obj);
  42.                                 return CommonHelper.Md5For16(result);
  43.                         }
  44.                         else if (arg.GetType().IsClass)
  45.                         {
  46.                                 return CommonHelper.Md5For16(Newtonsoft.Json.JsonConvert.SerializeObject(arg));
  47.                         }
  48.                 }
  49.                 return string.Empty;
  50.         }
  51.         private static string Resolve(Expression expression)
  52.         {
  53.                 if (expression is LambdaExpression)
  54.                 {
  55.                         LambdaExpression lambda = expression as LambdaExpression;
  56.                         expression = lambda.Body;
  57.                         return Resolve(expression);
  58.                 }
  59.                 if (expression is BinaryExpression)
  60.                 {
  61.                         BinaryExpression binary = expression as BinaryExpression;
  62.                         if (binary.Left is MemberExpression && binary.Right is ConstantExpression)//解析x=>x.Name=="123" x.Age==123这类
  63.                                 return ResolveFunc(binary.Left, binary.Right, binary.NodeType);
  64.                         if (binary.Left is MethodCallExpression && binary.Right is ConstantExpression)//解析x=>x.Name.Contains("xxx")==false这类的
  65.                         {
  66.                                 object value = (binary.Right as ConstantExpression).Value;
  67.                                 return ResolveLinqToObject(binary.Left, value, binary.NodeType);
  68.                         }
  69.                         if ((binary.Left is MemberExpression && binary.Right is MemberExpression)
  70.                                 || (binary.Left is MemberExpression && binary.Right is UnaryExpression))//解析x=>x.Date==DateTime.Now这种
  71.                         {
  72.                                 LambdaExpression lambda = Expression.Lambda(binary.Right);
  73.                                 Delegate fn = lambda.Compile();
  74.                                 ConstantExpression value = Expression.Constant(fn.DynamicInvoke(null), binary.Right.Type);
  75.                                 return ResolveFunc(binary.Left, value, binary.NodeType);
  76.                         }
  77.                 }
  78.                 if (expression is UnaryExpression)
  79.                 {
  80.                         UnaryExpression unary = expression as UnaryExpression;
  81.                         if (unary.Operand is MethodCallExpression)//解析!x=>x.Name.Contains("xxx")或!array.Contains(x.Name)这类
  82.                                 return ResolveLinqToObject(unary.Operand, false);
  83.                         if (unary.Operand is MemberExpression && unary.NodeType == ExpressionType.Not)//解析x=>!x.isDeletion这样的
  84.                         {
  85.                                 ConstantExpression constant = Expression.Constant(false);
  86.                                 return ResolveFunc(unary.Operand, constant, ExpressionType.Equal);
  87.                         }
  88.                 }
  89.                 if (expression is MemberExpression && expression.NodeType == ExpressionType.MemberAccess)//解析x=>x.isDeletion这样的
  90.                 {
  91.                         MemberExpression member = expression as MemberExpression;
  92.                         ConstantExpression constant = Expression.Constant(true);
  93.                         return ResolveFunc(member, constant, ExpressionType.Equal);
  94.                 }
  95.                 if (expression is MethodCallExpression)//x=>x.Name.Contains("xxx")或array.Contains(x.Name)这类
  96.                 {
  97.                         MethodCallExpression methodcall = expression as MethodCallExpression;
  98.                         return ResolveLinqToObject(methodcall, true);
  99.                 }
  100.                 var body = expression as BinaryExpression;
  101.                 //已经修改过代码body应该不会是null值了
  102.                 if (body == null)
  103.                         return string.Empty;
  104.                 var Operator = GetOperator(body.NodeType);
  105.                 var Left = Resolve(body.Left);
  106.                 var Right = Resolve(body.Right);
  107.                 string Result = string.Format("({0} {1} {2})", Left, Operator, Right);
  108.                 return Result;
  109.         }
  110.         private static string GetOperator(ExpressionType expressiontype)
  111.         {
  112.                 switch (expressiontype)
  113.                 {
  114.                         case ExpressionType.And:
  115.                                 return "and";
  116.                         case ExpressionType.AndAlso:
  117.                                 return "and";
  118.                         case ExpressionType.Or:
  119.                                 return "or";
  120.                         case ExpressionType.OrElse:
  121.                                 return "or";
  122.                         case ExpressionType.Equal:
  123.                                 return "=";
  124.                         case ExpressionType.NotEqual:
  125.                                 return "<>";
  126.                         case ExpressionType.LessThan:
  127.                                 return "<";
  128.                         case ExpressionType.LessThanOrEqual:
  129.                                 return "<=";
  130.                         case ExpressionType.GreaterThan:
  131.                                 return ">";
  132.                         case ExpressionType.GreaterThanOrEqual:
  133.                                 return ">=";
  134.                         default:
  135.                                 throw new Exception(string.Format("不支持{0}此种运算符查找!" + expressiontype));
  136.                 }
  137.         }
  138.         private static string ResolveFunc(Expression left, Expression right, ExpressionType expressiontype)
  139.         {
  140.                 var Name = (left as MemberExpression).Member.Name;
  141.                 var Value = (right as ConstantExpression).Value;
  142.                 var Operator = GetOperator(expressiontype);
  143.                 return Name + Operator + Value ?? "null";
  144.         }
  145.         private static string ResolveLinqToObject(Expression expression, object value, ExpressionType? expressiontype = null)
  146.         {
  147.                 var MethodCall = expression as MethodCallExpression;
  148.                 var MethodName = MethodCall.Method.Name;
  149.                 switch (MethodName)
  150.                 {
  151.                         case "Contains":
  152.                                 if (MethodCall.Object != null)
  153.                                         return Like(MethodCall);
  154.                                 return In(MethodCall, value);
  155.                         case "Count":
  156.                                 return Len(MethodCall, value, expressiontype.Value);
  157.                         case "LongCount":
  158.                                 return Len(MethodCall, value, expressiontype.Value);
  159.                         default:
  160.                                 throw new Exception(string.Format("不支持{0}方法的查找!", MethodName));
  161.                 }
  162.         }
  163.         private static string In(MethodCallExpression expression, object isTrue)
  164.         {
  165.                 var Argument1 = (expression.Arguments[0] as MemberExpression).Expression as ConstantExpression;
  166.                 var Argument2 = expression.Arguments[1] as MemberExpression;
  167.                 var Field_Array = Argument1.Value.GetType().GetFields().First();
  168.                 object[] Array = Field_Array.GetValue(Argument1.Value) as object[];
  169.                 List<string> SetInPara = new List<string>();
  170.                 for (int i = 0; i < Array.Length; i++)
  171.                 {
  172.                         string Name_para = "InParameter" + i;
  173.                         string Value = Array[i].ToString();
  174.                         SetInPara.Add(Value);
  175.                 }
  176.                 string Name = Argument2.Member.Name;
  177.                 string Operator = Convert.ToBoolean(isTrue) ? "in" : " not in";
  178.                 string CompName = string.Join(",", SetInPara);
  179.                 string Result = string.Format("{0} {1} ({2})", Name, Operator, CompName);
  180.                 return Result;
  181.         }
  182.         private static string Like(MethodCallExpression expression)
  183.         {
  184.                 var Temp = expression.Arguments[0];
  185.                 LambdaExpression lambda = Expression.Lambda(Temp);
  186.                 Delegate fn = lambda.Compile();
  187.                 var tempValue = Expression.Constant(fn.DynamicInvoke(null), Temp.Type);
  188.                 string Value = string.Format("%{0}%", tempValue);
  189.                 string Name = (expression.Object as MemberExpression).Member.Name;
  190.                 string Result = string.Format("{0} like {1}", Name, Value);
  191.                 return Result;
  192.         }
  193.         private static string Len(MethodCallExpression expression, object value, ExpressionType expressiontype)
  194.         {
  195.                 object Name = (expression.Arguments[0] as MemberExpression).Member.Name;
  196.                 string Operator = GetOperator(expressiontype);
  197.                 string Result = string.Format("len({0}){1}{2}", Name, Operator, value.ToString());
  198.                 return Result;
  199.         }
  200. }
复制代码
缓存AOP的实现:
  1. /// <summary>
  2. /// 面向切换的内存缓存使用
  3. /// </summary>
  4. public class MemoryCacheAop : CacheAopBase
  5. {
  6.         //通过注入的方式,把缓存操作接口通过构造函数注入
  7.         private readonly ICachingProvider _cache;
  8.         public MemoryCacheAop(ICachingProvider cache)
  9.         {
  10.                 _cache = cache;
  11.         }
  12.         //Intercept方法是拦截的关键所在,也是IInterceptor接口中的唯一定义
  13.         public override void Intercept(IInvocation invocation)
  14.         {
  15.                 var method = invocation.MethodInvocationTarget ?? invocation.Method;
  16.                 //对当前方法的特性验证
  17.                 //如果需要验证
  18.                 //var CachingAttribute = method.GetCustomAttributes(true).FirstOrDefault(x => x.GetType() == typeof(CachingAttribute));
  19.                 var CachingAttribute = method.GetCustomAttribute<CachingAttribute>(true);
  20.                 Console.WriteLine(method.Name);
  21.                 Console.WriteLine(method.DeclaringType.FullName);
  22.                 if (CachingAttribute is CachingAttribute qCachingAttribute)
  23.                 {
  24.                         //获取自定义缓存键
  25.                         var cacheKey = CustomCacheKey(invocation);
  26.                         //根据key获取相应的缓存值
  27.                         var cacheValue = _cache.Get(cacheKey);
  28.                         if (cacheValue != null)
  29.                         {
  30.                                 //将当前获取到的缓存值,赋值给当前执行方法
  31.                                 invocation.ReturnValue = cacheValue;
  32.                                 return;
  33.                         }
  34.                         //去执行当前的方法
  35.                         invocation.Proceed();
  36.                         //存入缓存
  37.                         if (!string.IsNullOrWhiteSpace(cacheKey))
  38.                         {
  39.                                 _cache.Set(cacheKey, invocation.ReturnValue, qCachingAttribute.AbsoluteExpiration);
  40.                         }
  41.                 }
  42.                 else
  43.                 {
  44.                         invocation.Proceed();//直接执行被拦截方法
  45.                 }
  46.         }
  47. }
复制代码
5.界说AutofacModuleRegister 模块注册类实现模块间的解耦,在这个类中注册所有的接口和服务等:在这里实现MemoryCacheAop的拦截开启
  1. public class AutofacModuleRegister : Autofac.Module
  2. {
  3.         protected override void Load(ContainerBuilder builder)
  4.         {
  5.                 var basePath = AppContext.BaseDirectory;
  6.                 #region 带有接口层的服务注入
  7.                 var servicesDllFile = Path.Combine(basePath, "Net.Service.dll");
  8.                 var repositoryDllFile = Path.Combine(basePath, "Net.Repository.dll");
  9.                 if (!(File.Exists(servicesDllFile) && File.Exists(repositoryDllFile)))
  10.                 {
  11.                         var msg = "Repository.dll和Service.dll 丢失,因为项目解耦了,所以需要先F6编译,再F5运行,请检查 bin 文件夹,并拷贝。";
  12.                         throw new Exception(msg);
  13.                 }
  14.                 // AOP 开关,如果想要打开指定的功能,只需要在 appsettigns.json 对应的配置项设置为 true 就行。
  15.                 var cacheType = new List<Type>();
  16.                 if (AppSettingsConstVars.RedisUseCache)
  17.                 {
  18.                         //builder.RegisterType<RedisCacheAop>();
  19.                         //cacheType.Add(typeof(RedisCacheAop));
  20.                 }
  21.                 else
  22.                 {
  23.                         builder.RegisterType<MemoryCacheAop>();
  24.                         cacheType.Add(typeof(MemoryCacheAop));
  25.                 }
  26.                 //// 获取 Service.dll 程序集服务,并注册
  27.                 //var assemblysServices = Assembly.LoadFrom(servicesDllFile);
  28.                 ////支持属性注入依赖重复
  29.                 //builder.RegisterAssemblyTypes(assemblysServices).AsImplementedInterfaces().InstancePerDependency()
  30.                 //    .PropertiesAutowired(PropertyWiringOptions.AllowCircularDependencies);
  31.                 //// 获取 Repository.dll 程序集服务,并注册
  32.                 //var assemblysRepository = Assembly.LoadFrom(repositoryDllFile);
  33.                 ////支持属性注入依赖重复
  34.                 //builder.RegisterAssemblyTypes(assemblysRepository).AsImplementedInterfaces().InstancePerDependency()
  35.                 //    .PropertiesAutowired(PropertyWiringOptions.AllowCircularDependencies);
  36.                 // 获取 Service.dll 程序集服务,并注册
  37.                 var assemblysServices = Assembly.LoadFrom(servicesDllFile);
  38.                 builder.RegisterAssemblyTypes(assemblysServices)
  39.                         .AsImplementedInterfaces()
  40.                         .InstancePerDependency()
  41.                         .PropertiesAutowired()
  42.                         .EnableInterfaceInterceptors()//引用Autofac.Extras.DynamicProxy;
  43.                         .InterceptedBy(cacheType.ToArray());//允许将拦截器服务的列表分配给注册。
  44.                 // 获取 Repository.dll 程序集服务,并注册
  45.                 var assemblysRepository = Assembly.LoadFrom(repositoryDllFile);
  46.                 builder.RegisterAssemblyTypes(assemblysRepository)
  47.                         .AsImplementedInterfaces()
  48.                         .PropertiesAutowired()
  49.                         .InstancePerDependency();
  50.                 #endregion
  51.         }
  52. }
复制代码
在program里注册缓存

6.设置数据库链接

在appsettings.json设置文件上设置数据库链接,以及redis缓存连接,本文档暂时先不讲解redis缓存(等待下一篇)
如果设置UseCache=true,那么就开启redis缓存,设置UseCache=false则开启memoryCache缓存
  1. //redis为必须启动项,请保持redis为正常可用
  2. "RedisConfig": {
  3.   "UseCache": false, //启用redis作为内存选择
  4.   // 如果采用容器化部署Service 要写成redis的服务名,否则写地址
  5.   "ConnectionString": "127.0.0.1:6379,password=CoreShop,connectTimeout=3000,connectRetry=1,syncTimeout=10000,DefaultDatabase=10" //redis数据库连接字符串
  6. },
复制代码
这里我设置了环境变量,包管我的数据库真实连接不袒露,你也可以改为自己的数据库连接:


设置完后需要先关闭vs,然后再打开才能生效
7.怎样使用缓存拦截

起首在服务的基类实现中添加Caching缓存特性;
在BaseServices里添加缓存特性,并设置过期时间为:30分钟:Caching(AbsoluteExpiration = 3)

其次在GetUserInfo方法调用QueryByIdAsync方法查询id=1的用户

再次调用GetUserCache方法,获取缓存中的id=1的用户数据,可以查看输出结果:
8.启动项目,分别调用方法:GetUserInfo和GetUserCache

调用GetUserInfo查询数据,写入缓存成功

再次调用GetUserCache,一样的缓存key和返回一样的用户信息:

通过以上的讲解:你是否已司理解了MemoryCache的使用呢?
欢迎点赞,关注,批评!

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
继续阅读请点击广告

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

伤心客

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表