C#用表达式树构建动态查询的方法

下面是C#用表达式树构建动态查询的完整攻略。

什么是表达式树

表达式树(Expression Tree)是将操作表达式按照层级结构组成的一种数据结构,类似于抽象语法树(AST)。在C#中,表达式树可以动态表示Lambda表达式的结构。

为何要用表达式树构建动态查询

在很多情况下,我们需要设计一个通用的、可扩展的查询条件表达式,比如一个动态搜索框,用户可以在其中输入部分查询条件,程序应该根据用户输入生成对应的查询语句。此时,表达式树的动态构建能力可以帮助我们实现这一需求。

如何用表达式树构建动态查询

以下是具体的步骤。

第一步:定义查询参数类

首先,我们需要先定义一个查询参数类,它包含了我们需要查询的表的所有属性,并且这些属性都是可空的。代码示例:

public class QueryParams
{
    public string? Name { get; set; }
    public int? Age { get; set; }
    public string? Gender { get; set; }
    // 其他属性
}

第二步:根据参数动态构建表达式树

接下来,我们需要根据用户输入的查询参数动态构建表达式树。以上面的查询参数类为例,查询条件可以是多个条件组成的逻辑与(And)关系。我们可以像下面这样构建表达式树:

public Expression<Func<UserInfo, bool>> BuildQueryExpression(QueryParams queryParams)
{
    // 创建 UserInfo 的参数表达式
    var userInfoParam = Expression.Parameter(typeof(UserInfo), "u");

    // 创建一个空的查询表达式,作为操作的基础
    Expression queryExpr = Expression.Constant(true);

    // 根据查询参数构建查询表达式
    if (!string.IsNullOrEmpty(queryParams.Name))
    {
        var nameExpr = Expression.Equal(Expression.Property(userInfoParam, nameof(UserInfo.Name)), Expression.Constant(queryParams.Name));
        queryExpr = Expression.AndAlso(queryExpr, nameExpr);
    }

    if (queryParams.Age.HasValue)
    {
        var ageExpr = Expression.Equal(Expression.Property(userInfoParam, nameof(UserInfo.Age)), Expression.Constant(queryParams.Age.Value));
        queryExpr = Expression.AndAlso(queryExpr, ageExpr);
    }

    if (!string.IsNullOrEmpty(queryParams.Gender))
    {
        var genderExpr = Expression.Equal(Expression.Property(userInfoParam, nameof(UserInfo.Gender)), Expression.Constant(queryParams.Gender));
        queryExpr = Expression.AndAlso(queryExpr, genderExpr);
    }

    // 将查询表达式和参数表达式组合成 Lambda 表达式
    return Expression.Lambda<Func<UserInfo, bool>>(queryExpr, userInfoParam);
}

以上代码通过循环遍历查询参数,根据参数组合运算构建查询表达式。同时,Expression.Lambda方法将表达式和参数组合成了一个Lambda表达式,得到一个可以用于Linq查询的查询条件。

第三步:用Linq查询结果

最后,我们可以拿着得到的查询条件,使用Linq查询数据源,比如:

using (var db = new DbContext())
{
    var userInfoQuery = db.UserInfo.AsQueryable();
    var queryParams = new QueryParams { Name = "张三", Age = 18, Gender = "Male" };
    var queryExpr = BuildQueryExpression(queryParams);

    var result = userInfoQuery.Where(queryExpr).ToList();

    // 处理查询结果
}

两条示例说明

以下是两个使用表达式树动态构造查询条件的示例。

示例一:动态构造Like查询条件

假设我们需要实现一个模糊查询,可以匹配一张表中的多个属性字段。在Linq中,模糊查询可以用like关键字实现,但是每个属性都需要构造一遍like查询表达式,非常麻烦。通过表达式树的动态构造能力,我们可以实现动态的多属性模糊查询。代码示例:

