.NET查询条件解析为Expression及排序解析

定义接收参数的数据类型

public class DataFilter
{
    public string Name { get; set; }
    public string Option { get; set; }
    public string Value { get; set; }
    public List<string> ValueList { get; set; }
}

/// <summary>
/// 排序字段
/// </summary>
public struct OrderField
{
    /// <summary>
    /// 字段名称
    /// </summary>
    public string PropertyName { get; set; }
    /// <summary>
    /// 是否倒序
    /// </summary>
    public bool IsDESC { get; set; }
}

public enum BasicDataType
{
    Int32,
    Int64,
    Single,
    Double,
    Decimal,
    Boolean,
    String,
    DateTime
}

public enum OperatorEnum
{
    // 等于
    OPT_EQ,

    // 不等于
    OPT_NQ,

    // 包含
    OPT_LIKE,

    // 不包含
    OPT_NOTLIKE,

    // 大于
    OPT_GT,

    // 大于等于
    OPT_GE,

    // 小于
    OPT_LT,

    // 小于等于
    OPT_LE,

    // 数组包含
    OPT_IN,

    // 数组不包含
    OPT_NOTIN,
}

Expression 解析器

namespace XXX
{
    /// <summary>
    /// 前端过滤条件转换为Expression供EFCore使用
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public static class DataFilterConvertor<T> where T : class
    {
        /// <summary>
        /// 入口方法,把数据过滤器转换为Expression
        /// </summary>
        /// <param name="dataFilterList"></param>
        /// <returns></returns>
        public static Expression<Func<T, bool>> ToExpression(List<DataFilter> dataFilterList)
        {
            if (dataFilterList == null || dataFilterList.Count < 1)
            {
                return (T) => true;
            }

            ParameterExpression parameterExpression = Expression.Parameter(typeof(T));
            Expression firstLambdaExpression = GetSingleFilterExpression(parameterExpression, dataFilterList[0]);
            Expression binaryExpression = Expression.AndAlso(Expression.Constant(true), firstLambdaExpression);
            foreach (DataFilter dataFilter in dataFilterList.Skip(1))
            {
                Expression lambdaExpression = GetSingleFilterExpression(parameterExpression, dataFilter);
                binaryExpression = Expression.AndAlso(binaryExpression, lambdaExpression);
            }
            return Expression.Lambda<Func<T, bool>>(binaryExpression, parameterExpression);
        }

        /// <summary>
        /// 获取单个过滤条件的Expression
        /// </summary>
        /// <param name="parameterExpression"></param>
        /// <param name="dataFilter"></param>
        /// <returns></returns>
        private static Expression GetSingleFilterExpression(ParameterExpression parameterExpression, DataFilter dataFilter)
        {
            MemberExpression memberExpression = null;
            if (dataFilter.Name.Contains('.'))
            {
                string[] multiLevelProperties = dataFilter.Name.Split('.');
                memberExpression = Expression.Property(parameterExpression, multiLevelProperties[0].ToString());
                foreach (string propertyName in multiLevelProperties.Skip(1))
                {
                    memberExpression = Expression.Property(memberExpression, propertyName);
                }
            }
            else
            {
                memberExpression = Expression.Property(parameterExpression, dataFilter.Name);
            }
            ConstantExpression constantExpression = null;
            if (!string.IsNullOrEmpty(dataFilter.Value))
            {
                dynamic formatedValue = DataValueFormat(dataFilter.Value, memberExpression.Type.Name);
                constantExpression = Expression.Constant(formatedValue, memberExpression.Type);
            }
            else
            {
                constantExpression = Expression.Constant(dataFilter.ValueList, GetGenericListType(memberExpression.Type.Name));
                if (memberExpression.Type.Name == "String")
                {
                    constantExpression = Expression.Constant(dataFilter.ValueList, typeof(List<string>));
                }
                else
                {
                    constantExpression = Expression.Constant(dataFilter.ValueList, typeof(List<int>));
                }
            }
            Expression body = CreateExpressionBody(memberExpression, constantExpression, dataFilter.Option);
            return body;
        }

        /// <summary>
        /// 根据字段,比较值获取Expression
        /// </summary>
        /// <param name="memberExpression"></param>
        /// <param name="constantExpression"></param>
        /// <param name="operatorStr"></param>
        /// <returns></returns>
        /// <exception cref="ArgumentException"></exception>
        private static Expression CreateExpressionBody(
            MemberExpression memberExpression, ConstantExpression constantExpression, string operatorStr)
        {
            if (!Enum.TryParse($"OPT_{operatorStr}".ToUpper(), true, out OperatorEnum operatorEnum))
            {
                throw new ArgumentException($"不支持操作符:{operatorStr}");
            }
            Type genericListType = null;
            switch (operatorEnum)
            {
                case OperatorEnum.OPT_EQ:
                    return Expression.Equal(memberExpression, constantExpression);
                case OperatorEnum.OPT_NQ:
                    return Expression.NotEqual(memberExpression, constantExpression);
                case OperatorEnum.OPT_LIKE:
                    return Expression.Call(memberExpression, typeof(string).GetMethod("Contains", new Type[] { typeof(string) }), constantExpression);
                case OperatorEnum.OPT_NOTLIKE:
                    return Expression.Not(Expression.Call(memberExpression, typeof(string).GetMethod("Contains", new Type[] { typeof(string) }), constantExpression));
                case OperatorEnum.OPT_GT:
                case OperatorEnum.OPT_GE:
                case OperatorEnum.OPT_LT:
                case OperatorEnum.OPT_LE:
                    return GetCompareExpression(memberExpression, constantExpression, operatorEnum);
                case OperatorEnum.OPT_IN:
                    genericListType = GetGenericListType(memberExpression.Type.Name);
                    return Expression.Call(constantExpression, genericListType.GetMethod("Contains", new Type[] { memberExpression.Type }), memberExpression);
                case OperatorEnum.OPT_NOTIN:
                    genericListType = GetGenericListType(memberExpression.Type.Name);
                    return Expression.Not(Expression.Call(constantExpression, genericListType.GetMethod("Contains", new Type[] { memberExpression.Type }), memberExpression));
                default:
                    throw new ArgumentException($"不支持操作符:{operatorEnum}");
            }
        }

        /// <summary>
        /// 获取比较表达式,支持字符串、数字、日期比较大小
        /// </summary>
        /// <param name="memberExpression"></param>
        /// <param name="constantExpression"></param>
        /// <param name="operatorEnum"></param>
        /// <returns></returns>
        private static Expression GetCompareExpression(MemberExpression memberExpression, ConstantExpression constantExpression, OperatorEnum operatorEnum)
        {
            ConstantExpression constant0Expression = Expression.Constant(0);
            Expression strCompareExpression = null;
            if (memberExpression.Type.Name == BasicDataType.String.ToString())
            {
                strCompareExpression = Expression.Call(memberExpression, typeof(string).GetMethod("CompareTo", new Type[] { typeof(string) }), constantExpression);
            }
            switch (operatorEnum)
            {
                case OperatorEnum.OPT_GT:
                    if (memberExpression.Type.Name == BasicDataType.String.ToString())
                    {
                        return Expression.GreaterThan(strCompareExpression, constant0Expression);
                    }
                    else 
                    {
                        return Expression.GreaterThan(memberExpression, constantExpression);
                    }
                case OperatorEnum.OPT_GE:
                    if (memberExpression.Type.Name == BasicDataType.String.ToString())
                    {
                        return Expression.GreaterThanOrEqual(strCompareExpression, constant0Expression);
                    }
                    else
                    {
                        return Expression.GreaterThanOrEqual(memberExpression, constantExpression);
                    }
                case OperatorEnum.OPT_LT:
                    if (memberExpression.Type.Name == BasicDataType.String.ToString())
                    {
                        return Expression.LessThan(strCompareExpression, constant0Expression);
                    }
                    else
                    {
                        return Expression.LessThan(memberExpression, constantExpression);
                    }
                case OperatorEnum.OPT_LE:
                    if (memberExpression.Type.Name == BasicDataType.String.ToString())
                    {
                        return Expression.LessThanOrEqual(strCompareExpression, constant0Expression);
                    }
                    else
                    {
                        return Expression.LessThanOrEqual(memberExpression, constantExpression);
                    }
                default:
                    return null;
            }
        }

        /// <summary>
        /// 根据属性类型对获取list泛型类型
        /// </summary>
        /// <param name="typeName"></param>
        /// <returns></returns>
        /// <exception cref="ArgumentException"></exception>
        private static Type GetGenericListType(string typeName)
        {
            if (!Enum.TryParse(typeName, true, out BasicDataType dataType))
            {
                throw new ArgumentException($"不支的数据类型:{typeName}");
            }

            switch (dataType)
            {
                case BasicDataType.String:
                    return typeof(List<string>);
                case BasicDataType.Int32:
                    return typeof(List<string>);
                default:
                    throw new ArgumentException("不支持的泛型类型转换");
            }
        }

        /// <summary>
        /// 输入value值根据对应的属性类型格式化
        /// </summary>
        /// <param name="value"></param>
        /// <param name="typeName"></param>
        /// <returns></returns>
        /// <exception cref="ArgumentException"></exception>
        /// <exception cref="Exception"></exception>
        private static dynamic DataValueFormat(string value, string typeName)
        {
            if (!Enum.TryParse(typeName, true, out BasicDataType dataType))
            {
                throw new ArgumentException($"不支的数据类型:{typeName}");
            }

            switch (dataType)
            {
                case BasicDataType.Int32:
                    return Convert.ToInt32(value);
                case BasicDataType.Int64:
                    return Convert.ToInt64(value);
                case BasicDataType.Single:
                    return Convert.ToSingle(value);
                case BasicDataType.Double:
                    return Convert.ToDouble(value);
                case BasicDataType.Decimal:
                    return Convert.ToDecimal(value);
                case BasicDataType.String:
                    return value;
                case BasicDataType.DateTime:
                    return Convert.ToDateTime(value);
                case BasicDataType.Boolean:
                    value = value.ToUpper();
                    if (value == "TRUE" || value == "1")
                    {
                        return true;
                    }
                    else if (value == "FALSE" || value == "0")
                    {
                        return false;
                    }
                    else
                    {
                        throw new Exception($"{value}不能被解析为Bool类型");
                    }
                default:
                    throw new Exception($"{value}不能被解析为{typeName}类型");
            }
        }
    }
}

