Try fast search NHibernate

08 July 2010

Enhanced Query Object

One of the annoying thing in our daily work is the modification of the Repository/DAO interface, and implementation, each time a new use-case, of our application, needs a new query.

The three solutions available so far

1.
The Query Object pattern was described by Martin Fowler. I like its short definition:
An object that represents a database query.
What I don’t like so much is its long description:
A Query Object is an interpreter [Gang of Four], that is, a structure of objects that can form itself into a SQL query.
A good example of Query Object is our dear ICriteria with all its classes. The problem is that I can’t expose neither ICriteria nor a concrete class using it because I don’t want expose nothing using NHibernate.
2.
Another possible solution is the Specifications Pattern (by Eric Evans and Martin Fowler).
Specification is again very powerful especially because easy to test. In .NET we have various implementations/interpretations based on LINQ. The advantage is basically that you can test your specification and your specification-composition in RAM… sure… hoping for that your LINQ provider can then efficiently translate it in SQL.
3.
The last is a repository implementing IQueriable (as proposed here) with its pro/cons:
  • Easy to test.
  • LINQ queries, for persistence, wrote every where.
  • Hope that your LINQ provider can then efficiently translate it in SQL.


The fact

In NHibernate we have at least six ways to query our persistence (have a look to this post). I can choose the query-system that best fit my needs of balance between code-readability, code-maintainability and query-performance.
Why I should limit myself in using just one ?
What I need is a solution to avoid the continuous modification of the repository’s interface maintaining the advantage NHibernate gives to me.


Enhanced Query Object

An object that represent a query. The responsibility of a EQO is returns the query results.
In general you will express a EQO as an interface placed in the same assembly where you are putting your IRepository interfaces. An example could look like:
public interface IContactByNameQuery
{
    string PartialFirstNameOrLastName    { get; set; }
    IPagedResults<Contact> GetAll(int pageSize, int pageNumber);
}
because the EQO is responsible to return the results of the query, in its implementation I can use whatever the persistence framework gives to me. I can add EQOs to my system without touch existing code. Because a EQO is an interface I can easily mock it. Perhaps I can’t test it quickly in RAM… perhaps… but in general I prefer to write integration tests (tests hitting a well-known-DataBase) instead test my queries using a fake-database.
The implementation of the above EQO, for NHibernate, could look as:
public class ContactByNameQuery : IContactByNameQuery
{
    private readonly ISessionFactory sessionFactory;

    public ContactByNameQuery(ISessionFactory sessionFactory)
    {
        this.sessionFactory = sessionFactory;
    }

    #region IContactByNameQuery Members

    public string PartialFirstNameOrLastName { get; set; }
    public IPagedResults<Contact> GetAll(int pageSize, int pageNumber)
    {
        var query = sessionFactory.GetCurrentSession().QueryOver<Contact>();
        if (!string.IsNullOrWhiteSpace(PartialFirstNameOrLastName))
        {
            query.Where(
                Restrictions.On<Contact>(c => c.FirstName).IsLike(PartialFirstNameOrLastName, MatchMode.Anywhere) |
                Restrictions.On<Contact>(c => c.LastName).IsLike(PartialFirstNameOrLastName, MatchMode.Anywhere)
                );
        }
        query.OrderBy(c => c.LastName).Asc.ThenBy(c => c.FirstName);

        var queryCount = query.ToRowCountQuery();

        var contacts = query.Take(pageSize).Skip((pageNumber -1) * pageSize).Future<Contact>();
        var count = queryCount.FutureValue<int>().Value;
        return new PagedResults<Contact>(count, contacts);
    }

    #endregion
}

but you are completely free to implement it using LINQ, HQL, Criteria, H-SQL, SQL, NamedQuery or whatever you feel comfortable with.

How create a concrete instance of EQO ?

Using exactly the same identical way you are using for your concrete implementation of Repositories or DAOs implementations.
Try it and let me know how you feel.

18 comments:

  1. I like it very much.
    It is the best way to abstract this concept.
    I have seem many unsecsuffull attempts to abstract in a generic way the criteria api and even hql!.. On the other hand it is hard to test the code when you have Linq spread all over.

    ReplyDelete
  2. Excelent! It is a great way of abstract de problem of touch DAOs, Repositories and a clean strategy for organize all queries.

    ReplyDelete
  3. By using a EQO based on Properties, perhaps the GetAll method will become very long and unreadable. What I'm doing is something like that but using Fluent interfaces, something like:

    contactSearcher.NewSearch().ByName(partialName).GetResults();

    The searcher in the NewSearch method creates a search class for that entity (a wrapper for the Criteria, HQL, etc..), then injects the session wrapper and then in the search class's fluent method, you add the restrictions.

    The ByName method would be something like:

    public IContactSearch ByName(string partialName){
    criteria.Add(Restrictions.Like("Name", partialName, MatchMode.Start));
    ...
    return this;
    }

    You can use the same approach to page results you use in your example.

    What do you think?

    ReplyDelete
  4. @Lenny
    perhaps to much work to do...
    You are defining your DSL, you have to define joins, order, OR and so on... seems a Criteria over Criteria.
    If you look at your system you will see that you end in certain query for specific use-case, in some cases you will have better results using MultiQuery/MultiCriteria... I'm feeling better with one-class, one-responsibility, one-resultset.

    ReplyDelete
  5. Seems fair enough... Sometimes my approach goes well, but as a query gets very specific, your approach may suit better, I'll give it a try.

    Thanks!

    ReplyDelete
  6. Cool post. Do you have a download sample?

    ReplyDelete
  7. @KJ
    what you need exactly ?
    I'm asking because the code of the example is already in the post.

    ReplyDelete
  8. Is there a mail id i can reach you offline?

    ReplyDelete
  9. @id
    sure, namesurname @ gmail.com

    ReplyDelete
  10. Nice, now we just need repositories to add objects to the database.

    ReplyDelete
  11. @Carlos Peix
    You can avoid the usage of plurals talking about "repository".

    ReplyDelete
  12. Isn't this just a specialized repository? The fact that the implementation resides in different classes is, well, an implementation detail and you could already achieve the same by applying the interface segregation principle by still having a single repository class.

    ReplyDelete
  13. There was others candidate names... one of those was "micro-repository".
    btw I found EQO as a good enough name basically because it match a big part of the Fowler's definition.

    ReplyDelete
  14. I came up with the same type of solution and i even went as far to create a framework for caching my individual EQO results. simply decorate the class with for example [CacheQuery(Hours = 1)] and transparent caching takes place.

    ReplyDelete
  15. does it do it only for full entities or projections as well?

    ReplyDelete