using SqlSugar; using System; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; using System.Reflection; using VolPro.Core.BaseProvider; using VolPro.Core.Const; using VolPro.Core.Enums; namespace VolPro.Core.Extensions { public static class LambdaExtensions { /// /// 分页查询 /// /// /// /// /// /// public static ISugarQueryable TakePage(this ISugarQueryable queryable, int page, int size = 15) { return queryable.TakeOrderByPage(page, size); } /// /// 分页查询 /// /// /// /// /// /// /// public static ISugarQueryable TakeOrderByPage(this ISugarQueryable queryable, int page, int size = 15, Expression>> orderBy = null) { if (page <= 0) { page = 1; } return queryable.GetISugarQueryableOrderBy(orderBy.GetExpressionToDic()) .Skip((page - 1) * size) .Take(size); } public static IEnumerable TakePage(this List list, int page, int size = 15) { if (page <= 0) { page = 1; } return list.Skip((page - 1) * size).Take(size); } /// /// 创建lambda表达式:p=>true /// /// /// public static Expression> True() { return p => true; } /// /// 创建lambda表达式:p=>false /// /// /// public static Expression> False() { return p => false; } public static ParameterExpression GetExpressionParameter(this Type type) { return Expression.Parameter(type, "p"); } /// /// 创建lambda表达式:p=>p.propertyName /// /// /// /// /// public static Expression> GetExpression(this string propertyName) { return propertyName.GetExpression(typeof(T).GetExpressionParameter()); } /// /// 创建委托有返回值的表达式:p=>p.propertyName /// /// /// /// /// public static Func GetFun(this string propertyName) { return propertyName.GetExpression(typeof(T).GetExpressionParameter()).Compile(); } /// /// 创建lambda表达式:p=>false /// 在已知TKey字段类型时,如动态排序OrderBy(x=>x.ID)会用到此功能,返回的就是x=>x.ID /// Expression> expression = x => x.CreateDate;指定了类型 /// /// /// public static Expression> GetExpression(this string propertyName, ParameterExpression parameter) { if (typeof(TKey).Name == "Object") return Expression.Lambda>(Expression.Convert(Expression.Property(parameter, propertyName), typeof(object)), parameter); return Expression.Lambda>(Expression.Property(parameter, propertyName), parameter); } /// /// 创建lambda表达式:p=>false /// object不能确认字段类型(datetime,int,string),如动态排序OrderBy(x=>x.ID)会用到此功能,返回的就是x=>x.ID /// Expression> expression = x => x.CreateDate;任意类型的字段 /// /// /// public static Expression> GetExpression(this string propertyName) { return propertyName.GetExpression(typeof(T).GetExpressionParameter()); } public static Expression> GetExpression(this string propertyName, ParameterExpression parameter) { return Expression.Lambda>(Expression.Convert(Expression.Property(parameter, propertyName), typeof(object)), parameter); } /// /// /// /// /// 字段名 /// 表达式的值 /// 创建表达式的类型,如:p=>p.propertyName != propertyValue /// p=>p.propertyName.Contains(propertyValue) /// /// /// /// /// /// 字段名 /// 表达式的值 /// 创建表达式的类型,如:p=>p.propertyName != propertyValue /// p=>p.propertyName.Contains(propertyValue) /// public static Expression> CreateExpression(this string propertyName, object propertyValue, LinqExpressionType expressionType, bool checkNullProperty = false) { return propertyName.CreateExpression(propertyValue, null, expressionType, checkNullProperty); } private static Expression> GetContainsExpression(string propertyName, object propertyValue, ParameterExpression parameter, LinqExpressionType expressionType) { Type proType = typeof(T).GetProperty(propertyName).PropertyType; var list = propertyValue as System.Collections.IList; List arr = new List(); foreach (var value in list) { if (value == null || value.ToString().Trim() == "") { continue; } var valRes = (proType == typeof(string) ? value.ToString() : value).ChangeType(proType); if (valRes != null) { arr.Add((FieldType)valRes); } } //string 类型的字段,如果值带有'单引号,EF会默认变成''两个单引号 var method = arr.GetType().GetMethod("Contains"); ConstantExpression constantCollection = Expression.Constant(arr); MemberExpression memberProperty = Expression.PropertyOrField(parameter, propertyName); if (expressionType == LinqExpressionType.NotIn) { return Expression.Lambda>(Expression.Not(Expression.Call(constantCollection, method, memberProperty)), parameter); } else { MethodCallExpression methodCall = Expression.Call(constantCollection, method, memberProperty); return Expression.Lambda>(methodCall, parameter); } } public static bool CheckFilterNullExpression(this LinqExpressionType filterType) { switch (filterType) { case LinqExpressionType.Empty: case LinqExpressionType.NotEmpty: case LinqExpressionType.Null: case LinqExpressionType.NotNull: case LinqExpressionType.NullOrEmpty: case LinqExpressionType.NotNullOrEmpty: return true; } return false; } // 核心:创建与目标类型兼容的null常量表达式 private static ConstantExpression CreateNullConstant(Type targetType) { if (!targetType.IsValueType) { return Expression.Constant(null, targetType); } if (targetType.IsGenericType && targetType.GetGenericTypeDefinition() == typeof(Nullable<>)) { // 可空类型的null常量需用默认值(底层类型为null) return Expression.Constant(null, targetType); } throw new InvalidOperationException($"非可空值类型 {targetType} 不支持null常量"); } /// /// 根据条件类型生成相应的查询表达式 /// /// 实体类型 /// 属性名称 /// 条件类型 /// 生成的查询表达式 public static Expression> GenerateEmptyCondition( string propertyName, LinqExpressionType conditionType) { // 获取属性信息并验证 PropertyInfo propertyInfo = typeof(T).GetProperty(propertyName); Type propertyType = propertyInfo.PropertyType; // 检查字符串专属条件是否应用在字符串属性上 bool isStringCondition = conditionType == LinqExpressionType.Empty || conditionType == LinqExpressionType.NotEmpty || conditionType == LinqExpressionType.NotNullOrEmpty; if (isStringCondition && propertyType != typeof(string)) { throw new InvalidOperationException( $"条件类型 {conditionType} 仅适用于字符串类型的属性,不能用于 {propertyType} 类型"); } // 创建参数表达式 (x => ... 中的 x) ParameterExpression parameter = Expression.Parameter(typeof(T), "x"); // 创建属性访问表达式 (x.PropertyName) MemberExpression property = Expression.Property(parameter, propertyInfo); Expression conditionExpression; switch (conditionType) { case LinqExpressionType.Empty: // x.PropertyName == "" (仅适用于字符串) conditionExpression = Expression.Equal( property, Expression.Constant("", typeof(string)) ); break; case LinqExpressionType.Null: // x.PropertyName == null if (typeof(string) == propertyType) { conditionExpression = Expression.Equal( property, Expression.Constant(null, propertyType.IsValueType ? typeof(object) : propertyType) ); } else { conditionExpression = Expression.Equal(property, CreateNullConstant(propertyType)); } break; case LinqExpressionType.NotEmpty: case LinqExpressionType.NotNullOrEmpty: // x.PropertyName != "" (仅适用于字符串) var notNull = Expression.NotEqual(property, Expression.Constant(null, typeof(string))); var notEmpty = Expression.NotEqual(property, Expression.Constant("", typeof(string))); conditionExpression = Expression.AndAlso(notNull, notEmpty); break; case LinqExpressionType.NotNull: // x.PropertyName != null if (typeof(string) == propertyType) { conditionExpression = Expression.NotEqual( property, Expression.Constant(null, propertyType.IsValueType ? typeof(object) : propertyType) ); } else { conditionExpression = Expression.NotEqual(property, CreateNullConstant(propertyType)); } break; case LinqExpressionType.NullOrEmpty: // !string.IsNullOrEmpty(x.PropertyName) (仅适用于字符串) var isNull = Expression.Equal(property, Expression.Constant(null, typeof(string))); var isEmpty = Expression.Equal(property, Expression.Constant("", typeof(string))); conditionExpression = Expression.OrElse(isNull, isEmpty); break; default: throw new ArgumentOutOfRangeException( nameof(conditionType), "不支持的条件类型" ); } // 创建并返回lambda表达式 return Expression.Lambda>(conditionExpression, parameter); } /// /// /// /// /// 字段名 /// 表达式的值 /// 创建表达式的类型,如:p=>p.propertyName != propertyValue /// p=>p.propertyName.Contains(propertyValue) /// private static Expression> CreateExpression( this string propertyName, object propertyValue, ParameterExpression parameter, LinqExpressionType expressionType, bool checkNullProperty = false) { Type proType = typeof(T).GetProperty(propertyName).PropertyType; //空或者null查询 if (expressionType.CheckFilterNullExpression()) { return GenerateEmptyCondition(propertyName, expressionType); } if (checkNullProperty) { if (propertyValue == null) { return True(); } if (!(proType == typeof(string) || proType == typeof(int?) || proType == typeof(long?) || proType == typeof(decimal?) || proType == typeof(float?) || proType == typeof(bool?) || proType == typeof(DateTime?))) { checkNullProperty = false; } } //创建节点变量如p=>的节点p // parameter ??= Expression.Parameter(typeof(T), "p");//创建参数p parameter = parameter ?? Expression.Parameter(typeof(T), "p"); Expression> expression = null; //创建节点的属性p=>p.name 属性name MemberExpression memberProperty = Expression.PropertyOrField(parameter, propertyName); if (expressionType == LinqExpressionType.In || expressionType == LinqExpressionType.NotIn) { if (!(propertyValue is System.Collections.IList list) || list.Count == 0) return x => false; var res = typeof(LambdaExtensions).GetMethod("GetContainsExpression", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Static) .MakeGenericMethod(new Type[] { typeof(T), proType }) .Invoke(null, new object[] { propertyName, propertyValue, parameter, expressionType }) as Expression>; return res; } // object value = propertyValue; ConstantExpression constant = proType == typeof(string) ? Expression.Constant(propertyValue) : Expression.Constant(propertyValue.ToString().ChangeType(proType)); // DateTime只选择了日期的时候自动在结束日期加一天,修复DateTime类型使用日期区间查询无法查询到结束日期的问题 if ((proType == typeof(DateTime) || proType == typeof(DateTime?)) && expressionType == LinqExpressionType.LessThanOrEqual && propertyValue.ToString().Length == 10) { expressionType = LinqExpressionType.LessThan; constant = Expression.Constant(Convert.ToDateTime(propertyValue.ToString()).AddDays(1)); } if (checkNullProperty) { expression = CreateHasValueCondition(proType, parameter, propertyName, propertyValue, constant, expressionType); if (expression != null) { return expression; } } UnaryExpression member = Expression.Convert(memberProperty, constant.Type); switch (expressionType) { ////p=>p.propertyName == propertyValue //case LinqExpressionType.Equal: // expression = Expression.Lambda>(Expression.Equal(member, constant), parameter); // break; //p=>p.propertyName != propertyValue case LinqExpressionType.NotEqual: expression = Expression.Lambda>(Expression.NotEqual(member, constant), parameter); break; // p => p.propertyName > propertyValue case LinqExpressionType.GreaterThan: expression = Expression.Lambda>(Expression.GreaterThan(member, constant), parameter); break; // p => p.propertyName < propertyValue case LinqExpressionType.LessThan: expression = Expression.Lambda>(Expression.LessThan(member, constant), parameter); break; // p => p.propertyName >= propertyValue case LinqExpressionType.ThanOrEqual: expression = Expression.Lambda>(Expression.GreaterThanOrEqual(member, constant), parameter); break; // p => p.propertyName <= propertyValue case LinqExpressionType.LessThanOrEqual: expression = Expression.Lambda>(Expression.LessThanOrEqual(member, constant), parameter); break; // p => p.propertyName.Contains(propertyValue) // p => !p.propertyName.Contains(propertyValue) case LinqExpressionType.Like: case LinqExpressionType.NotLike: case LinqExpressionType.Contains: case LinqExpressionType.NotContains: MethodInfo method = typeof(string).GetMethod("Contains", new[] { typeof(string) }); constant = Expression.Constant(propertyValue, typeof(string)); if (expressionType == LinqExpressionType.Like || expressionType == LinqExpressionType.Contains) { expression = Expression.Lambda>(Expression.Call(member, method, constant), parameter); } else { expression = Expression.Lambda>(Expression.Not(Expression.Call(member, method, constant)), parameter); } break; case LinqExpressionType.LikeStart: case LinqExpressionType.LikeEnd: string m = expressionType == LinqExpressionType.LikeStart ? "StartsWith" : "EndsWith"; var startsWithMethod = typeof(string).GetMethod(m, new[] { typeof(string) }); var searchTermConstant = Expression.Constant(propertyValue, typeof(string)); var startsWithCall = Expression.Call(member, startsWithMethod, searchTermConstant); expression = Expression.Lambda>(startsWithCall, parameter); break; default: expression = Expression.Lambda>(Expression.Equal(member, constant), parameter); break; // break; } return expression; } private static Expression> CreateHasValueCondition( Type proType, ParameterExpression parameter, string propertyName, object propertyValue, ConstantExpression constant, LinqExpressionType expressionType ) { BinaryExpression condition = null; var property = Expression.Property(parameter, propertyName); if (proType == typeof(string)) { Expression binaryExpression = null; switch (expressionType) { case LinqExpressionType.Like: case LinqExpressionType.Contains: binaryExpression = Expression.Call(property, "Contains", null, constant); break; case LinqExpressionType.LikeStart: binaryExpression = Expression.Call(property, "StartsWith", null, constant); break; case LinqExpressionType.LikeEnd: binaryExpression = Expression.Call(property, "EndsWith", null, constant); break; case LinqExpressionType.NotLike: case LinqExpressionType.NotContains: binaryExpression = Expression.Not(Expression.Call(property, "Contains", null, constant)); break; case LinqExpressionType.NotEqual: binaryExpression = Expression.Not(Expression.Equal(property, constant)); break; default: binaryExpression = Expression.Equal(property, constant); break; } var stringCondition = Expression.Condition( Expression.Equal(property, Expression.Constant(null)), // 如果 Charlie 为 null Expression.Constant(false), // 返回 false binaryExpression // 否则比较值 ); var stringExpression = Expression.Lambda>(stringCondition, parameter); return stringExpression; } var hasValue = Expression.Property(property, "HasValue"); // 判断 字段 是否有值 var value = Expression.Property(property, "Value"); // 获取 字段 的值 switch (expressionType) { case LinqExpressionType.NotEqual: condition = Expression.NotEqual(value, constant); break; // p => p.propertyName > propertyValue case LinqExpressionType.GreaterThan: condition = Expression.GreaterThan(value, constant); break; // p => p.propertyName < propertyValue case LinqExpressionType.LessThan: condition = Expression.LessThan(value, constant); break; case LinqExpressionType.LessThanOrEqual: condition = Expression.LessThanOrEqual(value, constant); break; // p => p.propertyName >= propertyValue case LinqExpressionType.ThanOrEqual: condition = Expression.GreaterThanOrEqual(value, constant); break; case LinqExpressionType.Equal: condition = Expression.Equal(value, constant); // 构建条件:xx== break; } if (condition == null) { return null; } var body = Expression.Condition(hasValue, condition, Expression.Constant(false)); // 构建条件体 var expression = Expression.Lambda>(body, parameter); return expression; } /// /// 表达式转换成KeyValList(主要用于多字段排序,并且多个字段的排序规则不一样) /// 如有多个字段进行排序,参数格式为 /// Expression>> orderBy = x => new Dictionary() { /// { x.ID, true }, /// { x.DestWarehouseName, true } /// }; /// 返回的是new Dictionary(){{}}key为排序字段,bool为升降序 /// /// /// /// public static IEnumerable> GetExpressionToPair(this Expression>> expression) { foreach (var exp in ((ListInitExpression)expression.Body).Initializers) { yield return new KeyValuePair( exp.Arguments[0] is MemberExpression ? (exp.Arguments[0] as MemberExpression).Member.Name.ToString() : ((exp.Arguments[0] as UnaryExpression).Operand as MemberExpression).Member.Name, (QueryOrderBy)( exp.Arguments[1] as ConstantExpression != null ? (exp.Arguments[1] as ConstantExpression).Value //2021.07.04增加自定排序按条件表达式 : Expression.Lambda>(exp.Arguments[1] as Expression).Compile()() )); } } /// /// 表达式转换成KeyValList(主要用于多字段排序,并且多个字段的排序规则不一样) /// 如有多个字段进行排序,参数格式为 /// Expression>> orderBy = x => new Dictionary() { /// { x.ID, QueryOrderBy.Desc }, /// { x.DestWarehouseName, QueryOrderBy.Asc } /// }; /// 返回的是new Dictionary(){{}}key为排序字段,QueryOrderBy为排序方式 /// /// /// /// public static Dictionary GetExpressionToDic(this Expression>> expression) { //2020.09.14增加排序字段null值判断 if (expression == null) { return new Dictionary(); } return expression.GetExpressionToPair().ToList().ToDictionary(x => x.Key, x => x.Value); } /// /// 解析多字段排序 /// /// /// /// string=排序的字段,bool=true降序/false升序 /// public static ISugarQueryable GetISugarQueryableOrderBy(this ISugarQueryable queryable, Dictionary orderBySelector) { string[] orderByKeys = orderBySelector.Select(x => x.Key).ToArray(); if (orderByKeys == null || orderByKeys.Length == 0) return queryable; ISugarQueryable queryableOrderBy = null; // string orderByKey = orderByKeys[^1]; string orderByKey = orderByKeys[0]; queryableOrderBy = orderBySelector[orderByKey] == QueryOrderBy.Desc ? queryableOrderBy = queryable.OrderByDescending(orderByKey.GetExpression()) : queryable.OrderBy(orderByKey.GetExpression()); for (int i = 1; i < orderByKeys.Length; i++) { queryableOrderBy = orderBySelector[orderByKeys[i]] == QueryOrderBy.Desc ? queryableOrderBy.OrderByDescending(orderByKeys[i].GetExpression()) : queryableOrderBy.OrderBy(orderByKeys[i].GetExpression()); } return queryableOrderBy; } /// /// 获取对象表达式指定属性的值 /// 如获取:Out_Scheduling对象的ID或基他字段 /// /// /// 格式 Expression>sch=x=>new {x.v1,x.v2} or x=>x.v1 解析里面的值返回为数组 /// public static string[] GetExpressionToArray(this Expression> expression) { string[] propertyNames = null; if (expression.Body is MemberExpression) { propertyNames = new string[] { ((MemberExpression)expression.Body).Member.Name }; } else { propertyNames = expression.GetExpressionProperty().Distinct().ToArray(); } return propertyNames; } /// /// 与下面and生成方式有所不同,如果直接用表达式1.2进行合并产会提示数据源不同的异常,只能使用下面的的and合并 /// 此种合并是在使用的同一个数据源(变量),生成的sql语句同样有性能问题(本身可以索引扫描的,生成的sql语句的case when变成索引查找) /// /// 通过字段动态生成where and /or表达 /// 如:有多个where条件,当条件成立时where 1=1 and/or 2=2,依次往后拼接 /// /// /// /// ExpressionParameters /// 1、Field生成的字段 /// 2、ExpressionType 表达式类型大于、小于、于大=、小于=、contains /// 3、Value表达式的值 /// /// public static Expression> And(List listExpress) { return listExpress.Compose(Expression.AndAlso); } /// /// 同上面and用法相同 /// /// /// /// public static Expression> Or(this List listExpress) { return listExpress.Compose(Expression.OrElse); } private static Expression> Compose(this List listExpress, Func merge) { ParameterExpression parameter = Expression.Parameter(typeof(T), "p"); Expression> expression = null; foreach (ExpressionParameters exp in listExpress) { if (expression == null) { expression = exp.Field.GetExpression(parameter); } else { expression = expression.Compose(exp.Field.GetExpression(parameter), merge); } } return expression; } /// /// https://blogs.msdn.microsoft.com/meek/2008/05/02/linq-to-entities-combining-predicates/ /// 表达式合并(合并生产的sql语句有性能问题) /// 合并两个where条件,如:多个查询条件时,判断条件成立才where /// /// /// /// /// public static Expression> And(this Expression> first, Expression> second) { return first.Compose(second, Expression.AndAlso); } public static Expression> Or(this Expression> first, Expression> second) { return first.Compose(second, Expression.OrElse); } public static Expression Compose(this Expression first, Expression second, Func merge) { // build parameter map (from parameters of second to parameters of first) var map = first.Parameters.Select((f, i) => new { f, s = second.Parameters[i] }).ToDictionary(p => p.s, p => p.f); // replace parameters in the second lambda expression with parameters from the first var secondBody = ParameterRebinder.ReplaceParameters(map, second.Body); // apply composition of lambda expression bodies to parameters from the first expression return Expression.Lambda(merge(first.Body, secondBody), first.Parameters); } public static ISugarQueryable GetQueryableSelect(this ISugarQueryable queryable) { Expression> expression = CreateMemberInitExpression(); return queryable.Select(expression); } /// /// 动态创建表达式Expression> expression = CreateMemberInitExpression(); ///结果为Expression> expression1 = x => new User() { Age = x.Age, Species = x.Species }; ///参照文档https://docs.microsoft.com/zh-cn/dotnet/api/system.linq.expressions.memberinitexpression?redirectedfrom=MSDN&view=netframework-4.8 /// /// /// /// public static Expression> CreateMemberInitExpression(Type resultType = null) { resultType = resultType ?? typeof(Result); ParameterExpression left = Expression.Parameter(typeof(Source), "p"); NewExpression newExpression = Expression.New(resultType); PropertyInfo[] propertyInfos = resultType.GetProperties(); List memberBindings = new List(); foreach (PropertyInfo propertyInfo in propertyInfos) { MemberExpression member = Expression.Property(left, propertyInfo.Name); MemberBinding speciesMemberBinding = Expression.Bind(resultType.GetMember(propertyInfo.Name)[0], member); memberBindings.Add(speciesMemberBinding); } MemberInitExpression memberInitExpression = Expression.MemberInit(newExpression, memberBindings); Expression> expression = Expression.Lambda>(memberInitExpression, new ParameterExpression[] { left }); return expression; } public static Expression> CreateMemberInitExpression(Type resultType) { return CreateMemberInitExpression(resultType); } /// /// 属性判断待完 /// /// /// public static IEnumerable GetGenericProperties(this Type type) { return type.GetProperties().GetGenericProperties(); } /// /// 属性判断待完 /// /// /// public static IEnumerable GetGenericProperties(this IEnumerable properties) { return properties.Where(x => !x.PropertyType.IsGenericType && x.PropertyType.GetInterface("IList") == null || x.PropertyType.GetInterface("IEnumerable", false) == null); } public static string GetDbCondition(this string stringType) { string reslut = null; switch (stringType?.ToLower()) { case HtmlElementType.droplist: case HtmlElementType.selectlist: case HtmlElementType.textarea: case HtmlElementType.checkbox: reslut = HtmlElementType.Contains; break; case HtmlElementType.thanorequal: reslut = HtmlElementType.ThanOrEqual; break; case HtmlElementType.lessorequal: reslut = HtmlElementType.LessOrequal; break; case HtmlElementType.gt: reslut = HtmlElementType.GT; break; case HtmlElementType.lt: reslut = HtmlElementType.lt; break; case HtmlElementType.like: reslut = HtmlElementType.like; break; case HtmlElementType.NotEqual: reslut = HtmlElementType.NotEqual; break; default: reslut = HtmlElementType.Equal; break; } return reslut; } } public class ExpressionParameters { public string Field { get; set; } public LinqExpressionType ExpressionType { get; set; } public object Value { get; set; } // public } public class ParameterRebinder : ExpressionVisitor { private readonly Dictionary map; public ParameterRebinder(Dictionary map) { this.map = map ?? new Dictionary(); } public static Expression ReplaceParameters(Dictionary map, Expression exp) { return new ParameterRebinder(map).Visit(exp); } protected override Expression VisitParameter(ParameterExpression p) { ParameterExpression replacement; if (map.TryGetValue(p, out replacement)) { p = replacement; } return base.VisitParameter(p); } } }