排序 OrderBy 解析器

/// <summary>
/// 排序
/// </summary>
/// <param name="querySource"></param>
/// <param name="orderFields"></param>
/// <returns></returns>
public static IQueryable<T> BatchOrderBy<T>(this IQueryable<T> querySource, params OrderField[] orderFields)
{
    ParameterExpression parameterExpression = Expression.Parameter(typeof(T));
    if (orderFields == null || orderFields.Length == 0)
    {
        return querySource;
    }

    bool isFirstLoop = true;
    foreach (OrderField orderField in orderFields)
    {
        MemberExpression memberExpression = null;
        if (orderField.PropertyName.Contains('.'))
        {
            string[] multiLevelProperties = orderField.PropertyName.Split('.');
            memberExpression = Expression.Property(parameterExpression, multiLevelProperties[0].ToString());
            foreach (string propertyName in multiLevelProperties.Skip(1))
            {
                memberExpression = Expression.Property(memberExpression, propertyName);
            }
        }
        else
        {
            memberExpression = Expression.Property(parameterExpression, orderField.PropertyName);
        }
        Expression orderExpression = Expression.Lambda(memberExpression, parameterExpression);

        string orderMethodName = string.Empty;
        if (isFirstLoop)
        {
            isFirstLoop = false;
            orderMethodName = orderField.IsDESC ? "OrderByDescending" : "OrderBy";
        }
        else
        {
            orderMethodName = orderField.IsDESC ? "ThenByDescending" : "ThenBy";
        }
        MethodCallExpression resultExp = Expression.Call(typeof(Queryable), orderMethodName,
            new Type[] { typeof(T), memberExpression.Type }, querySource.Expression, Expression.Quote(orderExpression));
        querySource = querySource.Provider.CreateQuery<T>(resultExp);
    }
    return querySource;
}

调用

//...
Expression<Func<StructureModel, bool>> expression = 
    DataFilterConvertor<StructureModel>.ToExpression(requestVM.DataFilterList);
IQueryable<StructureModel> query = nContext.Set<StructureModel>()
    .Include(o => o.Structure)
    .AsNoTracking().Where(expression);
totalCount = query.Count();
return query.BatchOrderBy(sortParaList)
    .Skip((pageIndex - 1) * pageSize)
    .Take(pageSize).ToList();
//...
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容