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#

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

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
}
}