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