EF 7 包含一个带有 where 子句的集合,然后包括一个下一级的集合

本文关键字:集合 包括一 一级 然后 where 子句 EF 包含一 | 更新日期: 2023-09-27 18:35:27

我正在尝试使用 EF 7 查询集合和子集合。代码如下:

var customerID = 86795;
var query = await _context.Contacts
            .Where(g => g.CustomerID == customerID )
            .Include(g => g.Address.Where(p => p.AddressTypeID == 1))
            .ThenInclude(p=> p.City)
            .ToListAsync();

> Error CS1061  'IEnumerable<Address>' does not contain a definition for
> 'City' and no extension method 'City' accepting a first argument of
> type 'IEnumerable<Address>' could be found (are you missing a using
> directive or an assembly reference?)  Contacts.DNX 4.5.1, Contacts.DNX
> Core 5.0

当我只是使用

var customerID = 86795;
var query = await _context.Contacts
            .Where(g => g.CustomerID == customerID )
            .Include(g => g.Address)
            .ThenInclude(p=> p.City)
            .ToListAsync();

但这将加载客户的所有地址,我只想要 AddressTypeID 为 1 的最近地址。

知道怎么做吗?

EF 7 包含一个带有 where 子句的集合,然后包括一个下一级的集合

您可以尝试匿名投影,这会将您的查询转换为 SQL。

var customerID = 86795;
var query = await _context.Contacts
                  .Where(g => g.CustomerID == customerID)
                  .Select(cntct=> new
                  {
                     contact = cntct,
                     address = cntct.Address.Where(p => p.AddressTypeID == 1),
                     city    = cntct.Address.Where(p => p.AddressTypeID == 1)
                                            .Select(h=>h.City),
                  }.ToList();

无法在"包含"中进行筛选。在任何版本的实体框架中。

如果需要加载集合的子集,则需要使用 Join 而不是使用导航属性,并在需要使用 Where 子句进行筛选

像这样(简化,可读性的额外步骤):

var filteredAddresses = Addresses.Where(x=>x.AddressTypeId==1);
var customersWithAddress = Customers.Join(filteredAddresses, x=>x.Id,x=>x.CustomerId,(c,a)=> new {
    Customer=c,
    Address=a
});

或者,如果需要单个客户,假设您在"地址"中具有"客户"导航属性:

var addressWithCustomer = Addresses
    .Where(x=>x.AddressTypeId==1 && x.CustomerId == customerId)
    .Include(x=>x.Customer)
    .Include(x=>x.City)
    .Single();

很多时候,最好处理涉及条件嵌套实体的查询,从嵌套实体开始,将条件应用于此嵌套的同伴,然后投影出父实体,因为从嵌套的可枚举实体访问父实体总是更容易。(多对一)

在我们的例子中,我们可以在地址实体上

应用过滤器,然后在联系人实体上对其进行分组。

var customerID = 86795;
var query = await _context.Addresses
            .Where(a => a.Contact.CustomerID == customerID 
                        && a.Contact.RegistrationDate.Year == 2016 
                        && a.AddressTypeID == 1)
            .Include(a => a.Contact)
            .Include(a => a.City)
            .GroupBy(a => a.Contact)
            .Take(20)  // by the way, you should apply some orderby for a predicatble Take
            .ToListAsync();

如果您绝对想要联系人列表作为上述查询的输出,则可以执行此操作。

var contacts = query.Select(g => 
{
 g.Key.Addresses = g.ToList();
 return g.Key;
}).ToList();
// now you can work off the Contacts list, which has only specific addresses

这基本上将为您提供具有CustomerID的所有联系人的分组列表,并且仅具有这些地址类型和注册年份。这里重要的是遍历组以获取地址,而不是使用分组。键地址导航。(分组。键将是联系人实体)

另外,我不知道 CustomerID 是否是联系人实体上的主键,但如果是,看起来您只需要一个联系人的匹配地址列表。在这种情况下,查询将是:

var query = await _context.Addresses
            .Where(a => a.Contact.CustomerID == customerID && a.AddressTypeID == 1)
            .Include(a => a.Contact)
            .Include(a => a.City)
            .ToListAsync();

包括预先加载的集合,然后使用 Any 而不是 Where ...以选择所需实体的子项中的特定项。

var customerID = 86795;
var query = await _context.Contacts
        .Where(g => g.CustomerID == customerID )
        .Include(g => g.Address.Any(p => p.AddressTypeID == 1))
        .ThenInclude(p=> p.City)
        .ToListAsync();