RIA – Should a service return the IQueryable<T>?

Note: The examples in this blog post uses WCF RIA Services PDC Beta, and changes can be made in the future to the framework.

In several of my blog posts about WCF RIA Services I have used the Repository pattern to return an IEnumerable<T>, for example:

public class CustomerRepository : ICustomerRepository
{
     public IEnumerable<Customer> GetCustomers()
     {
           //...
     }
}


And in the DomainService:


[EnableClientAccess()] public class CustomerService : DomainService { private ICustomerRepository _customerRepository; public CustomerService(ICustomerRepository customerRepository) { _customerRepository = customerRepository; } public IEnumerable<Customer> GetCustomers() { //Use _customerRepository } }
 
Here is the client-side code which Loads the DTO/”Presentation Model” into the DomainContext:

 var customerContext = new CustomerContext();

 customerContext.Load<Customer>(customerContext.GetCustomers());


The Load method takes a Query object as an argument, by doing so, it knows which method in the DomainService it should call to load the objects. We can on the client-side specify our own query and pass it down to the server, and WCF RIA Services will make sure the execution of the Query will take place on the server side, this will reduce the data passed over the wire to the client. BUT! There is a problem of using IEnumerable<T>. The query will take place on top of the IEnumerable<T>, which means that our Repository will return ALL customers from the database, and this can affect performance (I talked to a well known SQL Server MVP about retrieving too much data from the database, he told me that he never notice a problem of retrieving to much data from a database, there are other issues that can affect performance badly.). So in that case, we don’t need to be worry?! I still think we should try to avoid retrieving data we don’t need from the database.

How can we solve the above issue with IEnumerable<T>?

We can for example get the query passed down to our DomainService and pass it to our Repositories, in that case we can use it as a Query Object. We can get the Query information by overriding the Query method of the DomainService class. Another solution is to create our own Query Object and pass it to the server as an argument to the GetCustomers method or just simply use the IQueryable<T> interface.

How can IQueryable<T> be used?

When using Entity Framework or Linq To SQL etc, we can create a query, this query will only be executed when we tell it to, for example calling the ToList method, Single etc of the query object:

var customersQuery = from customer in myContext.Customers
                     where customter.Country == "Sweden"
                     orderby customer.CompanyName
select customer; var results = customersQuery.ToList(); //Execute the query here


As long as we don’t execute the customerQuery, we can extend the query, when we are done we execute it. When WCF RIA Services uses the EF or L2S Domain Service classes, it will only return the query (in this case use the IQueryable<T>) and not a list of objects, so when we use the Load method and pass down a query, the query will extend the returned query, and then WCF RIA Services will invoke the query and  EF and L2S will turn the query into T-SQL and execute it against the database. In that way we can reduce the data returned from our database to the DomainSerivce.

This sounds great, let us always return IQueryable<T>!

But! and a big one, we can’t use this solution in a RIA where business logic should be on the server-side. In some cases we may need to perform some business logic based on the data retrieved from the database, in this case we need to execute the query before returning our data to the client. So in this case we need to use IEnumerable<T>. But (a lot of but here), if we don’t need to perform any business logic on the server-side before we return our objects to the client, we can use the IQueryable<T>, I think it’s a nice solution for small client/server apps. It requires that the objects we want to send to the client are well designed and with the View in consideration. If we use the IQueryable<T>, we can’t use the Repository pattern, instead we can use a Query provider. So the Repository turns into a query provider:

public class CustomerQueryProvider : ICustomerQueryProvider
{
     public IQueryable<Customer> GetCustomers()
     {
           //...
     }
}

 

The CustomerQueryProvider can for example use EF or L2S to return an IQueryable<T> of customers:

public IQueryable<Customer> GetCustomers()
{
    _myObjectContext.Customers;
}

 

The DomainService will look like this:

 

[EnableClientAccess()]
public class CustomerService : DomainService
{
     private ICustomerQueryProvider _customerQueryProvider;

     public CustomerService(ICustomerQueryProvider customerQueryProvider)
     {
         _customerQueryPrivider = customerQueryProvider;
     }

     public IQueryable<Customer> GetCustomers()
     {
         //Use _customerQueryProvider.GetCustomers()
      }
}


The reason why a Query Provider is used here, is to remove dependencies to a specific detail. Now we can have different Query Providers as long as they implements the ICustomerQueryProvider interface and the infrastructure used in the Query Provider supports the IQueryable<T> interface.

Summary

For small database driven RIA where almost not business logic is needed, we can get a lot of benefits of using the IQueryable<T> interface instead of the IEnumerable<T>. But in a enterprise RIA where business logic should take place on the server-side, the IEnumerable<T> can be a better choice. But it depends! There are so many factors that must be known before we can know which design will fit best.

If you want to know when I publish new blog posts, you can follow me on twitter: http://www.twitter.com/fredrikn

2 Comments

  • I think it´s better to just return IEnumerable instead.

    Why just not use something like this, which will do the same?

    public IEnumerable GetCustomers(int startIndex, int sum)
    {
    return ctx.Customers().Skip(startIndex).Take(sum).AsEnumerable();
    }

    Or maybe pageIndex instead of startIndex.

    I think that looks better.


  • @wimpyboy:
    What you refer to now, is some sort of query object solution, but you use several arguments instead of on single query object. I do prefer IEnumerable&lt;T&gt;, but IQueryable&lt;T&gt; can solve some problems, but shouldn't be used as a Silver bullet, instead be used wary carefully.

Comments have been disabled for this content.