You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

663 lines
23 KiB
C#

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;
using Mapster;
namespace EntrustSettle.Common.Helper
{
#region 动态linq帮助类连接符号运算符号
/// <summary>
/// 动态linq工厂
/// </summary>
public static class DynamicLinqFactory
{
private static readonly Dictionary<string, OperationSymbol> _operatingSystems = new Dictionary<string, OperationSymbol>();
public static Dictionary<string, OperationSymbol> OperatingSystems => GetOperationSymbol();
private static readonly Dictionary<string, LinkSymbol> _linkSymbols = new Dictionary<string, LinkSymbol>();
public static Dictionary<string, LinkSymbol> LinkSymbols => GetLinkSymbol();
/// <summary>
/// 生成lambd表达式(如:CompanyID != 1 & CompanyID == 1)
/// </summary>
/// <typeparam name="TSource"></typeparam>
/// <param name="propertyStr"></param>
/// <returns></returns>
public static Expression<Func<TSource, bool>> CreateLambda<TSource>(string propertyStr)
{
// 设置自定义lanbd
// 定义 lanbd 种子p=> xxxxxx中的 p
if (string.IsNullOrWhiteSpace(propertyStr))
return LinqHelper.True<TSource>(); //为空就返回空的表达式
var parameter = Expression.Parameter(typeof(TSource), "p");
var strArr = SplitOperationSymbol(propertyStr);
// 第一个判断条件,固定一个判断条件作为最左边
Expression mainExpressin = ExpressionStudio(null, strArr[0], parameter);
// 将需要放置在最左边的判断条件从列表中去除,因为已经合成到表达式最左边了
strArr.RemoveAt(0);
foreach (var x in strArr)
{
mainExpressin = ExpressionStudio(mainExpressin, x, parameter);
}
return mainExpressin.ToLambda<Func<TSource, bool>>(parameter);
}
/// <summary>
/// 组合条件判断表达式
/// </summary>
/// <param name="left">左边的表达式</param>
/// <param name="dynamicLinq"></param>
/// <param name="key"></param>
/// <returns></returns>
public static Expression ExpressionStudio(Expression left, DynamicLinqHelper dynamicLinq, ParameterExpression key)
{
Expression mainExpression = key;
if (!dynamicLinq.Left.IsNullOrEmpty())
{
var properties = dynamicLinq.Left.Split('.');
int index = 0;
foreach (var t in properties)
{
if (mainExpression.Type.HasImplementedRawGeneric(typeof(IEnumerable<>)))
{
return ExpressionStudioEnumerable(left, mainExpression, dynamicLinq.Adapt<DynamicLinqHelper>(),
properties.Skip(index).ToArray());
}
mainExpression = mainExpression.Property(t);
index++;
}
}
Expression right = null;
if (dynamicLinq.IsMerge && dynamicLinq.Child.Any())
{
right = ExpressionStudio(null, dynamicLinq.Child[0], key);
for (var i = 1; i < dynamicLinq.Child.Count; i++)
{
right = ChangeLinkSymbol(dynamicLinq.Child[i].LinkSymbol, right, ExpressionStudio(null, dynamicLinq.Child[i], key));
}
}
else
{
right = ChangeOperationSymbol(dynamicLinq.OperationSymbol, mainExpression, dynamicLinq.Right);
}
left = left == null
// 如果左边表达式为空,则当前的表达式就为最左边
? right
// 如果不为空,则将当前的表达式连接到左边
: ChangeLinkSymbol(dynamicLinq.LinkSymbol, left, right);
return left;
}
public static Expression ExpressionStudioEnumerable(Expression left, Expression property, DynamicLinqHelper dynamicLinq, string[] properties)
{
var realType = property.Type.GenericTypeArguments[0];
var parameter = Expression.Parameter(realType, "z");
Expression mainExpression = property;
if (!properties.Any())
{
throw new ApplicationException("条件表达式错误,属性为集合时,需要明确具体属性");
}
dynamicLinq.Left = string.Join(".", properties);
mainExpression = ExpressionStudio(null, dynamicLinq, parameter);
var lambda = Expression.Lambda(mainExpression, parameter);
mainExpression = Expression.Call(typeof(Enumerable), "Any", new[] {realType}, property, lambda);
left = left == null
? mainExpression
: ChangeLinkSymbol(dynamicLinq.LinkSymbol, left, mainExpression);
return left;
}
public static List<DynamicLinqHelper> SplitOperationSymbol(string str)
{
var outList = new List<DynamicLinqHelper>();
var tokens = Regex.Matches(FormatString(str), _pattern, RegexOptions.Compiled)
.Select(m => m.Groups[1].Value.Trim())
.ToList();
SplitOperationSymbol(tokens, outList);
return outList;
}
private static void SplitOperationSymbol(List<string> tokens, List<DynamicLinqHelper> outList, int start = 0, int end = 0)
{
var dys = new Stack<DynamicLinqHelper>();
var dynamicLinqHelper = new DynamicLinqHelper();
if (end == 0)
{
end = tokens.Count - 1;
}
for (int i = start; i <= end; i++)
{
var token = tokens[i];
if (LinkSymbols.TryGetValue(token, out var symbol))
{
if (dys.Count > 0)
{
var linqHelper = dys.Peek();
linqHelper.Child.Add(dynamicLinqHelper);
}
else
{
outList.Add(dynamicLinqHelper);
}
dynamicLinqHelper = new DynamicLinqHelper()
{
LinkSymbol = symbol,
};
continue;
}
if (OperatingSystems.TryGetValue(token.ToLower(), out var system))
{
dynamicLinqHelper!.OperationSymbol = system;
continue;
}
if (dynamicLinqHelper!.OperationSymbol != OperationSymbol.In)
{
if (string.Equals(token.Trim(), "("))
{
dynamicLinqHelper!.IsMerge = true;
dynamicLinqHelper.Child = new List<DynamicLinqHelper>();
dys.Push(dynamicLinqHelper);
dynamicLinqHelper = new DynamicLinqHelper();
continue;
}
if (string.Equals(token.Trim(), ")"))
{
if (dys.Count > 1)
{
var dya = dys.Pop();
dya.Child.Add(dynamicLinqHelper);
dynamicLinqHelper = dya;
continue;
}
else
{
var dya = dys.Pop();
dya.Child.Add(dynamicLinqHelper);
outList.Add(dya);
dynamicLinqHelper = null;
continue;
}
}
}
if (dynamicLinqHelper!.OperationSymbol is null)
{
dynamicLinqHelper.Left += token;
}
else
{
dynamicLinqHelper.Right += FormatValue(token);
}
if (i == end)
{
outList.Add(dynamicLinqHelper);
dynamicLinqHelper = null;
}
}
}
public static string FormatValue(string str)
{
return str.TrimStart('"').TrimEnd('"');
// return str.TrimStart('"').TrimEnd('"').Replace(@"\""", @"""");
}
/// <summary>
/// 将运算枚举符号转成具体使用方法
/// </summary>
public static Expression ChangeLinkSymbol(LinkSymbol symbol, Expression left, Expression right)
{
switch (symbol)
{
case LinkSymbol.OrElse:
return left.OrElse(right);
case LinkSymbol.AndAlso:
return left.AndAlso(right);
default:
return left;
}
}
public static Dictionary<string, OperationSymbol> GetOperationSymbol()
{
if (_operatingSystems.Any()) return _operatingSystems;
var fielding = typeof(OperationSymbol).GetFields();
foreach (var item in fielding)
{
if (item.GetCustomAttribute(typeof(DisplayAttribute)) is DisplayAttribute attr && !attr.Name.IsNullOrEmpty())
{
foreach (var name in attr.Name.Split(';'))
{
_operatingSystems.Add(name.ToLower(), (OperationSymbol) item.GetValue(null));
}
}
}
return _operatingSystems;
}
public static Dictionary<string, LinkSymbol> GetLinkSymbol()
{
if (_linkSymbols.Any()) return _linkSymbols;
var fielding = typeof(LinkSymbol).GetFields();
foreach (var item in fielding)
{
if (item.GetCustomAttribute(typeof(DisplayAttribute)) is DisplayAttribute attr && !attr.Name.IsNullOrEmpty())
{
foreach (var name in attr.Name.Split(';'))
{
_linkSymbols.Add(name, (LinkSymbol) item.GetValue(null));
}
}
}
return _linkSymbols;
}
public static string FormatString(string str)
{
var sb = new StringBuilder();
var firstIndex = -1;
var lastIndex = -1;
for (var i = 0; i < str.Length; i++)
{
var character = str[i];
if (firstIndex == -1)
{
if (character.IsNullOrEmpty() && i < str.Length - 2)
if ('"'.Equals(str[i + 1]))
firstIndex = i + 1;
}
else
{
if ('\"'.Equals(character))
{
var andIndex = str.IndexOf("\" &", firstIndex);
var orIndex = str.IndexOf("\" |", firstIndex);
var andOrIndex = Math.Min(andIndex, orIndex);
andOrIndex = andOrIndex == -1 ? Math.Max(andOrIndex, orIndex) : andOrIndex;
if (andOrIndex != -1)
{
lastIndex = andOrIndex;
}
else
{
if (i == firstIndex) continue;
if (i == str.Length - 1 || str[i + 1].IsNullOrEmpty()) lastIndex = i;
}
}
if (lastIndex != -1)
{
var temp = str.Substring(firstIndex + 1, lastIndex - firstIndex - 1).Replace(@"""", @"\""");
sb.Append($" \"{temp}\" ");
i = lastIndex;
firstIndex = -1;
lastIndex = -1;
continue;
}
}
if (firstIndex != -1) continue;
sb.Append(character);
}
return sb.ToString();
}
/// <summary>tokenizer pattern: Optional-SpaceS...Token...Optional-Spaces</summary>
public static readonly string _pattern = @"\s*(" + string.Join("|", new string[]
{
// operators and punctuation that are longer than one char: longest first
string.Join("|", new[]
{
"||", "&&", "==", "!=", "<=", ">=",
"in",
"like", "contains", "%=",
"startslike", "StartsLike", "startscontains", "StartsContains", "%>",
"endlike", "EndLike", "endcontains", "EndContains", "%<",
}.Select(Regex.Escape)),
@"""(?:\\.|[^""])*""", // string
@"\d+(?:\.\d+)?", // number with optional decimal part
@"\w+", // word
@"\S", // other 1-char tokens (or eat up one character in case of an error)
}) + @")\s*";
/// <summary>
/// 将运算枚举符号转成具体使用方法
/// </summary>
public static Expression ChangeOperationSymbol(OperationSymbol? symbol, Expression key, object right)
{
// 将右边数据类型强行转换成左边一样的类型
// 两者如果Type不匹配则无法接下去的运算操作抛出异常
object newTypeRight;
if (right == null || string.IsNullOrEmpty(right.ToString()) || right.ToString() == "null")
{
newTypeRight = null;
}
else
{
if (symbol == OperationSymbol.In)
{
newTypeRight = right.ChangeTypeList(key.Type);
}
else
{
newTypeRight = right.ChangeType(key.Type);
}
}
// 根据当前枚举类别判断使用那种比较方法
switch (symbol)
{
case OperationSymbol.Equal:
return key.Equal(Expression.Constant(newTypeRight));
case OperationSymbol.GreaterThan:
{
if (key.Type == typeof(string))
return key.Contains(Expression.Constant(newTypeRight)); //对string 特殊处理 由于string
return key.GreaterThan(Expression.Constant((newTypeRight)));
}
case OperationSymbol.GreaterThanOrEqual:
{
if (key.Type == typeof(string))
return key.Contains(Expression.Constant(newTypeRight, typeof(string)));
return key.GreaterThanOrEqual(Expression.Constant(newTypeRight));
}
case OperationSymbol.LessThan:
{
if (key.Type == typeof(string))
return key.Contains(Expression.Constant(newTypeRight, typeof(string)));
return key.LessThan(Expression.Constant((newTypeRight)));
}
case OperationSymbol.LessThanOrEqual:
{
if (key.Type == typeof(string))
return key.Contains(Expression.Constant(newTypeRight, typeof(string)));
return key.LessThanOrEqual(Expression.Constant((newTypeRight)));
}
case OperationSymbol.NotEqual:
return key.NotEqual(Expression.Constant(newTypeRight));
case OperationSymbol.Contains:
return key.Contains(Expression.Constant(newTypeRight));
case OperationSymbol.StartsContains:
return key.StartContains(Expression.Constant(newTypeRight));
case OperationSymbol.EndContains:
return key.EndContains(Expression.Constant(newTypeRight));
case OperationSymbol.In:
return Expression.Constant(newTypeRight).Contains(key);
default:
throw new ArgumentException("OperationSymbol IS NULL");
}
}
}
/// <summary>
/// 动态linq帮助类
/// </summary>
public class DynamicLinqHelper
{
[Display(Name = "左")]
public string Left { get; set; }
[Display(Name = "右")]
public string Right { get; set; }
[Display(Name = "运算符")]
public OperationSymbol? OperationSymbol { get; set; }
[Display(Name = "连接符")]
public LinkSymbol LinkSymbol { get; set; }
/// <summary>
/// 是否是合并 用于括号
/// </summary>
public bool IsMerge { get; set; } = false;
/// <summary>
/// 再有括号时候使用
/// </summary>
public List<DynamicLinqHelper> Child { get; set; }
}
/// <summary>
/// 连接符枚举(将来可能会包含 括号
/// </summary>
public enum LinkSymbol
{
[Display(Name = "&&;&")]
AndAlso,
[Display(Name = "||;|")]
OrElse,
Empty
}
/// <summary>
/// 常用比较运算符 > , >= , == , < , <= , != ,Contains
/// </summary>
public enum OperationSymbol
{
[Display(Name = "in")]
In,
[Display(Name = "like;contains;%=")]
Contains,
[Display(Name = "StartsLike;StartsContains;%>")]
StartsContains,
[Display(Name = "EndLike;EndContains;%<")]
EndContains,
[Display(Name = ">")]
GreaterThan,
[Display(Name = ">=")]
GreaterThanOrEqual,
[Display(Name = "<")]
LessThan,
[Display(Name = "<=")]
LessThanOrEqual,
[Display(Name = "==;=")]
Equal,
[Display(Name = "!=")]
NotEqual
}
#endregion
/// <summary>
/// Queryable扩展
/// </summary>
public static class QueryableExtensions
{
#region 自定义扩展Queryable
/// <summary>
/// Where扩展
/// </summary>
public static IEnumerable<TSource> IWhere<TSource>(this IEnumerable<TSource> source, string linqStr)
{
return source.Where(DynamicLinqFactory.CreateLambda<TSource>(linqStr).Compile());
}
/// <summary>
/// FirstOrDefault扩展
/// </summary>
public static TSource IFirstOrDefault<TSource>(this IEnumerable<TSource> source, string linqStr)
{
return source.FirstOrDefault(DynamicLinqFactory.CreateLambda<TSource>(linqStr).Compile());
}
/// <summary>
/// Count扩展
/// </summary>
public static Int32 ICount<TSource>(this IEnumerable<TSource> source, string linqStr)
{
return source.Count(DynamicLinqFactory.CreateLambda<TSource>(linqStr).Compile());
}
/// <summary>
/// 自定义排序
/// </summary>
public static IOrderedQueryable<TSource> ISort<TSource>(this IQueryable<TSource> source, string orderByProperty, bool asc)
{
string command = asc ? "OrderBy" : "OrderByDescending";
var type = typeof(TSource);
var property = type.GetProperty(orderByProperty);
var parameter = Expression.Parameter(type, "p");
var propertyAccess = Expression.MakeMemberAccess(parameter, property);
var orderByExpression = Expression.Lambda(propertyAccess, parameter);
var resultExpression = Expression.Call(typeof(Queryable), command, new Type[] {type, property.PropertyType}, source.Expression,
Expression.Quote(orderByExpression));
return (IOrderedQueryable<TSource>) source.Provider.CreateQuery<TSource>(resultExpression);
}
/// <summary>
/// 自定义分页
/// </summary>
/// <typeparam name="TSource"></typeparam>
/// <param name="source"></param>
/// <param name="nowPage"></param>
/// <param name="pageSize"></param>
/// <returns></returns>
public static IQueryable<TSource> IPaging<TSource>(this IQueryable<TSource> source, int nowPage, int pageSize)
{
return source.ISkip((nowPage - 1) * pageSize).ITake(pageSize);
}
/// <summary>
/// 自定义Skip
/// </summary>
public static IQueryable<TSource> ISkip<TSource>(this IQueryable<TSource> source, int count)
{
return source.Provider.CreateQuery<TSource>(Expression.Call(
// 类别
typeof(Queryable),
// 调用的方法
"Skip",
// 元素类别
new Type[] {source.ElementType},
// 调用的表达树
source.Expression,
// 参数
Expression.Constant(count)));
}
/// <summary>
/// 自定义Take
/// </summary>
public static IQueryable<TSource> ITake<TSource>(this IQueryable<TSource> source, int count)
{
return source.Provider.CreateQuery<TSource>(Expression.Call(
// 类别
typeof(Queryable),
// 调用的方法
"Take",
// 元素类别
new Type[] {source.ElementType},
// 调用的表达树
source.Expression,
// 参数
Expression.Constant(count)));
}
/// <summary>
/// 自定义去重复
/// </summary>
public static IEnumerable<TSource> IDistinctBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector)
{
var seenKeys = new HashSet<TKey>();
return source.Where(element => seenKeys.Add(keySelector(element)));
}
/// <summary>
/// 动态赋值
/// </summary>
public static void CopyTo<T>(this object source, T target) where T : class, new()
{
if (source == null)
return;
if (target == null)
{
target = new T();
}
foreach (var property in target.GetType().GetProperties())
{
// 这里可以判断一下当前属性值是否为空的 source.GetType().GetProperty(property.Name).GetValue(source, null)
target.GetType().InvokeMember(property.Name, BindingFlags.SetProperty, null, target,
new object[] {source.GetType().GetProperty(property.Name).GetValue(source, null)});
}
}
/// <summary>
/// 移除特殊字段数据
/// </summary>
public static void RemoveSpecialPropertyValue(this object source)
{
var properties = source.GetType().GetProperties();
foreach (var x in properties)
{
if (x.GetAccessors().Any(y => y.IsVirtual))
{
source.GetType().GetProperty(x.Name).SetValue(source, null, null);
}
}
}
#endregion
}
}