NET Lambda表达式工具类

帮助类

using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using xFramework.Enums;
using xFramework.Extends;

namespace Commx.Utilx
{
    /// <summary>
    /// 表达式工具类
    /// </summary>
    public static class ExpressionUtil
    {
        /// <summary>
        /// 把 查询对象 转换成 拉姆达表达式
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <typeparam name="TSearch"></typeparam>
        /// <param name="searchData"></param>
        /// <returns></returns>
        public static Func<T, bool> ConvertWhereLambda<T, TSearch>(TSearch searchData)
        {
            // x => true
            Expression<Func<T, bool>> where = xBiz => true;
            if (searchData == null) return where.Compile();

            var tPropertityArr = typeof(T).GetProperties();
            var tsearchPropertityArr = typeof(TSearch).GetProperties();
            //构建Lambda表达式  x => 
            var parameter = Expression.Parameter(typeof(T), "x");

            foreach (var itemPropertity in tsearchPropertityArr)
            {
                var fieldName = itemPropertity.Name;
                if (fieldName == "ROWUID") continue;
                var contains = tPropertityArr.FirstOrDefault(x => x.Name == fieldName);
                if (contains == null) continue;

                var checkTuple = itemPropertity.CheckValidValue(searchData);
                if (!checkTuple.isValid) continue;

                //  表达式左侧 like: p.Name
                var left = Expression.PropertyOrField(parameter, fieldName);

                //  表达式右侧,比较值, like '张三'
                var fieldValue = checkTuple.value;
                var right = Expression.Constant(fieldValue);

                //  表达式连接符
                Expression constant;
                //  属性的连接符
                var searchConditionEnum = itemPropertity.GetSearchConditionAttribute();
                //  属性的连接符 Convert 表达式连接符
                switch (searchConditionEnum)
                {
                    case SearchConditionEnum.Gt:
                        constant = Expression.GreaterThan(left, right);
                        break;
                    case SearchConditionEnum.Gte:
                        constant = Expression.GreaterThanOrEqual(left, right);
                        break;
                    case SearchConditionEnum.Lt:
                        constant = Expression.LessThan(left, right);
                        break;
                    case SearchConditionEnum.Lte:
                        constant = Expression.LessThanOrEqual(left, right);
                        break;
                    case SearchConditionEnum.DateRange:
                        var itemValue = right.Value;
                        if (itemValue == null) continue;
                        var itemDateArr = itemValue as string[];
                        var tuple = itemDateArr.ConvertArrToDateTuple();
                        var itemStartDateBoo = tuple.firstBoo;
                        var itemStartDate = tuple.firstDate;
                        var itemEndDateBoo = tuple.secondBoo;
                        var itemEndDate = tuple.secondDate;

                        if (itemStartDateBoo && itemEndDateBoo)
                        {
                            // x => x.Date >= yesterday
                            var itemLeft = Expression.Constant(itemStartDate);
                            var itemLeftConstant = Expression.GreaterThanOrEqual(left, itemLeft);
                            // x => x.Date <= tomorrow
                            var itemRight = Expression.Constant(itemEndDate);
                            var itemRightConstant = Expression.LessThanOrEqual(left, itemRight);
                            constant = Expression.And(itemLeftConstant, itemRightConstant);
                        }
                        else if (itemStartDateBoo && !itemEndDateBoo)
                        {
                            // x => x.Date >= yesterday
                            var itemLeft = Expression.Constant(itemStartDate);
                            constant = Expression.GreaterThanOrEqual(left, itemLeft);
                        }
                        else if (!itemStartDateBoo && itemEndDateBoo)
                        {
                            // x => x.Date >= yesterday
                            var itemRight = Expression.Constant(itemEndDate);
                            constant = Expression.GreaterThanOrEqual(left, itemRight);
                        }
                        else
                        {
                            constant = null;
                        }

                        break;
                    case SearchConditionEnum.In:
                        constant = CaseIn(itemPropertity, left, right);
                        break;
                    case SearchConditionEnum.Equals:
                    default:
                        constant = Expression.Equal(left, right);
                        break;
                }
                if (constant != null) where = where.And(Expression.Lambda<Func<T, bool>>(constant, parameter));

            }
            return where.Compile();
        }

        /// <summary>
        /// 特殊处理 In: 集合/数组/字符串/数字
        /// </summary>
        /// <param name="itemPropertity"></param>
        /// <param name="left"></param>
        /// <param name="right"></param>
        /// <returns></returns>
        private static Expression CaseIn(PropertyInfo itemPropertity, MemberExpression left, ConstantExpression right)
        {
            Expression constant;
            var propertyType = itemPropertity.PropertyType;
            // like 查询,需要调用外部int或string的Contains方法
            MethodInfo method = null;
            if (propertyType.Name.Contains("List") || propertyType.IsArray)
            {
                method = propertyType.GenericTypeArguments.FirstOrDefault() == typeof(int)
                    ? typeof(List<int>).GetMethod("Contains", new Type[] { typeof(int) })
                    : typeof(List<string>).GetMethod("Contains", new Type[] { typeof(string) });
                constant = Expression.Call(right, method, left);
            }
            else
            {
                // TODO: 如果值为null,会引发空异常
                method = propertyType == typeof(int)
                    ? typeof(int).GetMethod("Contains", new Type[] { typeof(int) })
                    : typeof(string).GetMethod("Contains", new Type[] { typeof(string) });
                constant = Expression.Call(left, method, right);
            }

            return constant;
        }

        public static Expression<T> Compose<T>(this Expression<T> first, Expression<T> second, Func<Expression, Expression, Expression> 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<T>(merge(first.Body, secondBody), first.Parameters);
        }

        public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> first, Expression<Func<T, bool>> second)
        {
            return first.Compose(second, Expression.And);
        }
    }

    public class ParameterRebinder : ExpressionVisitor
    {
        private readonly Dictionary<ParameterExpression, ParameterExpression> map;

        public ParameterRebinder(Dictionary<ParameterExpression, ParameterExpression> map)
        {
            this.map = map ?? new Dictionary<ParameterExpression, ParameterExpression>();
        }

        public static Expression ReplaceParameters(Dictionary<ParameterExpression, ParameterExpression> 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);
        }
    }
}
View Code

 

posted @ 2021-09-03 17:52  Robot-Blog  阅读(53)  评论(0编辑  收藏  举报