c# - 将表达式树与 EF Core 结合使用会产生奇怪的 SQL 语句

标签 c# sql entity-framework-core expression-trees

我使用表达式树在运行时动态构建表达式,然后在 .Where() 中使用它IQueryable<T> 的条款,正如我所说的我的数据库。生成的 SQL 语句对我来说看起来非常奇怪,我不明白那里发生了什么。

一般信息:项目使用 Framework 4.7.2、EF Core 3.1.3 NuGet

示例(以下是简化的):

考虑这样的客户类别:

public class Customer{
    public string Name {get; set;}
    public int Age {get; set;}
    public string Address {get; set;}
}

DBContext 已正确设置 DBSet<Customer>等等。

所以现在我想使用类似 ... db.Customer.Where(expression).ToList() ... 的东西我必须在运行时构建相应的表达式。该程序将获得用于搜索条件的列表,例如 List<(string, string)>其中第一个字符串是客户属性的名称,第二个字符串是用于过滤的值。这种传递搜索条件的方式无法更改。

我像这样构建我的表达式树,将搜索条件列表转换为字典( Dictionary<string, List<string>> ),并以属性名称作为键和要搜索的实际值的值列表。 typesDictionary 保存有关属性类型的信息(名称 -> 字符串等):

var param = Expression.Parameter(typeof(Customer), "c");
var andList = new List<Expression>();

foreach (var sc in searchCriteria){
    var orList = new List<Expression>();
    foreach (var value in sc.Value{
       var expr = Expression.Equal(
       Expression.Property(param, sc.Key),
       Expression.Constant(Convert.ChangeType(value, typesDictionary[sc.Key]), typesDictionary[sc.Key]));
       orList.Add(expr);
     }
     andList.Add(orList.Aggregate(Expression.Or));
}
var expression = Expression.Lambda<Func<Customer, bool>>(andList.Aggregate(Expression.And), param);

结果表达式类似于 c => ((c.Name == "Bob") OR (c.Name == "John")) AND (c.Age == 12)例如。括号的使用有点过多......

关于 SQL,我希望它是这样的:

SELECT c.Name, c.Age, c.Address
FROM someDB.someSchema.Customers as c
WHERE c.Name = "John" OR c.Name = "Bob" AND c.Age = 13

但是创建的内容是这样的:

SELECT [c].[Name], [c].[Aage], [c].[Address]
      FROM [Customer] AS [c]
      WHERE ((CASE
          WHEN [c].[Age] = CAST(12 AS int) THEN CAST(1 AS bit)
          ELSE CAST(0 AS bit)
      END | CASE
          WHEN [c].[Age] = CAST(15 AS int) THEN CAST(1 AS bit)
          ELSE CAST(0 AS bit)
      END) | CASE
          WHEN [c].[Age] = CAST(22 AS int) THEN CAST(1 AS bit)
          ELSE CAST(0 AS bit)
      END) = CAST(1 AS bit)

这是怎么回事? CASE WHEN都在哪里?和CASTS从哪里来?

最佳答案

你的问题是Expression.Orbitwise or ,所以基本上是 | 运算符。而 Expression.Andbitwise and (&)。所以你的表达是:

c => (c.Name == "John" | c.Name == "Bob" ) & c.Age = 13;

您想要的(您如何手写)是这样的:

c => (c.Name == "John" || c.Name == "Bob" ) && c.Age = 13;

为此,您需要使用 Expression.OrElseExpression.AndAlso:

foreach (var sc in searchCriteria){
    var orList = new List<Expression>();
    foreach (var value in sc.Value) {
        var expr = Expression.Equal(
            Expression.Property(param, sc.Key),
            Expression.Constant(Convert.ChangeType(value, typesDictionary[sc.Key]), typesDictionary[sc.Key]));
        orList.Add(expr);
    }
    andList.Add(orList.Aggregate(Expression.OrElse));
}
var expression = Expression.Lambda<Func<Customer, bool>>(andList.Aggregate(Expression.AndAlso), param);

之后,您应该会生成更多“正常”的 SQL 查询。

关于c# - 将表达式树与 EF Core 结合使用会产生奇怪的 SQL 语句,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/69634357/

相关文章:

c# - 动态输入数据时 TCP 服务器无法正确接收

C# - 将数据传递给 JS 并等待结果进行处理

mysql - 使用 SQL 在动态环境中分页排行榜查询

sql - 如何根据修改后的值更新 SQL Server 表?

mysql - 如何在同一张表上查询不同的where条件和连接结果

entity-framework - 仅从 Entity Framework Core 中包含的表中选择特定列

c# - 如何将 EF Core 与适用于 .NET 5+ 的 Azure SQL 分片结合使用?

c# - 为什么 WebClient (System.Net) 从 URL 获取两次?

c# - 在任务取消时处理 CancellationTokenSource 的代码是否正确?

c# - 网络核心 DbContextPool 与 AddDbContextPool 等