Lesser-Known NHibernate Features: Result Transformers

A result transformer, in NHibernate, is some class that implements the IResultTransformer interface:

   1: public interface IResultTransformer
   2: {
   3:     IList TransformList(IList collection);
   4:     Object TransformTuple(Object[] tuple, String[] aliases);
   5: }

Most query APIs, except LINQ, support specifying a result transformer. So, what is a result transformer used for? Just what the name says: it turns the values obtained from a query into some object. Normally, we just let NHibernate transform these values into instances of our entities, but we may want to do something different, either because we haven’t mapped some class that we want to use, or because we are not returning all of the entity’s properties, etc.

NHibernate includes some result transformers:

  • AliasToBeanResultTransformer: allows to transform a result to a user specified class which will be populated via setter methods or fields matching the alias names;
  • AliasToBeanConstructorResultTransformer: identical to AliasToBeanResultTransformer, but we specify a constructor for creating new instances of the target class;
  • AliasToEntityMapResultTransformer: returns a dictionary where the keys are the aliases and the values the corresponding columns;
  • AliasedTupleSubsetResultTransformer: ignores a tuple element if its corresponding alias is null;
  • CacheableResultTransformer: used to transform tuples to a value(s) that can be cached;
  • DistinctRootEntityResultTransformer: for joined queries, returns distinct root entities only;
  • PassThroughResultTransformer: just returns the row as it was obtained from the database;
  • RootEntityResultTransformer; returns the root entity of a joined query;
  • ToListResultTransformer: transforms each result row from a tuple into a IList, such that what you end up with is a IList of ILists.


All of these can be obtained from static properties in class NHibernate.Transform.Transformers. NHibernate implicitly uses some of these, for example, LINQ queries always use DistinctRootEntityResultTransformer.

It is easy to build our own transformer. Have a look at the following example:

   1: public class ExpressionsResultTransformer : IResultTransformer
   2: {
   3:     private readonly Type type;
   4:     private readonly Func<Object> constructorFunc;
   5:     private readonly ConstructorInfo constructor;
   6:     private readonly Object[] parameters;
   7:     private readonly Dictionary<Int32, String> expressions = new Dictionary<Int32, String>();
   8:     private PropertyDescriptorCollection props;
   9:  
  10:     public ExpressionsResultTransformer(ConstructorInfo constructor, params Object[] parameters)
  11:     {
  12:         this.constructor = constructor;
  13:         this.parameters = parameters;
  14:     }
  15:  
  16:     public ExpressionsResultTransformer(Func<Object> constructorFunc)
  17:     {
  18:         this.constructorFunc = constructorFunc;
  19:     }
  20:  
  21:     public ExpressionsResultTransformer(Type type)
  22:     {
  23:         this.type = type;
  24:     }
  25:  
  26:     private Object CreateInstance()
  27:     {
  28:         if (this.type != null)
  29:         {
  30:             return (Activator.CreateInstance(this.type));
  31:         }
  32:         else if (this.constructorFunc != null)
  33:         {
  34:             return (this.constructorFunc());
  35:         }
  36:         else
  37:         {
  38:             return (this.constructor.Invoke(this.parameters));
  39:         }
  40:     }
  41:  
  42:     public ExpressionsResultTransformer Add(Int32 index, String property)
  43:     {
  44:         this.expressions[index] = property;
  45:         return (this);
  46:     }
  47:  
  48:     public ExpressionsResultTransformer Add(params String[] properties)
  49:     {
  50:         foreach (var property in properties)
  51:         {
  52:             this.Add(property);
  53:         }
  54:  
  55:         return (this);
  56:     }
  57:  
  58:     public ExpressionsResultTransformer Add(String property)
  59:     {
  60:         var max = this.expressions.Keys.DefaultIfEmpty(-1).Max();
  61:         return (this.Add(max + 1, property));
  62:     }
  63:  
  64:     public static ExpressionsResultTransformer Add<T>(params String[] properties) where T : new()
  65:     {
  66:         return (new ExpressionsResultTransformer<T>().Add(properties));
  67:     }
  68:  
  69:     #region IResultTransformer Members
  70:  
  71:     IList IResultTransformer.TransformList(IList collection)
  72:     {
  73:         return (collection);
  74:     }
  75:  
  76:     Object IResultTransformer.TransformTuple(Object[] tuple, String[] aliases)
  77:     {
  78:         var entity = this.CreateInstance();
  79:  
  80:         if (this.props == null)
  81:         {
  82:             this.props = TypeDescriptor.GetProperties(entity);
  83:         }
  84:  
  85:         foreach (var expression in this.expressions)
  86:         {
  87:             this.props[expression.Value].SetValue(entity, tuple[expression.Key]);
  88:         }
  89:  
  90:         return (entity);
  91:     }
  92:  
  93:     #endregion
  94: }
  95:  
  96: public sealed class ExpressionsResultTransformer<T> : ExpressionsResultTransformer where T : new()
  97: {
  98:     public ExpressionsResultTransformer() : base(typeof(T))
  99:     {
 100:     }
 101:  
 102:     public ExpressionsResultTransformer(Func<T> constructor) : base(new Func<Object>(() => (Object) constructor()))
 103:     {
 104:     }
 105:  
 106:     public ExpressionsResultTransformer(ConstructorInfo constructor, params Object[] parameters) : base(constructor, parameters)
 107:     {
 108:     }
 109: }

The TransformTuple method is the one used to turn each returned record into an instance of something. TransformList is called at the end, when all the records have been processed.

The ExpressionResultTransformer class allows us to select which indexes, in the database record, map to which properties in some entity. For our convenience, it offers a number of options to construct an instance (type, constructor + parameters and delegate). We would use it like this:

   1: var products = session.CreateSQLQuery("select p.Name, p.Price, p.ProductId from Product p").SetResultTransformer(ExpressionsResultTransformer.Add<Product>("Name", "Price", "ProductId")).List<Product>();

                             

No Comments

Add a Comment

As it will appear on the website

Not displayed

Your website