【问题标题】:Use WHERE clause on a subtable in LINQ在 LINQ 中的子表上使用 WHERE 子句
【发布时间】:2018-07-25 21:59:00
【问题描述】:

在我的Customer 模型中,我有

public virtual ICollection<AddressModel> AddressIDs { get; set; }

引用AddressModel 为我提供客户与其地址之间的一对多关系。

我有一个搜索功能,它使用

var CustomerList = _context.Customers
    .Where(ps => ps.Surname.Contains(surnameToSearchFor))

按姓氏限制返回的数据集。

我正在尝试添加在地址中搜索邮政编码的功能。按照各种链接,这在 Visual Studio 中有效,但在执行时中断

CustomerList = CustomerList
    .Include(ps => ps.AddressIDs
                     .Where(a => a.Postcode == postcodeToSearchFor));

有错误

InvalidOperationException: The property expression 'ps => {from AddressModel a in ps.AddressIDs where ([a].Postcode == __p_0) select [a]}' is not valid. The expression should represent a property access: 't => t.MyProperty

如何在子表上的 LINQ 中添加Where 子句?

编辑 对于建议Multiple WHERE clause in Linq 作为答案的人,该问题显然与单个表有关,而我明确询问了子表。

【问题讨论】:

标签: c# linq entity-framework-core


【解决方案1】:

您不能在 Include 中使用 where 语句。你可以像这样使用单个 linq 查询得到你想要的:

var CustomerList = _context.Customers.Where(ps =>
    ps.Surname.Contains(surnameToSearchFor) 
    && ps.AddressIDs.Any(ad => ad.Postcode == postcodeToSearchFor ));

【讨论】:

  • 请注意,这不会过滤包含的列表。它将为您提供邮政编码中至少有一个地址的所有客户,但随后它将加载客户的所有地址,而不管他们的邮政编码如何。
  • @Flatter 5,没有 Include 关键字,它不会加载所有地址。实际上,它还没有加载任何地址(假设 op 使用预先加载选项)。不过你的想法其实是对的。
  • 我错过了失踪的Include(),你是对的。我有点假设 OP 想要加载地址。我在这个假设下写了一个答案,但如果 OP 不想要这些地址,那么你的答案会更好。
【解决方案2】:

正如已经提到的另一个答案(Progressive),您无法过滤Include

另一个答案(Progressive)可能对你来说是一个解决方案,也可能不是。它会为您提供在邮政编码中至少有一个地址的所有客户,然后它会加载客户的所有地址(包括在不同邮政编码中的地址)。

这个答案是为了你只想检索邮政编码的地址而写的,我怀疑是这种情况。如果您只想检索客户并且只过滤他们的地址(而不是加载地址),那么另一个答案(由 Progressive 提供)就是解决方案。


正如我所说,你不能通过包含来做到这一点。但是,还有其他解决方案:

1.查找地址并包括客户。

而不是查找客户并包括他们的地址。

 var addresses = _context.Addresses
                         .Include(a => a.Customer)
                         .Where(a =>
                              a.Postcode == postcodeToSearchFor
                              &&
                              a.Customer.Surname.Contains(surnameToSearchFor))
                     .ToList();

您仍然可以通过这种方式获取客户列表:

var customer = addresses.Select(a => a.Customer).Distinct();

作为一般经验法则,始终从孩子开始查询并包括其父母,而不是相反。在某些情况下这并不重要,但在您的特定情况下,它确实很重要,因为您希望避免隐式加载 all 子项。

2。明确定义您的结果集

换句话说,使用Select()

如果您对结果有非常具体的期望,这会给您更多控制权,但它更冗长并且(imo)不如其他解决方案好。仅当第一个解决方案不适合您时才使用它。

_context.Customers.Where(ps =>
                             ps.Surname.Contains(surnameToSearchFor) 
                             && 
                             ps.AddressIDs.Any(ad => ad.Postcode == postcodeToSearchFor))
                  .Select(ps => new
                                {
                                    Customer = ps,
                                    Addresses = ps.AddressIDs.Where(ad => ad.Postcode == postcodeToSearchFor))
                                })
                  .ToList();

请注意,这里不需要Include()Include() 配置隐式加载行为,但您的 Select() 显式加载数据。

【讨论】:

    【解决方案3】:

    试试这个:

    var CustomerList = _context.Customers.Where(ps => ps.Surname.Contains("surnameToSearchFor")).Select(ps => new
    {
       Surname = ps.Name,
       AddressIDs = ps.AddressIDs.Where(a => a.PostCode == postcodeToSearchFor)
     });
    

    【讨论】:

    • 您还将加载在postcodeToSearchFor 中没有任何地址的客户。
    • 可以通过添加这个和条件来解决:ps.Surname.Contains("surnameToSearchFor") && ps.AddressIDs.Count > 0
    • 但这一切都取决于您想要什么,如果您想要可以拥有 0 AddressIds 的客户,则需要删除第二个条件。
    • &amp;&amp; ps.AddressIDs.Count &gt; 0 会过滤掉没有任何地址的客户。它不会过滤掉有地址但在postcodeToSearchFor 中没有地址的客户。
    • ps.Surname.Contains("surnameToSearchFor") && ps.AddressIDs.Where(a => a.PostCode == postcodeToSearchFor).Count > 0 :)
    【解决方案4】:

    我需要来自Customer 模型的名字和姓氏,他们有一个特定的地址,所以根据 Progressive 的回答,我使用了这个

    var CustomerList = CustomerList.Where(ps => ps.AddressIDs
          .Any(a => a.Postcode.Contains(postcodeToSearchFor)));
    

    它会带回任何带有邮政编码任何部分的Customer,但不会带回带有空白或null邮政编码的任何人。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-08
      • 2020-12-31
      相关资源
      最近更新 更多