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# Winform消息通知系统托盘气泡提示框ToolTip控件

    一、引言 在C# Winform界面开发中,消息通知和提示框往往是必不可少的功能。Winform提供了两种常用的消息通知方式:系统托盘气泡提示和ToolTip控件。本文将详细讲解如何使用这两种控件。 二、系统托盘气泡提示 添加系统托盘图标 在Winform中使用系统托盘气泡提示,首先需要在窗体上添加一个NotifyIcon控件,用于显示图标。添加方法如下: …

    C# 2023年6月7日
    00
  • 灵活使用asp.net中的gridview控件

    使用ASP.NET中的GridView控件可以快速实现数据的呈现和管理。下面是灵活使用GridView控件的攻略: 1.绑定数据源 GridView控件的数据源可以是DataTable、DataSet、Array等多种类型的对象。以下是以DataTable作为数据源的示例: protected void Page_Load(object sender, Ev…

    C# 2023年6月3日
    00
  • asp.net(c#)实现从sqlserver存取二进制图片的代码

    实现从SQL Server存取二进制图片需要经历以下步骤: 在SQL Server中创建表来存储图片数据。通常,您需要为每个图像分配两个列:一个用于存储二进制数据,另一个用于存储图像的MIME类型。例如: CREATE TABLE [dbo].[ImageTable]( [ID] INT PRIMARY KEY IDENTITY(1,1), [ImageDa…

    C# 2023年5月31日
    00
  • C#中感叹号(!) 的作用总结

    当在C#中提及感叹号(!)时,通常指的是逻辑非运算符。这个运算符常用于实现反转布尔值。 逻辑非运算符返回一个布尔值(true或false)。如果操作数为true,则该运算符返回false;如果操作数为false,则该运算符返回true。 在C#中,逻辑非运算符主要有以下两种应用: 运用于空引用类型,表示判定该对象是否为空 在C#中,操作符!被用来判断对象是否…

    C# 2023年6月6日
    00
  • 浅析ASP.NET万能JSON解析器

    浅析ASP.NET万能JSON解析器 什么是JSON解析器? JSON(JavaScript Object Notation)是一种轻量级的数据交换格式。它易于人阅读和编写,同时也易于机器解析和生成。因此,JSON成为了一个广泛使用的数据交互格式。 在ASP.NET中,我们使用JSON格式来传递数据,以便客户端和服务器之间进行数据通信。JSON解析器是一种在…

    C# 2023年5月31日
    00
  • c# 实现位图算法(BitMap)

    C# 实现位图算法(BitMap)攻略 什么是位图算法 位图算法(BitMap),也称为比特映射算法。是一种基于位运算的数据结构。 它的原理是把数据映射到包含这些数据的整数范围内,利用0和1的二进制方式来记录数据是否出现过。当数据量庞大时,时间复杂度远低于其他数据结构,所以在一些需要高效的场景中应用广泛。 例如,在搜索引擎的爬虫程序中,经常需要对已爬取的网页…

    C# 2023年6月8日
    00
  • AntDesign Pro + .NET Core 实现基于JWT的登录认证功能

    AntDesign Pro + .NET Core 实现基于JWT的登录认证功能攻略 本攻略将介绍如何使用AntDesign Pro和.NET Core实现基于JWT的登录认证功能。本攻略将提供详细的步骤和示例说明,以帮助您快速入门AntDesign Pro和.NET Core的登录认证功能。 步骤 步骤1:创建一个新的AntDesign Pro项目 首先,…

    C# 2023年5月17日
    00
  • IIS7配置PHP图解(IIS7+PHP_5.2.17/PHP_5.3.5)

    IIS7配置PHP图解(IIS7+PHP_5.2.17/PHP_5.3.5) IIS7是一种Web服务器,可以用于托管ASP.NET和PHP应用程序。在IIS7中,可以使用PHP来开发Web应用程序。本文提供详细的“IIS7配置PHP图解(IIS7+PHP_5.2.17/PHP_5.3.5)”的完整攻略,包括如何安装PHP和IIS7,以及如何配置PHP和II…

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