Monday, August 8, 2011

NHibernate is testable too

On my last post regarding Entity Framework is infinitely testable, we can also do that on NHibernate too. We just need to suppress NHibernate from performing its fetching function when the provider of the given IQueryable is not of NHibernate type.

This is the code to accomplish that:

public static class UnitTestFriendlyNhFetching
{
    public static IQueryable<TOriginating> NhxFetch<TOriginating, TRelated>(this IQueryable<TOriginating> query, Expression<Func<TOriginating, TRelated>> relatedObjectSelector)        
    {
        // Prior to NHibernate 3.2, use NhQueryProvider
        // (query.Provider is NHibernate.Linq.NhQueryProvider)

        if (query.Provider is NHibernate.Linq.DefaultQueryProvider)
            return query.Fetch(relatedObjectSelector);
        else
            return query;
    }

    
    public static IQueryable<TOriginating> NhxFetchMany<TOriginating, TRelated>(this IQueryable<TOriginating> query, Expression<Func<TOriginating, IEnumerable<TRelated>>> relatedObjectSelector)
    {
        // Prior to NHibernate 3.2, use NhQueryProvider
        // (query.Provider is NHibernate.Linq.NhQueryProvider)

        if (query.Provider is NHibernate.Linq.DefaultQueryProvider)
            return query.FetchMany(relatedObjectSelector);
        else
            return query;
    }
}


Example use:

public class MovieController : Controller
{
    IRepository<Movie> _rm = null;
 
    public HomeController(IRepository<Movie> rm)
    {            
        _rm = rm;
    }
 
    public ViewResult Index()
    {        
        // NHibernate NhxFetchMany won't complain even if _rm.All came from mocked IQueryable only
        return View(_rm.All.Where(x => x.Title == "Thor")
            .NhxFetchMany(x=> x.Quotes));    
    }    
}

This is the repository interface
public interface IRepository<Ent> where Ent : class
{
    IQueryable<Ent> All { get; }
    void Save(Ent ent, byte[] version);
    void VersionedDelete(object id, byte[] version);
    Ent LoadStub(object id);
 
    string PrimaryKeyName { get; set; }
    string VersionName { get; set; }
}

The code above can work on aggregate root only, i.e. it can only fetch the children and parent of a given aggregate root, but it cannot further fetch the grandchildren of an aggregate root; ThenFetch and ThenFetchMany can only be chained after Fetch and FetchMany.

If we can find how to cast IQueryable to NHibernate's INhFetchRequest<TOriginating, TRelated>, then we can include unit testing-friendly extension methods for grandchildren entities

No comments:

Post a Comment