Querying Sub-Types with Linq

November 6th, 2007

When querying with linq the background compiler automatically infers type information from any implementation of IEnumerable<T> as IEnumerable<anonymous type>. This gives us the IntelliSense we know and love while writing a query against a known type, without being bound to query results of which are of that type. Instead we can project onto new known types, new anonymous types, or in this case, a sub type.

Consider the following class:

public class Person
{
 public string Name { get; set; }
}

A list:

List<Person> persons = new List<Person>()
{
 new Person { Name="Bob" },
 new Person { Name="Nancy" },
};

And a simple query:
var query = from p in persons
 where p.Name == "Bob"
 select p;

In this query, only one expression has been selected, so our var query has inferred the type Person from the List<Person> implementation of IEnumerable<T>. Our IntelliSense for p looks like this:

query1

Now lets add some inheritance:

public class Employee : Person
{
 public bool IsActive { get; set; }
 public string Dept { get; set; }
}

Some new Employees to our list:

List<Person> persons = new List<Person>()
{
 new Person { Name="Bob" },
 new Person { Name="Nancy" },
 new Employee { Name="Sam", IsActive=true, Dept="Sales" },
 new Employee { Name="Alice", IsActive=false, Dept="Accounting" }
};

Hmm, now how do I select Employees who are active and work in sales if my IntelliSense is being driven off List<Person>?

Well, we can certainly use standard C# operators to get the job done:

var query = from p in persons
 where p is Employee &&
 (p as Employee).Dept == "Sales" &&
 (p as Employee).IsActive == true
 select p as Employee;

 
The compiler has indeed realized that we are now talking about Employee, and the var query is now of IEnumerable<Employee>.
 
Personally I think that is ugly. As an alternative, we can use the OfType operator to filter our Persons for Employees, which will automagically give us the IntelliSense we are looking for and infer that our var query is of type IEnumerable<Employee>
 
var query = from p in persons.OfType<Employee>()
 where p.Dept == "Sales" &&
 p.IsActive == true
 select p;

 
You might say, "Well that is sorta cool, but why would I ever be in a situation where I would need do this? Wouldnt I just fetch Employees from the DB and not worry about Person? I mean it IS a Person?"
 
Im glad you asked. When querying EDM types with the Entity Framework you pretty much have too. EF’s ObjectContext implements IEnumerable<T> with ObjectQuery<T>, where T is the type in the EntitySet. All Inherited types belong to their base types EntitySet, and are queried through its ObjectQuery<T>.
 

Comments are closed.