I am currently looking for a contract in the London area -

If you're hiring a .NET contractor in or around London, look no further!

Linq tip - returning polymorphic types from a query


I haven't posted since February, mainly because my time has been taken up with preparations for, and actually doing, the Mongol Rally 2008 - driving from London to Mongolia in a 1988 Fiat Panda. We completed it in 6 weeks 2 days, having travelled through 16 countries, 7 time zones, 3 rivers, 2 deserts, 1 blizzard, and 0 Starbucks.

While I'm getting back into the swing of things, I thought I'd share a little Linq tip with you. Quite often I have data in a database that has a 'type' column, indicating the type of data in another column. When I deserialize this from the database, I would like to change the type of object I am creating based on the value of this column, normally with a common base class.

The select statement in Linq allows us to put any expression we like in, as long as it conforms to the type rules of the context of the query. So, using the conditional assignment operator (?), we can return a collection of objects descended from a common type, like this:

abstract class Number { }
class EvenNumber : Number { }
class OddNumber : Number { }

IEnumerable<Number> GetNumbers()
{
 return from i in Enumerable.Range(1, 10)
  select
   (i % 2 == 0) ? 
    (Number)new EvenNumber() : (Number)new OddNumber();
}


Note that you have to cast both sides of the expression to the base type you are returning. That is because the compiler isn't clever enough to work out that both sides of the expression are type equivalent when the return type is their superclass.

This method can be used in a database query as shown below.

IEnumberable<User> GetUsers()
{
 using (MyDatabaseContext ctx = new MyDatabaseContext())
 {
  return from u in ctx.Users
   select
    u.user_type == UserType.Admin ? 
      (User)new AdministratorUser() : 
      (User)new NormalUser();
 }
}


It should be noted that this technique doesn't scale to more than 3 different types however - due to the spaghetti code created when nesting lots of ? expressions together. Maybe we should all petition Anders for a case-expression similar to SQL? :)

1 comments:

Nik Smit

18 October 2008 at 17:08

Very useful - especially as we tend towards the "sparsely populated column" paradigm.

Could see a lot of business layer code-generators taking advantage of this (even with that spaghetti code)... if you even need such generators anymore.