NHibernate.Criterion Extensions workaround

I’ve been working with Fluent NHibernate for the past month or so, and I realized while writing an NHibernate query that the NHibernate Criterion’s Restrictions class isn’t very refactor-friendly. The refactorability of Fluent NHibernate is ultimately why we’ve decided to use it. It does take a little longer to set up than some other ORMs, but in the long-run it’s nice to be able to change a property name and refactor.

… except the property names are hard-coded in the Criteria!

My workaround for this is to use a bit of static reflection a-la-FNH, and maintain code that is easily refactored.

For instance, I’ve created a static utility class (Showing the Restrictions.Eq() substitute):

public static class CriterionExtensions
    {
        public static SimpleExpression Eq<T>(
                Expression<Func<T, object>> exp, object value
        )
        {
            var memberExpression = GetMemberExpression(exp);
            string propertyName = ((PropertyInfo)memberExpression.Member).Name;
            if (!string.IsNullOrEmpty(propertyName))
                return Restrictions.Eq(propertyName, value);
            else
                return null;
        }

        private static MemberExpression GetMemberExpression<T>(
                Expression<Func<T, object>> expression
        )
        {
            MemberExpression memberExpression = null;
            if (expression.Body.NodeType == ExpressionType.Convert)
            {
                var body = (UnaryExpression)expression.Body;
                memberExpression = body.Operand as MemberExpression;
            }
            else if (expression.Body.NodeType == ExpressionType.MemberAccess)
            {
                memberExpression = expression.Body as MemberExpression;
            }
            if (memberExpression == null)
            {
                throw new ArgumentException("Not a member access", "member");
            }
            return memberExpression;
        }
    }

And to use this code, you can create an aliased using directive and call it in code:

using Ensure = MyNamespace.Extensions.Criterion.CriterionExtensions;
    /* class declarations and whatnot */
     internal IEnumerable<Product> GetAll(int shelfNumber)
      {
          var session = SessionManager.GetCurrentSession();
          return session.CreateCriteria<Product>()
              .Add(Ensure.Eq<Product>(x => x.ShelfNumber, shelfNumber))
              .List<Product>();
      }

It’s pretty simple to use and is easily refactored. You may be able to add your *extensions* to a namespace: NHibernate.Criterion and call your class Restrictions, but I didn’t try this (I don’t like mixing namespaces). Let me know what you think!

Related Articles