public Expression<Func<TEntity, bool>> BuildLikeExpression<TEntity>(params Expression<Func<TEntity, object>>[] propertySelectors)
{
    var parameter = Expression.Parameter(typeof(TEntity), "x");
    var constants = new List<ConstantExpression>();
    var body = propertySelectors.Aggregate<UnaryExpression, Expression>(null, (current, selector) =>
    {
        var property = (PropertyInfo)((MemberExpression)selector.Body).Member;
        var constant = Expression.Constant($"%{search}%");

        if (property.PropertyType != typeof(string) && property.PropertyType != typeof(Guid))
        {
            throw new ArgumentException("属性类型必须为string或Guid");
        }

        if (property.PropertyType == typeof(Guid))
        {
            constant = Expression.Convert(constant, typeof(Guid));
        }

        constants.Add(constant);
        var left = Expression.Call(CreateToUpperMethodCallExpression(selector), typeof(string).GetMethod("Contains", new[] { typeof(string) }), constant);
        return current == null ? left : Expression.Or(current, left);
    });

    var lambda = Expression.Lambda<Func<TEntity, bool>>(body, parameter);
    return lambda;
}

private static MethodCallExpression CreateToUpperMethodCallExpression(Expression expression)
{
    if (!(expression is MemberExpression memberExpression))
    {
        throw new ArgumentException("selector必须是MemberExpression类型");
    }

    var property = (PropertyInfo) memberExpression.Member;
    if (property.PropertyType != typeof(string))
    {
        throw new ArgumentException("属性类型必须为string");
    }

    var toUpperMethod = typeof(string).GetMethod("ToUpper", new Type[0]);
    var propertyAccess = Expression.MakeMemberAccess(expression, property);
    return Expression.Call(propertyAccess, toUpperMethod);
}

以上代码中,我们定义了一个BuildLikeExpression方法,它接受一个或多个属性选择器作为参数,用于构建Like查询条件。该方法利用了表达式树的组合能力,将多个查询属性的like表达式组合成一个复合表达式,并返回与该复合表达式等效的Lambda表达式。

示例二:动态构造多关键词查询

假如我们需要实现一个多关键词查询,用户输入的查询条件可以是多个关键词组成的逻辑"或"(Or)关系。在Linq中,多关键词查询可以用多个Contains关键字实现,但是每个查询关键字都需要构造对应的Contains查询表达式,非常繁琐。通过表达式树的动态构造能力,我们可以实现动态的多关键词查询。代码示例:

public Expression<Func<TEntity, bool>> BuildMultipleContainsExpression<TEntity>(string[] keywordArray, params Expression<Func<TEntity, object>>[] propertySelectors)
{
    var parameter = Expression.Parameter(typeof(TEntity), "x");
    var constants = new List<ConstantExpression>();
    var body = keywordArray.Aggregate<Expression>(null, (current, keyword) =>
    {
        var contain = propertySelectors.Aggregate<UnaryExpression, Expression>(null, (current1, selector) =>
        {
            var property = (PropertyInfo)((MemberExpression)selector.Body).Member;
            var constant = Expression.Constant($"%{keyword}%");

            if (property.PropertyType != typeof(string) && property.PropertyType != typeof(Guid))
            {
                throw new ArgumentException("属性类型必须为string或Guid");
            }

            if (property.PropertyType == typeof(Guid))
            {
                constant = Expression.Convert(constant, typeof(Guid));
            }

            constants.Add(constant);
            var left = Expression.Call(CreateToUpperMethodCallExpression(selector), typeof(string).GetMethod("Contains", new[] { typeof(string) }), constant);
            return current1 == null ? left : Expression.Or(current1, left);
        });

        return current == null ? contain : Expression.And(current, contain);
    });

    var lambda = Expression.Lambda<Func<TEntity, bool>>(body, parameter);
    return lambda;
}

以上代码中,我们定义了一个BuildMultipleContainsExpression方法,它接受一个或多个关键词和一个或多个属性选择器作为参数,用于构建多关键词查询表达式。该方法利用了表达式树的组合能力,将多个关键词和多个查询属性的Expression。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:C#用表达式树构建动态查询的方法 - Python技术站

(0)
上一篇 2023年6月1日
下一篇 2023年6月1日

