Sep 26, 2009

IEnumberable, IQueryable , Lambda expression - part2

I have seen such following piece of code written by a developer from a client.

interface IContactRepository
{
    IEnumberable<contact> GetSomeContacts();
}

class ContactRepository : IContactRepository
{
   public IEnumerable<contact> GetSomeContacts()
   {
      //query is linq to sql query object
      IQueryable<contact> query = ...
      return query;
    }
}

Is it a better choice to using IEnumerable<t> instead of IQueryable<t>. I guess his concerns is that, if the interface is too specific, first this may give client more functionality than is required, second this may limit the server's choice of implementation. In lots case, this concern is right, we should give client the only functionality which client needs, nothing less and nothing more, and server should has more freedom to implement.

interface IPerson 
{
   void Eat();
   void Sleep();
}

interface ISales : IPerson
{
    void Sell();
}

interface ITeacher : IPerson
{
    void Teache();
}

class Service
{
     //Unappropriate
//     public ISales  GetPerson()
//     {
//            return ...
//     }

     //better
     public IPerson GetPerson()
     {
            return ...
     }

}

Firstly, if the method return a ISales, First client will have one extra unnecessary method Sell. Secondly If the client only needs a IPerson, and the contract says client needs a IWorker, this will limit server's ability to serve the client, for example, server can not return a ITeacher.

Is this design guideline also applicable to the case of IContactRepository.

public interface IQueryable<t> : IEnumerable<t>, IQueryable, IEnumerable 
{} 
 
public interface IQueryable : IEnumerable 
{ 
        Type ElementType { get; } 
        Expression Expression { get; } 
        IQueryProvider Provider { get; } 
} 

First the the IQuerable<t> interface does give user more functionality than the IEnunumerable<t>, but these members are read only, and client can not use them directly for query. Because the query functionality comes from the static method in Enumerable and Queryable, but not the IQuerable<t>, and IEnumeralbe<t>, from the client's perspective, Two interfaces works identically. Secondly, the interface does limit limit server's implementation choice, because server cannot return a IEnumberable<t> . Initially, I thought I can implement easily a empty IQueryable<t> that wrap a IEnumberable<t>. It turns out to be even easier. Because the Enumerable already implement an static method AsQueryable() for you, the Linq team in Microsoft already expect this is a common use case. So all you need to do is call the method can you IEnumberable&lgt;T> will become IQueryable<t>. like the following.

int[] intEnumerable = { 1, 2, 3 , 5};
IQueryable intQuery = intEnumerable.AsQueryable().Where( number => number > 2);
foreach (var item in intQuery)
{
    Console.WriteLine(item);
}
Console.WriteLine(intQuery.GetType().ToString()); //System.Linq.EnumerableQuery`1[System.Int32]

//code decompiled by reflector
  ParameterExpression CS$0$0000;
    IQueryable intQuery = new int[] { 1, 2, 3, 5 }.AsQueryable<int>().Where<int>(Expression.Lambda<Func<int, bool>>(Expression.GreaterThan(CS$0$0000 = Expression.Parameter(typeof(int), "number"), Expression.Constant(2, typeof(int))), new ParameterExpression[] { CS$0$0000 }));
    foreach (object item in intQuery)
    {
        Console.WriteLine(item);
    }
    Console.WriteLine(intQuery.GetType().ToString());


So a it seems be a better to replace IEnumberable<t> with IQueryable<t>. As for as the interface concerns, the replacement does not give client any exactly same query experience and it is more difficult to implement. A great benefit of this replacement is the performance, using IEnumberable<t> will be much slower than IQuerable<t>. Consider the following code, the Where method for IQueryable<t> will treat the lambda expression as expression tree and query will be executed at server side which is much faster, while the IEnumerable<t> will treat the lambda expression as delegate and query will be executed at client side, which will be slower. Consider the following code.

var thisContact = contaceRepository. GetSomeContacts().Where( ctc => ctc.Id = 1).First();

Linq provide us a new way to design our domain model. In the post Extending the World, author says

Typically for a given problem, a programmer is accustomed to building up a solution until it finally meets the requirements. Now, it is possible to extend the world to meet the solution instead of solely just building up until we get to it. That library doesn't provide what you need, just extend the library to meet your needs.

It is very important to build extensible domain model by taking the advantage of IQueryable<t> interface. Using IEnumberable<t> only will hit the performance very seriously. The only pitfall to user IQueryable<t> is that user may send unnecessary complex to the server, but this can be resolved by designing the method so that only appropriate IQueryable<t> is returned, for example return GetSome instead of GetAll. Another solution is adding a view model which return a IEnumberable<t>