【问题标题】:How to query on groups of predicate in EF Core如何在 EF Core 中查询谓词组
【发布时间】:2019-05-13 15:04:15
【问题描述】:

我想对一组数据应用多个复合过滤器,如下所示:

Filter[] filters = new[] { new Filter { Name = "Bob", Gender = "Male"  },
new Filter { Name = "Alice", Height = "Female" } };

_dbContext.People.Where(p => filter.Any(f => f.Name == p.Name && f.Gender == p.Gender)).Select(p => p.Id);

我对 Male BobsFemale AlicesIds 感兴趣。对不起女性鲍勃。我不要你。

这是在内存 Linq 中解决该问题的正确方法,但是 有问题。这就是 SQL EF 生成的样子(我正在我的 SQL Server Profiler 中检查)

SELECT [p].[Name], [p].[Gender], [p].[Id] FROM [People] AS [p]

这太可怕了。它挖掘所有内容,然后在内存中进行实际工作。这对很多人来说是不可能的,它会停止。

有没有办法让生成的sql看起来更像这样?

SELECT 
[Person].[Id]
FROM [Person] 
WHERE 
   ((([Person].[Name] = "Bob") AND ([Person].[Gender] = "Male")) 
   OR (([Person].[Name] = "Alice") AND ([Person].[Gender] = "Female")))

(在Dapper 中是可能的)

【问题讨论】:

  • 你可以试试predicate builder
  • 嘿,是不是有错误,应该是_dbContext.People.Where(p => filter.Any(f => f.Name == p.Name && f.Gender == p.Gender)).Select(p => p.Id);

标签: c# entity-framework-core


【解决方案1】:

您的查询结构表明姓名/性别组合的数量可能超过两个。在这种情况下,编写自己的存储过程而不是让 EF 创建查询可能更有意义。

在这种情况下,我将使用table-valued parameter 将一组行作为参数传递给存储过程。每行都包含一个名字和一个性别。该查询将该表参数连接到您的 Person 表,返回名称和性别都匹配的行。

【讨论】:

    【解决方案2】:

    这是我最后所做的,正如@stuartd 建议的那样:

    var predicate = PredicateBuilder.New<Person>();
    foreach (var filter in filters)
    {
        predicate = predicate.Or(p =>
           p.Gender == filter.Gender &&  filter.Name == p.Name));
    }
    
    people = _dbContext.People.Where(predicate).Select(r => r.Id).Distinct().ToArrayAsync();
    

    像魅力一样工作。谢谢。

    【讨论】:

      【解决方案3】:

      在这种特殊情况下(如果您只有少量过滤器),我建议将查询拆分为显式表达式,例如

      var maleNameFilter = "Bob";
      var femaleNameFilter = "Alice";
      _dbContext.People.Where(p => 
          (p.Name == maleNameFilter && p.Gender == "Male") 
          || (p.Name == femaleNameFilter && p.Gender == "Female")
      ).Select(p => p.Id);
      

      但是,我怀疑您会想要使用大量过滤器,在这种情况下,使用 LINQ 会变得更加困难。正如某些 cmets 中已经建议的那样,您可以为此使用 Predicate Builder(请参阅 example)。

      最后,如果您希望以更复杂的代价获得最大性能,您可以考虑将过滤器值放入数据库中的单独表中,并使用Join() 重写您的查询。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-11-11
        • 2019-01-14
        • 1970-01-01
        • 2022-10-17
        • 2023-03-19
        • 1970-01-01
        • 2022-01-14
        • 2021-08-02
        相关资源
        最近更新 更多