Search code examples
entity-frameworklinq-to-entities

How to return all records and whether a related record exists?


Using Entity Framework 4 CTP5 I have a basic model and a basic DbContext that works

public class Customer {
  public int CustomerId { get; set; }
  public int Name { get; set; }
  //...
  public ICollection<Address> Addresses { get; set; }
  public bool HasAddress {
    get {
      return Addresses.Count > 0;
    }
  }
}

public class Address {
  public int AddressId { get; set; }
  public string StreetLine1 { get; set; }
  //....
  public Customer Customer { get; set; }
}

How can I query my DbContext to return all customers and whether they have an address?

A customer can have multiple addresses and I don't want to return all the addresses for each customer when I am only interested in whether they have an address or not. I use context.Customers.Include(c => c.Addresses) but that returns all addresses for each customer


Solution

  • Keep in mind, in order to determine if a Customer has addresses, an aggregate operation (COUNT) must be done on the server. I don't think you can use that HasAddress property directly in the LINQ predicate, as AFAIK aggregate operations cannot be used in LINQ-Entities predicates.

    So you might have to do something like this:

    var result = ctx.Customers.Select(x => new
                                      {
                                         Customer = x,
                                         HasAddress = x.Addresses.Count() > 0
                                      }).ToList();
    

    That will return a collection of anonymous types, including all Customers, and whether they have at least one address.

    Note the () on the Count - this performs an actual aggregate operation - whilst .Count is a client-side LINQ operation.

    Edit

    If you want to put that anonymous type back into a Customer object, you can do this after the query has materialized (e.g ensuring the .Count() has been done on the database):

    var result = ctx.Customers.Select(x => new
                                          {
                                             Customer = x,
                                             HasAddress = x.Addresses.Count() > 0
                                          }).ToList()
                                          .Select(x => new Customer
                                          {
                                             // left to right copy....
                                             HasAddress = true
                                          }).ToList();
    

    Which is pretty dirty IMO, but it'll work.