I ran into some unexpected behavior related to NHibernate and NHibernate Search. I was getting an error that looks like this:

An AssertionFailure occurred - this may indicate a bug in NHibernate or in your custom types.
NHibernate.AssertionFailure: collection [NS.Domain.GenericList.Items] was not processed by flush()

The Assertion Failure was occuring in code that was calling Flush() on domain objects that defined indexed AND lazy-loaded collection fields. Here is the code before the fix:

   1:  try
   2:  {
   3:      InternalDownload(session, netUtil);
   4:      Downloaded = DateTime.Now;
   5:      IsDownloaded = true;
   6:  }
   7:  catch (StopRunningException srex)
   8:  {
   9:      TraceWriter.LogException(srex,
  10:          string.Format("Stop Running Exception: [{0}]", DerivedID));
  11:                  
  12:      ErrorMessage = srex.Message;
  13:   
  14:      // Throw the exception out.
  15:      throw;
  16:   
  17:  }
  18:  catch (Exception ex)
  19:  {
  20:      TraceWriter.LogException(ex,
  21:          string.Format("Error Downloading: [{0}]", DerivedID));
  22:      ErrorMessage = ex.Message;
  23:      HasError = true;
  24:   
  25:      // Exception handled, processing will continue
  26:      // on the next item.
  27:  }
  28:  finally
  29:  {
  30:      session.Save(this);
  31:      session.Flush();
  32:  }

In Line 31, the call to Flush() was triggering the Lucene Indexing on the lazy-loaded collection fields. The fields were then being loaded during the Flush call. This is the root of the NHibernateAssertion failure. You can think of it as an error modifying a collection as you are enumerating it.

My workaround was to force eager loading on all collection fields prior to the call to Flush(). This avoids the assertion failure. Look at line 30 in the following code.

   1:  try
   2:  {
   3:      InternalDownload(session, netUtil);
   4:      Downloaded = DateTime.Now;
   5:      IsDownloaded = true;
   6:  }
   7:  catch (StopRunningException srex)
   8:  {
   9:      TraceWriter.LogException(srex,
  10:          string.Format("Stop Running Exception: [{0}]", DerivedID));
  11:                  
  12:      ErrorMessage = srex.Message;
  13:   
  14:      // Throw the exception out.
  15:      throw;
  16:   
  17:  }
  18:  catch (Exception ex)
  19:  {
  20:      TraceWriter.LogException(ex,
  21:          string.Format("Error Downloading: [{0}]", DerivedID));
  22:      ErrorMessage = ex.Message;
  23:      HasError = true;
  24:   
  25:      // Exception handled, processing will continue
  26:      // on the next item.
  27:  }
  28:  finally
  29:  {
  30:      EagerLoadAllFields();
  31:   
  32:      session.Save(this);
  33:      session.Flush();
  34:  }

 

EagerLoadAllFields() is a method defined in a base class. It's main purpose is on line 23, where it calls Count() on all the lazy-loaded collection fields. This triggers NHibernate to fetch the associations prior to the Flush().

   1:  static Dictionary<string, List<PropertyInfo>> s_eagerFieldCache = new Dictionary<string, List<PropertyInfo>>();
   2:  public override void EagerLoadAllFields()
   3:  {
   4:      lock (s_eagerFieldCache)
   5:      {
   6:          if (s_eagerFieldCache.ContainsKey(Discriminator) == false)
   7:          {
   8:              List<PropertyInfo> eagerFields = new List<PropertyInfo>();
   9:                      
  10:              foreach (var propInfo in GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public))
  11:              {
  12:                  if (DomainObj.GetDomainObjPropType(propInfo) == DomainObjPropType.GenericRefList)
  13:                      eagerFields.Add(propInfo);
  14:              }
  15:   
  16:              s_eagerFieldCache[Discriminator] = eagerFields;
  17:          }
  18:      }
  19:   
  20:      foreach (var propInfo in s_eagerFieldCache[Discriminator])
  21:      {
  22:          GenericRefList list = propInfo.GetValue(this, null) as GenericRefList;
  23:          list.Items.Count();
  24:      }
  25:  }

 

HTH