using Common.Const; using System; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; using System.Reflection; namespace Common.Extensions.Lambda { public static class LambdaExtensions { /// /// 分页查询 /// /// /// /// /// /// public static IQueryable TakePage(this IQueryable queryable, int page, int size = 15) { return queryable.TakeOrderByPage(page, size); } /// /// 分页查询 /// /// /// /// /// /// /// public static IQueryable TakeOrderByPage(this IQueryable queryable, int page, int size = 15, Expression>> orderBy = null) { if (page <= 0) { page = 1; } return Queryable.Take(Queryable.Skip(queryable.GetIQueryableOrderBy(orderBy.GetExpressionToDic()), (page - 1) * size), 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) /// public static Expression> CreateExpression(this string propertyName, object propertyValue, LinqExpressionType expressionType) { return propertyName.CreateExpression(propertyValue, null, expressionType); } /// /// /// /// /// 字段名 /// 表达式的值 /// 创建表达式的类型,如:p=>p.propertyName != propertyValue /// p=>p.propertyName.Contains(propertyValue) /// private static Expression> CreateExpression( this string propertyName, object propertyValue, ParameterExpression parameter, LinqExpressionType expressionType) { Type proType = typeof(T).GetProperty(propertyName).PropertyType; //创建节点变量如p=>的节点p // parameter ??= Expression.Parameter(typeof(T), "p");//创建参数p parameter = parameter ?? Expression.Parameter(typeof(T), "p"); //创建节点的属性p=>p.name 属性name MemberExpression memberProperty = Expression.PropertyOrField(parameter, propertyName); if (expressionType == LinqExpressionType.In) { if (!(propertyValue is System.Collections.IList list) || list.Count == 0) throw new Exception("属性值类型不正确"); bool isStringValue = true; List objList = new List(); if (proType.ToString() != "System.String") { isStringValue = false; foreach (var value in list) { objList.Add(value.ToString().ChangeType(proType)); } list = objList; } if (isStringValue) { //string 类型的字段,如果值带有'单引号,EF会默认变成''两个单引号 MethodInfo method = typeof(System.Collections.IList).GetMethod("Contains"); //创建集合常量并设置为常量的值 ConstantExpression constantCollection = Expression.Constant(list); //创建一个表示调用带参数的方法的:new string[]{"1","a"}.Contains("a"); MethodCallExpression methodCall = Expression.Call(constantCollection, method, memberProperty); return Expression.Lambda>(methodCall, parameter); } //非string字段,按上面方式处理报异常Null TypeMapping in Sql Tree BinaryExpression body = null; foreach (var value in list) { ConstantExpression constantExpression = Expression.Constant(value); UnaryExpression unaryExpression = Expression.Convert(memberProperty, constantExpression.Type); body = body == null ? Expression.Equal(unaryExpression, constantExpression) : Expression.OrElse(body, Expression.Equal(unaryExpression, constantExpression)); } return Expression.Lambda>(body, parameter); } // object value = propertyValue; ConstantExpression constant = proType.ToString() == "System.String" ? Expression.Constant(propertyValue) : Expression.Constant(propertyValue.ToString().ChangeType(proType)); UnaryExpression member = Expression.Convert(memberProperty, constant.Type); Expression> expression; 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.Contains: case LinqExpressionType.NotContains: MethodInfo method = typeof(string).GetMethod("Contains", new[] { typeof(string) }); constant = Expression.Constant(propertyValue, typeof(string)); if (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; default: // p => p.false expression = False(); break; } 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).Value)); } } /// /// 表达式转换成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) { return expression.GetExpressionToPair().Reverse().ToList().ToDictionary(x => x.Key, x => x.Value); } /// /// 解析多字段排序 /// /// /// /// string=排序的字段,bool=true降序/false升序 /// public static IQueryable GetIQueryableOrderBy(this IQueryable queryable, Dictionary orderBySelector) { string[] orderByKeys = orderBySelector.Select(x => x.Key).ToArray(); if (orderByKeys == null || orderByKeys.Length == 0) return queryable; IOrderedQueryable queryableOrderBy = null; // string orderByKey = orderByKeys[^1]; string orderByKey = orderByKeys[orderByKeys.Length - 1]; queryableOrderBy = orderBySelector[orderByKey] == QueryOrderBy.Desc ? queryableOrderBy = queryable.OrderByDescending(orderByKey.GetExpression()) : queryable.OrderBy(orderByKey.GetExpression()); for (int i = orderByKeys.Length - 2; i >= 0; i--) { queryableOrderBy = orderBySelector[orderByKeys[i]] == QueryOrderBy.Desc ? queryableOrderBy.ThenByDescending(orderByKeys[i].GetExpression()) : queryableOrderBy.ThenBy(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.And); } /// /// 同上面and用法相同 /// /// /// /// public static Expression> Or(this List listExpress) { return listExpress.Compose(Expression.Or); } 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.And); } public static Expression> Or(this Expression> first, Expression> second) { return first.Compose(second, Expression.Or); } 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 IQueryable GetQueryableSelect(this IQueryable 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 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); } } }