相关文章

  • C#使用dynamic一行代码实现反射操作

    针对这个问题,我会给出一个详细的攻略和两个示例说明,希望对您有所帮助。 C#使用dynamic一行代码实现反射操作 在C#中,我们通常使用反射来访问和操作对象的成员,这样做需要费一些脑筋和代码量,但是我们可以通过使用dynamic类型来使得反射操作变得更为简便。 下面是使用dynamic一行代码实现反射操作的步骤: 创建一个动态类型的对象; 使用点号访问对象…

    C# 2023年5月31日
    00
  • C#中Predicate与Func泛型委托的用法实例

    下面是关于“C#中Predicate与Func泛型委托的用法实例”的详细攻略。 1. 概述 在C#中,Predicate<T>与Func<T, bool>是两个重要的泛型委托类型。它们的主要作用是作为参数来传递一些特定的方法,来进行预测和筛选操作。 其中,Predicate<T>主要用于检索、查找等操作,它的定义如下: p…

    C# 2023年6月8日
    00
  • C# 基于NAudio实现对Wav音频文件剪切(限PCM格式)

    下面是详细讲解如何使用C#和NAudio库来实现对Wav音频文件的剪切操作。 1. 准备工作 在开始之前,需要先准备好以下工作: 安装.NET开发环境(建议使用Visual Studio,下载地址:https://visualstudio.microsoft.com/zh-hans/downloads/); 安装NAudio库(可以使用NuGet进行安装,或…

    C# 2023年6月1日
    00
  • .NET Core项目使用swagger开发组件

    .NET Core项目使用Swagger开发组件 Swagger是一种用于描述RESTful Web服务的标准格式,它可以帮助我们生成API文档和客户端代码。在.NET Core项目中,我们可以使用Swagger来开发组件。本攻略将详细介绍如何在.NET Core项目中使用Swagger开发组件。 环境要求 在进行.NET Core项目使用Swagger开发…

    C# 2023年5月17日
    00
  • C#读写INI文件的方法

    下面是C#读写INI文件的方法的完整攻略。 1. 前言 INI文件是一种常见的配置文件格式,其中存储了一些应用程序的配置信息,如用户设置和选项。使用INI文件可以方便地对应用程序进行配置和修改。在C#中,我们可以使用System.IO类库中的一些类来读写INI文件。 2. 读取INI文件 2.1 定义INI文件读取类 在进行INI文件的读取时,我们通常需要定…

    C# 2023年6月1日
    00
  • C#连接ODBC数据源的方法

    连接ODBC数据源是C#中常用到的功能,下面提供一份完整的攻略。 1. 安装ODBC驱动 在连接ODBC数据源之前,需要先安装对应的ODBC驱动程序。驱动的安装方式因具体驱动而异,一般可以通过官方网站下载安装包,并按照说明进行安装。 2. 安装ODBC数据源 在安装完ODBC驱动后,需要根据具体的数据源类型,安装对应的ODBC数据源。数据源安装的步骤与驱动程…

    C# 2023年6月2日
    00
  • C# ExecuteScalar()方法案例讲解

    下面是“C#ExecuteScalar()方法案例讲解”的完整攻略。 什么是ExecuteScalar()方法? ExecuteScalar()方法是ADO.NET命名空间中的一种方法,用于在C#中执行一个查询,并返回查询结果集中的第一行第一列(第一行第一列必须是一个值)。ExecuteScalar()方法主要用于执行一些单值查询,例如返回记录数或计算汇总值…

    C# 2023年5月15日
    00
  • 解析asp.net的分页控件

    下面是详细的讲解“解析asp.net的分页控件”的攻略: 背景 在ASP.NET应用程序中,经常需要处理大型数据集,使其分页显示在Web页面上。ASP.NET分页控件允许您轻松地完成此任务,自动为您处理分页逻辑。 步骤 第一步:添加分页控件 在ASP.NETWeb应用的UI设计界面中,您可以从工具箱中添加控件。在此过程中,您将找到一个分页控件用于访问和操作分…

    C# 2023年6月3日
    00
合作推广
合作推广
分享本页
返回顶部