views:

102

answers:

2

Usually I'd put my criterias/hql queries in a repository/dal class related to the entity, but lately I'be been thinking of adding another abstraction that represents what a query is, this would give me the possibility of adding common behavior to all queries (e.g. pagination) in a base class, etc.

so these are my components now;

generic interface not related to nhibernate:

public interface IQuery<T>
{
    IList<T> List();
    T Single();
}

Example implementation of a Criteria based query, something similar could be done with an Hql query, or a nhibernate-linq query

public abstract class CriteriaQuery<T>: IQuery<T>
{
    [Inject]
    public ISessionFactory SessionFactory { protected get; set; }

    protected ISession Session
    {
        get { return SessionFactory.GetCurrentSession(); }
    }

    protected abstract ICriteria Configure(ICriteria criteria);

    [Transaction]
    public virtual IList<T> List()
    {
        var criteria = Session.CreateCriteria(typeof (T));

        return Configure(criteria)
                 .List<T>();
    }

    [Transaction]
    public virtual T Single()
    {
        return Configure(Session.CreateCriteria(typeof(T)))
                .UniqueResult<T>();
    }
}

and here a domain specific query would look like:

public interface IGetVideosQuery: IQuery<Video>
{
    IGetVideosQuery Page(int index);
    IGetVideosQuery PageSize(int pageSize);

    IGetVideosQuery AllTime { get; }
    IGetVideosQuery Today { get; }
    IGetVideosQuery LastWeek { get; }
}

any thoughts on this? possible problems you see I might come across? Thanks!

+1  A: 

My first suggestion would be to go with Linq2NH, available in NHibernate 2.1 and beyond. The Session exposes an AsQueryable<T>() method on which you can build a Linq query. That will allow you to attach criteria and projections as you please using standard Linq extension methods, including Skip() and Take() to implement pagination (myQuery.Skip(PageIdx*PageSize).Take(PageSize).ToList()), and Where clauses to filter. The lambdas and variables used in those queries could be encapsulated in some sort of QueryInfo class, allowing repeatable, persistable queries.

KeithS
+2  A: 

I took a different path, that of CQS. What this does is that it separates my mutating logic from my query logic.

Now, there are different ideas on how to implement this, and I choose this one:

  • All my mutation logic is activated using command like DeactivateUser and ChangeRelationAddress. For this business logic, I have normal repositories like you describe;

  • For displaying data, I use a completely managed system. With this system, I describe queries like the Specification Pattern. I describes the base table and the fields. The query system automatically creates the joins for me and with filter definitions I provide filters.

This system allows me to keep the complexity of my repositories down because I do not have to think about filters a user may set or ORDER BY's. The system that displays this data automatically creates the filters using Criteria and handles paging for me.

Maybe such a system can work for you.

Pieter