添加项目文件。
This commit is contained in:
@@ -0,0 +1,423 @@
|
||||
// Copyright (c) 2022-Now 少林寺驻北固山办事处大神父王喇嘛
|
||||
//
|
||||
// SimpleAdmin 基于 Apache License Version 2.0 协议发布,可用于商业项目,但必须遵守以下补充条款:
|
||||
// 1.请不要删除和修改根目录下的LICENSE文件。
|
||||
// 2.请不要删除和修改SimpleAdmin源码头部的版权声明。
|
||||
// 3.分发源码时候,请注明软件出处 https://gitee.com/dotnetmoyu/SimpleAdmin
|
||||
// 4.基于本软件的作品,只能使用 SimpleAdmin 作为后台服务,除外情况不可商用且不允许二次分发或开源。
|
||||
// 5.请不得将本软件应用于危害国家安全、荣誉和利益的行为,不能以任何形式用于非法为目的的行为。
|
||||
// 6.任何基于本软件而产生的一切法律纠纷和责任,均于我司无关。
|
||||
|
||||
using SimpleAdmin.Core;
|
||||
|
||||
namespace SimpleAdmin.Plugin.Aop;
|
||||
|
||||
/// <summary>
|
||||
/// Aop
|
||||
/// 这里没有继承IGlobalDispatchProxy是因为IGlobalDispatchProxy会把一些没有必要的方法也aop了
|
||||
/// </summary>
|
||||
public class GlobalDispatchProxy : AspectDispatchProxy, IDispatchProxy
|
||||
{
|
||||
/// <summary>
|
||||
/// 当前服务实例
|
||||
/// </summary>
|
||||
public object Target { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 服务提供器,可以用来解析服务,如:Services.GetService()
|
||||
/// </summary>
|
||||
public IServiceProvider Services { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 方法
|
||||
/// </summary>
|
||||
/// <param name="method"></param>
|
||||
/// <param name="args"></param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="NotImplementedException"></exception>
|
||||
public override object Invoke(MethodInfo method, object[] args)
|
||||
{
|
||||
//如果不带返回值
|
||||
if (method.ReturnType == typeof(void))
|
||||
{
|
||||
After(method, null, args);
|
||||
return method.Invoke(Target, args);//直接返回
|
||||
}
|
||||
var result = Before(method, args);//方法执行之前判断是否有缓存的数据
|
||||
if (result == null) result = method.Invoke(Target, args);//如果没有缓存就执行方法返回数据
|
||||
After(method, result, args);//方法执行之后干的事
|
||||
return result;//返回结果
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 异步无返回值
|
||||
/// </summary>
|
||||
/// <param name="method"></param>
|
||||
/// <param name="args"></param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="NotImplementedException"></exception>
|
||||
public override async Task InvokeAsync(MethodInfo method, object[] args)
|
||||
{
|
||||
var task = method.Invoke(Target, args) as Task;
|
||||
await task;
|
||||
After(method, null, args);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 异步带返回值
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="method"></param>
|
||||
/// <param name="args"></param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="NotImplementedException"></exception>
|
||||
public override async Task<T> InvokeAsyncT<T>(MethodInfo method, object[] args)
|
||||
{
|
||||
var result = Before(method, args);//方法执行之前判断是否有缓存的数据
|
||||
if (result == null)
|
||||
{
|
||||
var taskT = method.Invoke(Target, args) as Task<T>;
|
||||
result = await taskT;//如果没有缓存就执行方法返回数据
|
||||
}
|
||||
After(method, result, args);//方法执行之后干的事
|
||||
return result;//返回结果
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 方法执行之后
|
||||
/// </summary>
|
||||
/// <param name="method">方法</param>
|
||||
/// <param name="returnValue">返回值</param>
|
||||
/// <param name="args">参数列表</param>
|
||||
private void After(MethodInfo method, object returnValue, object[] args)
|
||||
{
|
||||
RecordCache(method, args, returnValue);//记录返回值到缓存
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 方法执行之前
|
||||
/// </summary>
|
||||
/// <param name="method">方法</param>
|
||||
/// <param name="args">参数列表</param>
|
||||
/// <returns></returns>
|
||||
private dynamic Before(MethodInfo method, object[] args)
|
||||
{
|
||||
var cacheData = CheckCache(method, args);//检查缓存
|
||||
if (cacheData != null) { return cacheData; }//如果缓存有数据直接返回
|
||||
return null;//默认返回空
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检查缓存里是否有数据
|
||||
/// </summary>
|
||||
/// <param name="method">方法</param>
|
||||
/// <param name="args">参数列表</param>
|
||||
private dynamic CheckCache(MethodInfo method, object[] args)
|
||||
{
|
||||
var cacheAttribute = method.GetActualCustomAttribute<CacheAttribute>(Target);//读取缓存特性
|
||||
//判断需不需要读取缓存
|
||||
if (cacheAttribute != null)
|
||||
{
|
||||
var redisManager = Services.GetService<ISimpleCacheService>();// 获取redis服务
|
||||
var cacheKey = cacheAttribute.CustomKeyValue
|
||||
?? CustomCacheKey(cacheAttribute.KeyPrefix, method, args);//如果redisKey值,如果有自定义值就用自定义Key,否则以前缀+系统自动生成的Key
|
||||
string cacheValue;
|
||||
if (cacheAttribute.StoreType == CacheConst.CACHE_HASH)//如果存的是Hash值
|
||||
{
|
||||
cacheValue = redisManager.HashGet<string>(cacheKey, args[0].ToString())[0];//从redis获取Hash数据取第一个,注意是 string 类型
|
||||
}
|
||||
else
|
||||
{
|
||||
cacheValue = redisManager.Get<string>(cacheKey);//注意是 string 类型,方法GetValue
|
||||
}
|
||||
if (!string.IsNullOrEmpty(cacheValue))//如果返回值不是空
|
||||
{
|
||||
//if (cacheAttribute.IsDelete)//判断是否是删除操作
|
||||
//{
|
||||
// if (cacheAttribute.StoreType == RedisConst.Cache_Hash)//如果是Hash
|
||||
// {
|
||||
// _redisManager.HashDel<string>(cacheKey, new string[] { args[0].ToString() });
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// //删除Redis整个KEY
|
||||
// _redisManager.Remove(cacheKey);
|
||||
// }
|
||||
|
||||
//}
|
||||
//将当前获取到的缓存值,赋值给当前执行方法
|
||||
Type returnType;
|
||||
if (typeof(Task).IsAssignableFrom(method.ReturnType))
|
||||
{
|
||||
returnType = method.ReturnType.GenericTypeArguments.FirstOrDefault();
|
||||
}
|
||||
else
|
||||
{
|
||||
returnType = method.ReturnType;
|
||||
}
|
||||
if (!returnType.IsPrimitive && returnType != typeof(string))//判断返回类型是否原生类型并且不是string
|
||||
{
|
||||
dynamic result = JsonConvert.DeserializeObject(cacheValue, returnType);//序列化数据
|
||||
return result;
|
||||
}
|
||||
return cacheValue;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 写数据到缓存
|
||||
/// </summary>
|
||||
/// <param name="method">方法</param>
|
||||
/// <param name="args">参数列表</param>
|
||||
/// <param name="returnValue">返回值</param>
|
||||
private void RecordCache(MethodInfo method, object[] args, object returnValue)
|
||||
{
|
||||
var cacheAttribute = method.GetActualCustomAttribute<CacheAttribute>(Target);
|
||||
if (cacheAttribute != null)
|
||||
{
|
||||
//获取自定义缓存键
|
||||
var cacheKey = cacheAttribute.CustomKeyValue ?? CustomCacheKey(cacheAttribute.KeyPrefix, method, args);
|
||||
if (!string.IsNullOrWhiteSpace(cacheKey))//如果有key
|
||||
{
|
||||
var redisManager = Services.GetService<ISimpleCacheService>();// 获取redis服务
|
||||
if (cacheAttribute.IsDelete)//判断是否是删除操作
|
||||
{
|
||||
//删除Redis整个KEY
|
||||
redisManager.Remove(cacheKey);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (returnValue == null) { return; }
|
||||
if (cacheAttribute.StoreType == CacheConst.CACHE_HASH)//如果是hash类型的
|
||||
{
|
||||
//插入到hash,这里规定是方法的第一个参数
|
||||
redisManager.HashAdd(cacheKey, args[0].ToString(), returnValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (cacheAttribute.AbsoluteExpiration != null)//如果有超时时间
|
||||
{
|
||||
redisManager.Set(cacheKey, returnValue, cacheAttribute.AbsoluteExpiration.Value);//插入redis
|
||||
}
|
||||
else
|
||||
{
|
||||
redisManager.Set(cacheKey, returnValue);//插入redis
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 自定义缓存Key
|
||||
/// </summary>
|
||||
/// <param name="prefix">前缀</param>
|
||||
/// <param name="method">方法</param>
|
||||
/// <param name="args">参数</param>
|
||||
/// <returns></returns>
|
||||
protected string CustomCacheKey(string prefix, MethodInfo method, object[] args)
|
||||
{
|
||||
var key = prefix;//前缀
|
||||
var methodArguments = args.Select(GetCacheKey.GetArgumentValue).Take(3).ToList();//获取参数列表,最多三个
|
||||
if (!string.IsNullOrEmpty(key))//如果制定了前缀
|
||||
{
|
||||
foreach (var param in methodArguments)//遍历参数列表
|
||||
{
|
||||
key = $"{key}{param}:";//生成KEY
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var typeName = Target.GetType().Name;//获取实例名
|
||||
var methodName = method.Name;//获取方法名
|
||||
key = $"{CacheConst.CACHE_PREFIX_WEB}{typeName}:{methodName}:";//生成Key
|
||||
foreach (var param in methodArguments)//遍历参数列表
|
||||
{
|
||||
key = $"{key}{param}:";//生成加上参数的KEY
|
||||
}
|
||||
}
|
||||
return key.TrimEnd(':');
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取缓存Key
|
||||
/// </summary>
|
||||
internal static class GetCacheKey
|
||||
{
|
||||
/// <summary>
|
||||
/// object 转 string
|
||||
/// </summary>
|
||||
/// <param name="arg"></param>
|
||||
/// <returns></returns>
|
||||
public static string GetArgumentValue(object arg)
|
||||
{
|
||||
if (arg is DateTime || arg is DateTime)
|
||||
return ((DateTime)arg).ToString("yyyyMMddHHmmss");
|
||||
|
||||
if (arg is string || arg is ValueType || arg is Nullable)
|
||||
return arg.ToString();
|
||||
|
||||
if (arg != null)
|
||||
{
|
||||
if (arg is Expression)
|
||||
{
|
||||
var obj = arg as Expression;
|
||||
var result = Resolve(obj);
|
||||
return MD5Encryption.Encrypt(result);
|
||||
}
|
||||
if (arg.GetType().IsClass)
|
||||
{
|
||||
return MD5Encryption.Encrypt(JsonConvert.SerializeObject(arg));
|
||||
}
|
||||
}
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
private static string Resolve(Expression expression)
|
||||
{
|
||||
if (expression is LambdaExpression)
|
||||
{
|
||||
var lambda = expression as LambdaExpression;
|
||||
expression = lambda.Body;
|
||||
return Resolve(expression);
|
||||
}
|
||||
if (expression is BinaryExpression)
|
||||
{
|
||||
var 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这类的
|
||||
{
|
||||
var 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这种
|
||||
{
|
||||
var lambda = Expression.Lambda(binary.Right);
|
||||
var fn = lambda.Compile();
|
||||
var value = Expression.Constant(fn.DynamicInvoke(null), binary.Right.Type);
|
||||
return ResolveFunc(binary.Left, value, binary.NodeType);
|
||||
}
|
||||
}
|
||||
if (expression is UnaryExpression)
|
||||
{
|
||||
var 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这样的
|
||||
{
|
||||
var constant = Expression.Constant(false);
|
||||
return ResolveFunc(unary.Operand, constant, ExpressionType.Equal);
|
||||
}
|
||||
}
|
||||
if (expression is MemberExpression && expression.NodeType == ExpressionType.MemberAccess)//解析x=>x.isDeletion这样的
|
||||
{
|
||||
var member = expression as MemberExpression;
|
||||
var constant = Expression.Constant(true);
|
||||
return ResolveFunc(member, constant, ExpressionType.Equal);
|
||||
}
|
||||
if (expression is MethodCallExpression)//x=>x.Name.Contains("xxx")或array.Contains(x.Name)这类
|
||||
{
|
||||
var methodcall = expression as MethodCallExpression;
|
||||
return ResolveLinqToObject(methodcall, true);
|
||||
}
|
||||
//已经修改过代码body应该不会是null值了
|
||||
if (!(expression is BinaryExpression body))
|
||||
return string.Empty;
|
||||
var @operator = GetOperator(body.NodeType);
|
||||
var left = Resolve(body.Left);
|
||||
var right = Resolve(body.Right);
|
||||
var result = string.Format("({0} {1} {2})", left, @operator, right);
|
||||
return result;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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":
|
||||
return methodCall.Object != null ? Like(methodCall) : 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 GetOperator(ExpressionType expressiontype)
|
||||
{
|
||||
return expressiontype switch
|
||||
{
|
||||
ExpressionType.And => "and",
|
||||
ExpressionType.AndAlso => "and",
|
||||
ExpressionType.Or => "or",
|
||||
ExpressionType.OrElse => "or",
|
||||
ExpressionType.Equal => "=",
|
||||
ExpressionType.NotEqual => "<>",
|
||||
ExpressionType.LessThan => "<",
|
||||
ExpressionType.LessThanOrEqual => "<=",
|
||||
ExpressionType.GreaterThan => ">",
|
||||
ExpressionType.GreaterThanOrEqual => ">=",
|
||||
_ => throw new Exception(string.Format("不支持{0}此种运算符查找!" + expressiontype))
|
||||
};
|
||||
}
|
||||
|
||||
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 fieldArray = argument1.Value.GetType().GetFields().First();
|
||||
var array = fieldArray.GetValue(argument1.Value) as object[];
|
||||
var setInPara = new List<string>();
|
||||
for (var i = 0; i < array.Length; i++)
|
||||
{
|
||||
var value = array[i].ToString();
|
||||
setInPara.Add(value);
|
||||
}
|
||||
var name = argument2.Member.Name;
|
||||
var @operator = Convert.ToBoolean(isTrue) ? "in" : " not in";
|
||||
var compName = string.Join(",", setInPara);
|
||||
var result = string.Format("{0} {1} ({2})", name, @operator, compName);
|
||||
return result;
|
||||
}
|
||||
|
||||
private static string Like(MethodCallExpression expression)
|
||||
{
|
||||
var temp = expression.Arguments[0];
|
||||
var lambda = Expression.Lambda(temp);
|
||||
var fn = lambda.Compile();
|
||||
var tempValue = Expression.Constant(fn.DynamicInvoke(null), temp.Type);
|
||||
var value = string.Format("%{0}%", tempValue);
|
||||
var name = (expression.Object as MemberExpression).Member.Name;
|
||||
var 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;
|
||||
var @operator = GetOperator(expressiontype);
|
||||
var result = string.Format("len({0}){1}{2}", name, @operator, value);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user