In my project I am using the following approach to querying data from the database:
- Use a generic repository that can return any type and is not bound to one type, i.e.
IRepository.Get<T>instead ofIRepository<T>.Get. NHibernatesISessionis an example of such a repository. -
Use extension methods on
IQueryable<T>with a specificTto encapsulate recurring queries, e.g.public static IQueryable<Invoice> ByInvoiceType(this IQueryable<Invoice> q, InvoiceType invoiceType) { return q.Where(x => x.InvoiceType == invoiceType); }
Usage would be like this:
var result = session.Query<Invoice>().ByInvoiceType(InvoiceType.NormalInvoice);
Now assume I have a public method I want to test that uses this query. I want to test the three possible cases:
- The query returns 0 invoices
- The query returns 1 invoice
- The query returns multiple invoices
My problem now is: What to mock?
- I can’t mock
ByInvoiceTypebecause it is an extension method, or can I? - I can’t even mock
Queryfor the same reason.
After some more research and based on the answers here and on these links, I decided to completely re-design my API.
The basic concept is to completely disallow custom queries in the business code. This solves two problems:
IQueryable<T>and which are not.In the business code, a query now looks like this:
In practice this is implemented like this:
As Vytautas Mackonis suggested in his answer, I am no longer depending directly on NHibernate’s
ISession, instead I am now depending on anIRepository.This interface has a property named
Queryof typeIQueries. For each entity the business layer needs to query there is a property inIQueries. Each property has its own interface that defines the queries for the entity. Each query interface implements the genericIQuery<T>interface which in turn implementesIEnumerable<T>, leading to the very clean DSL like syntax seen above.Some code:
This fluent querying syntax allows the business layer to combine the supplied queries to take full advantage of the underlying ORM’s capabilities to let the database filter as much as possible.
The implementation for NHibernate would look something like this:
In my real implementation I extracted most of the infrastructure code into a base class, so that it becomes very easy to create a new query object for a new entity. Adding a new query to an existing entity is also very simple.
The nice thing about this is that the business layer is completely free of querying logic and thus the data store can be switched easily. Or one could implement one of the queries using the criteria API or get the data from another data source. The business layer would be oblivious to these details.