NHibernate Pitfalls: Refreshing Manually Created Entities Does Not Bring Lazy Properties of Base Types

This is part of a series of posts about NHibernate Pitfalls. See the entire collection here.

Suppose you have some properties of an entity, including its id, and you want to refresh its state from the database:

   1: var product = new Product { ProductId = 1 };
   2:  
   3: //product.Image is null
   4:  
   5: session.Refresh(product);
   6:  
   7: //product.Image is null

Image is a Byte[] and is configured as lazy. The problem is that NHibernate is not capable of returning a proxy for it, because the entity itself is not a proxy.

This does not happen for associated entities (one-to-one, many-to-one, one-to-many and many-to-many):

   1: var order = new Order { OrderId = 1 };
   2:  
   3: //order.Customer is null
   4:  
   5: session.Refresh(order);
   6:  
   7: //order.Customer is a proxy and accessing the property will load it

In this case, Customer is another entity, and NHibernate can assign the Order.Customer property a proxy to it.

Because of this problem, I created a simple extension method that loads all properties. It is even smart enough to use proxies, if we so require it:

   1: public static void InitializeLazyProperties(this ISession session, Object entity, Boolean useProxyWhenPossible = true)
   2: {
   3:     if (entity.IsProxy() == true)
   4:     {
   5:         //if entity is a proxy, use the default mechanism
   6:         NHibernateUtil.Initialize(entity);
   7:     }
   8:     else
   9:     {
  10:         var metadata = session.SessionFactory.GetClassMetadata(entity.GetType());
  11:         var propertiesToLoad = new List<String>();
  12:  
  13:         for (var i = 0; i < metadata.PropertyNames.Length; ++i)
  14:         {
  15:             if (metadata.GetPropertyValue(entity, metadata.PropertyNames[i], session.ActiveEntityMode) == null)
  16:             {
  17:                 if ((metadata.PropertyTypes[i].IsEntityType == false) || (useProxyWhenPossible == false))
  18:                 {
  19:                     //either load the value
  20:                     propertiesToLoad.Add(metadata.PropertyNames[i]);
  21:                 }
  22:                 else
  23:                 {
  24:                     //or the id of the associated entity
  25:                     propertiesToLoad.Add(String.Concat(metadata.PropertyNames[i], ".id"));
  26:                 }
  27:             }
  28:         }
  29:  
  30:         var hql = new StringBuilder();
  31:         hql.Append("select ");
  32:         hql.Append(String.Join(", ", propertiesToLoad));
  33:         hql.AppendFormat(" from {0} where id = :id", entity.GetType());
  34:  
  35:         var query = session.CreateQuery(hql.ToString());
  36:         query.SetParameter("id", metadata.GetIdentifier(entity, session.ActiveEntityMode));
  37:  
  38:         var result = query.UniqueResult();
  39:         var values = result as Object[] ?? new Object[] { result };
  40:  
  41:         for (var i = 0; i < propertiesToLoad.Count; ++i)
  42:         {
  43:             var parts = propertiesToLoad[i].Split('.');
  44:             var value = values[i];
  45:             var propertyName = parts.First();
  46:  
  47:             if (parts.Length > 0)
  48:             {
  49:                 var propertyIndex = Array.IndexOf(metadata.PropertyNames, propertyName);
  50:                 var propertyType = metadata.PropertyTypes[propertyIndex].ReturnedClass;
  51:  
  52:                 //create a proxy
  53:                 value = session.Load(propertyType, values[i]);
  54:             }
  55:  
  56:             metadata.SetPropertyValue(entity, propertyName, value, session.ActiveEntityMode);
  57:         }
  58:     }
  59: }

Of course, it requires that at leat the id property is set. It can be used as:

   1: var order = new Order { OrderId = 360448 };
   2:  
   3: session.InitializeLazyProperties(order);

Have fun! Winking smile

                             

No Comments

Add a Comment

As it will appear on the website

Not displayed

Your website