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

时间:2016-02-20 18:02:35

标签: c# asp.net-mvc entity-framework

我正在尝试使用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的最近地址。

知道怎么做吗?

4 个答案:

答案 0 :(得分:2)

您可以尝试anonymous projection,它会将您的查询转换为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();

答案 1 :(得分:1)

您无法在“包括”中进行过滤。在任何版本的实体框架中。

如果你需要加载集合的一个子集,那么你需要加入而不是使用导航属性和过滤器,只要你需要使用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();

答案 2 :(得分:0)

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

在我们的例子中,我们可以在Address实体上应用过滤器,然后将其分组到Contact实体上。

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的联系人的分组列表,以及这些地址类型和注册年份。这里重要的是遍历组以获取地址,而不是使用grouping.Key.Addresses导航。 (grouping.Key将是Contact实体)

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

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

答案 3 :(得分:0)

包含Eager Load的集合,然后使用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();