项目搭建了一个基础的框架,实现缓存的AOP拦截,首次查询从数据库获取,再写入缓存,设置过期时间,再次查询数据时从缓存获取。
话不多说我们来上代码实现:
1.定义缓存的接口和实现类
定义缓存接口ICachingProvider和实现类MemoryCaching:
/// <summary>
/// 简单的缓存接口,只有查询和添加,以后会进行扩展
/// </summary>
public interface ICachingProvider
{
object Get(string cacheKey);
void Set(string cacheKey, object cacheValue, int timeSpan);
}
/// <summary>
/// 实例化缓存接口ICaching
/// </summary>
public class MemoryCaching : ICachingProvider
{
//引用Microsoft.Extensions.Caching.Memory;这个和.net 还是不一样,没有了Httpruntime了
private readonly IMemoryCache _cache;
//还是通过构造函数的方法,获取
public MemoryCaching(IMemoryCache cache)
{
_cache = cache;
}
public object Get(string cacheKey)
{
return _cache.Get(cacheKey);
}
public void Set(string cacheKey, object cacheValue, int timeSpan)
{
_cache.Set(cacheKey, cacheValue, TimeSpan.FromSeconds(timeSpan * 60));
}
}
2.定义缓存的特性CachingAttribute:
/// <summary>
/// 这个Attribute就是使用时候的验证,把它添加到要缓存数据的方法中,即可完成缓存的操作。
/// </summary>
[AttributeUsage(AttributeTargets.Method, Inherited = true)]
public class CachingAttribute : System.Attribute
{
/// <summary>
/// 缓存绝对过期时间(分钟)
/// </summary>
public int AbsoluteExpiration { get; set; } = 30;
}
3.定义MemoryCacheSetup 启动服务注册
/// <summary>
/// Memory缓存 启动服务
/// </summary>
public static class MemoryCacheSetup
{
public static void AddMemoryCacheSetup(this IServiceCollection services)
{
if (services == null) throw new ArgumentNullException(nameof(services));
services.AddScoped<ICachingProvider, MemoryCaching>();
services.AddSingleton<IMemoryCache>(factory =>
{
var cache = new MemoryCache(new MemoryCacheOptions());
return cache;
});
}
}
4.CacheAopBase缓存aop基础类,面向切换的内存缓存使用
定义缓存aop基类:
public abstract class CacheAopBase : IInterceptor
{
/// <summary>
/// AOP的拦截方法
/// </summary>
/// <param name="invocation"></param>
public abstract void Intercept(IInvocation invocation);
/// <summary>
/// 自定义缓存的key
/// </summary>
/// <param name="invocation"></param>
/// <returns></returns>
protected string CustomCacheKey(IInvocation invocation)
{
var typeName = invocation.TargetType.Name;
var methodName = invocation.Method.Name;
var methodArguments = invocation.Arguments.Select(GetArgumentValue).Take(3).ToList();//获取参数列表,最多三个
string key = $"{typeName}:{methodName}:";
foreach (var param in methodArguments)
{
key = $"{key}{param}:";
}
return key.TrimEnd(':');
}
/// <summary>
/// object 转 string
/// </summary>
/// <param name="arg"></param>
/// <returns></returns>
protected static string GetArgumentValue(object arg)
{
if (arg is DateTime)
return ((DateTime)arg).ToString("yyyyMMddHHmmss");
if (arg is string || arg is ValueType)
return arg.ToString();
if (arg != null)
{
if (arg is Expression)
{
var obj = arg as Expression;
var result = Resolve(obj);
return CommonHelper.Md5For16(result);
}
else if (arg.GetType().IsClass)
{
return CommonHelper.Md5For16(Newtonsoft.Json.JsonConvert.SerializeObject(arg));
}
}
return string.Empty;
}
private static string Resolve(Expression expression)
{
if (expression is LambdaExpression)
{
LambdaExpression lambda = expression as LambdaExpression;
expression = lambda.Body;
return Resolve(expression);
}
if (expression is BinaryExpression)
{
BinaryExpression binary = expression as BinaryExpression;
if (binary.Left is MemberExpression && binary.Right is ConstantExpression)//解析x=>x.Name=="123" x.Age==123这类
return ResolveFunc(binary.Left, binary.Right, binary.NodeType);
if (binary.Left is MethodCallExpression && binary.Right is ConstantExpression)//解析x=>x.Name.Contains("xxx")==false这类的
{
object value = (binary.Right as ConstantExpression).Value;
return ResolveLinqToObject(binary.Left, value, binary.NodeType);
}
if ((binary.Left is MemberExpression && binary.Right is MemberExpression)
|| (binary.Left is MemberExpression && binary.Right is UnaryExpression))//解析x=>x.Date==DateTime.Now这种
{
LambdaExpression lambda = Expression.Lambda(binary.Right);
Delegate fn = lambda.Compile();
ConstantExpression value = Expression.Constant(fn.DynamicInvoke(null), binary.Right.Type);
return ResolveFunc(binary.Left, value, binary.NodeType);
}
}
if (expression is UnaryExpression)
{
UnaryExpression unary = expression as UnaryExpression;
if (unary.Operand is MethodCallExpression)//解析!x=>x.Name.Contains("xxx")或!array.Contains(x.Name)这类
return ResolveLinqToObject(unary.Operand, false);
if (unary.Operand is MemberExpression && unary.NodeType == ExpressionType.Not)//解析x=>!x.isDeletion这样的
{
ConstantExpression constant = Expression.Constant(false);
return ResolveFunc(unary.Operand, constant, ExpressionType.Equal);
}
}
if (expression is MemberExpression && expression.NodeType == ExpressionType.MemberAccess)//解析x=>x.isDeletion这样的
{
MemberExpression member = expression as MemberExpression;
ConstantExpression constant = Expression.Constant(true);
return ResolveFunc(member, constant, ExpressionType.Equal);
}
if (expression is MethodCallExpression)//x=>x.Name.Contains("xxx")或array.Contains(x.Name)这类
{
MethodCallExpression methodcall = expression as MethodCallExpression;
return ResolveLinqToObject(methodcall, true);
}
var body = expression as BinaryExpression;
//已经修改过代码body应该不会是null值了
if (body == null)
return string.Empty;
var Operator = GetOperator(body.NodeType);
var Left = Resolve(body.Left);
var Right = Resolve(body.Right);
string Result = string.Format("({0} {1} {2})", Left, Operator, Right);
return Result;
}
private static string GetOperator(ExpressionType expressiontype)
{
switch (expressiontype)
{
case ExpressionType.And:
return "and";
case ExpressionType.AndAlso:
return "and";
case ExpressionType.Or:
return "or";
case ExpressionType.OrElse:
return "or";
case ExpressionType.Equal:
return "=";
case ExpressionType.NotEqual:
return "<>";
case ExpressionType.LessThan:
return "<";
case ExpressionType.LessThanOrEqual:
return "<=";
case ExpressionType.GreaterThan:
return ">";
case ExpressionType.GreaterThanOrEqual:
return ">=";
default:
throw new Exception(string.Format("不支持{0}此种运算符查找!" + expressiontype));
}
}
private static string ResolveFunc(Expression left, Expression right, ExpressionType expressiontype)
{
var Name = (left as MemberExpression).Member.Name;
var Value = (right as ConstantExpression).Value;
var Operator = GetOperator(expressiontype);
return Name + Operator + Value ?? "null";
}
private static string ResolveLinqToObject(Expression expression, object value, ExpressionType? expressiontype = null)
{
var MethodCall = expression as MethodCallExpression;
var MethodName = MethodCall.Method.Name;
switch (MethodName)
{
case "Contains":
if (MethodCall.Object != null)
return Like(MethodCall);
return In(MethodCall, value);
case "Count":
return Len(MethodCall, value, expressiontype.Value);
case "LongCount":
return Len(MethodCall, value, expressiontype.Value);
default:
throw new Exception(string.Format("不支持{0}方法的查找!", MethodName));
}
}
private static string In(MethodCallExpression expression, object isTrue)
{
var Argument1 = (expression.Arguments[0] as MemberExpression).Expression as ConstantExpression;
var Argument2 = expression.Arguments[1] as MemberExpression;
var Field_Array = Argument1.Value.GetType().GetFields().First();
object[] Array = Field_Array.GetValue(Argument1.Value) as object[];
List<string> SetInPara = new List<string>();
for (int i = 0; i < Array.Length; i++)
{
string Name_para = "InParameter" + i;
string Value = Array[i].ToString();
SetInPara.Add(Value);
}
string Name = Argument2.Member.Name;
string Operator = Convert.ToBoolean(isTrue) ? "in" : " not in";
string CompName = string.Join(",", SetInPara);
string Result = string.Format("{0} {1} ({2})", Name, Operator, CompName);
return Result;
}
private static string Like(MethodCallExpression expression)
{
var Temp = expression.Arguments[0];
LambdaExpression lambda = Expression.Lambda(Temp);
Delegate fn = lambda.Compile();
var tempValue = Expression.Constant(fn.DynamicInvoke(null), Temp.Type);
string Value = string.Format("%{0}%", tempValue);
string Name = (expression.Object as MemberExpression).Member.Name;
string Result = string.Format("{0} like {1}", Name, Value);
return Result;
}
private static string Len(MethodCallExpression expression, object value, ExpressionType expressiontype)
{
object Name = (expression.Arguments[0] as MemberExpression).Member.Name;
string Operator = GetOperator(expressiontype);
string Result = string.Format("len({0}){1}{2}", Name, Operator, value.ToString());
return Result;
}
}
缓存AOP的实现:
/// <summary>
/// 面向切换的内存缓存使用
/// </summary>
public class MemoryCacheAop : CacheAopBase
{
//通过注入的方式,把缓存操作接口通过构造函数注入
private readonly ICachingProvider _cache;
public MemoryCacheAop(ICachingProvider cache)
{
_cache = cache;
}
//Intercept方法是拦截的关键所在,也是IInterceptor接口中的唯一定义
public override void Intercept(IInvocation invocation)
{
var method = invocation.MethodInvocationTarget ?? invocation.Method;
//对当前方法的特性验证
//如果需要验证
//var CachingAttribute = method.GetCustomAttributes(true).FirstOrDefault(x => x.GetType() == typeof(CachingAttribute));
var CachingAttribute = method.GetCustomAttribute<CachingAttribute>(true);
Console.WriteLine(method.Name);
Console.WriteLine(method.DeclaringType.FullName);
if (CachingAttribute is CachingAttribute qCachingAttribute)
{
//获取自定义缓存键
var cacheKey = CustomCacheKey(invocation);
//根据key获取相应的缓存值
var cacheValue = _cache.Get(cacheKey);
if (cacheValue != null)
{
//将当前获取到的缓存值,赋值给当前执行方法
invocation.ReturnValue = cacheValue;
return;
}
//去执行当前的方法
invocation.Proceed();
//存入缓存
if (!string.IsNullOrWhiteSpace(cacheKey))
{
_cache.Set(cacheKey, invocation.ReturnValue, qCachingAttribute.AbsoluteExpiration);
}
}
else
{
invocation.Proceed();//直接执行被拦截方法
}
}
}
5.定义AutofacModuleRegister 模块注册类实现模块间的解耦,在这个类中注册所有的接口和服务等:在这里实现MemoryCacheAop的拦截开启
public class AutofacModuleRegister : Autofac.Module
{
protected override void Load(ContainerBuilder builder)
{
var basePath = AppContext.BaseDirectory;
#region 带有接口层的服务注入
var servicesDllFile = Path.Combine(basePath, "Net.Service.dll");
var repositoryDllFile = Path.Combine(basePath, "Net.Repository.dll");
if (!(File.Exists(servicesDllFile) && File.Exists(repositoryDllFile)))
{
var msg = "Repository.dll和Service.dll 丢失,因为项目解耦了,所以需要先F6编译,再F5运行,请检查 bin 文件夹,并拷贝。";
throw new Exception(msg);
}
// AOP 开关,如果想要打开指定的功能,只需要在 appsettigns.json 对应的配置项设置为 true 就行。
var cacheType = new List<Type>();
if (AppSettingsConstVars.RedisUseCache)
{
//builder.RegisterType<RedisCacheAop>();
//cacheType.Add(typeof(RedisCacheAop));
}
else
{
builder.RegisterType<MemoryCacheAop>();
cacheType.Add(typeof(MemoryCacheAop));
}
//// 获取 Service.dll 程序集服务,并注册
//var assemblysServices = Assembly.LoadFrom(servicesDllFile);
////支持属性注入依赖重复
//builder.RegisterAssemblyTypes(assemblysServices).AsImplementedInterfaces().InstancePerDependency()
// .PropertiesAutowired(PropertyWiringOptions.AllowCircularDependencies);
//// 获取 Repository.dll 程序集服务,并注册
//var assemblysRepository = Assembly.LoadFrom(repositoryDllFile);
////支持属性注入依赖重复
//builder.RegisterAssemblyTypes(assemblysRepository).AsImplementedInterfaces().InstancePerDependency()
// .PropertiesAutowired(PropertyWiringOptions.AllowCircularDependencies);
// 获取 Service.dll 程序集服务,并注册
var assemblysServices = Assembly.LoadFrom(servicesDllFile);
builder.RegisterAssemblyTypes(assemblysServices)
.AsImplementedInterfaces()
.InstancePerDependency()
.PropertiesAutowired()
.EnableInterfaceInterceptors()//引用Autofac.Extras.DynamicProxy;
.InterceptedBy(cacheType.ToArray());//允许将拦截器服务的列表分配给注册。
// 获取 Repository.dll 程序集服务,并注册
var assemblysRepository = Assembly.LoadFrom(repositoryDllFile);
builder.RegisterAssemblyTypes(assemblysRepository)
.AsImplementedInterfaces()
.PropertiesAutowired()
.InstancePerDependency();
#endregion
}
}
在program里注册缓存
6.配置数据库链接
在appsettings.json配置文件上配置数据库链接,以及redis缓存连接,本文档暂时先不讲解redis缓存(等待下一篇)
如果设置UseCache=true,那么就开启redis缓存,设置UseCache=false则开启memoryCache缓存
//redis为必须启动项,请保持redis为正常可用
"RedisConfig": {
"UseCache": false, //启用redis作为内存选择
// 如果采用容器化部署Service 要写成redis的服务名,否则写地址
"ConnectionString": "127.0.0.1:6379,password=CoreShop,connectTimeout=3000,connectRetry=1,syncTimeout=10000,DefaultDatabase=10" //redis数据库连接字符串
},
这里我配置了环境变量,保证我的数据库真实连接不暴露,你也可以改为自己的数据库连接:
配置完后需要先关闭vs,然后再打开才能生效
7.如何使用缓存拦截
首先在服务的基类实现中添加Caching缓存特性;
在BaseServices里添加缓存特性,并设置过期时间为:30分钟:Caching(AbsoluteExpiration = 3)
其次在GetUserInfo方法调用QueryByIdAsync方法查询id=1的用户
再次调用GetUserCache方法,获取缓存中的id=1的用户数据,可以查看输出结果:
8.启动项目,分别调用方法:GetUserInfo和GetUserCache
调用GetUserInfo查询数据,写入缓存成功
再次调用GetUserCache,一样的缓存key和返回一样的用户信息:
通过以上的讲解:你是否已经理解了MemoryCache的使用呢?
欢迎点赞,关注,评论!
来源链接:https://www.cnblogs.com/chenshibao/p/18854479
© 版权声明
本站所有资源来自于网络,仅供学习与参考,请勿用于商业用途,否则产生的一切后果将由您(转载者)自己承担!
如有侵犯您的版权,请及时联系3500663466#qq.com(#换@),我们将第一时间删除本站数据。
如有侵犯您的版权,请及时联系3500663466#qq.com(#换@),我们将第一时间删除本站数据。
THE END
暂无评论内容