Adding Support for ISupportInitialize in NHibernate

The .NET ISupportInitialize interface is used when we want to support staged initialization for objects. Its BeginInit method is called when initialization is about to start and EndInit when it is finished.

If we want, it is easy to add support for it in NHibernate. An option would be:

  • BeginInit is called when the object is instantiated, like when NHibernate has loaded a record from the database and is about to hydrate the entity, and immediately after the Id property is set;
  • EndInit is called after all properties are set.

We do this by using a custom interceptor, like we have in the past. We start by writing a class that inherits from EmptyInterceptor, and implements the listener interface for the PostLoad event, IPostLoadEventListener:

   1: public sealed class SupportInitializeInterceptor : EmptyInterceptor, IPostLoadEventListener

   2: {

   3:     private ISession session = null;

   4:  

   5:     public override void SetSession(ISession session)

   6:     {

   7:         this.session = session;

   8:         base.SetSession(session);

   9:     }

  10:  

  11:     public override Object Instantiate(String clazz, EntityMode entityMode, Object id)

  12:     {

  13:         var listeners = (this.session.SessionFactory as SessionFactoryImpl).EventListeners;

  14:         var metadata = this.session.SessionFactory.GetClassMetadata(clazz);

  15:         var proxy = metadata.Instantiate(id, entityMode);

  16:         var initializable = proxy as ISupportInitialize;

  17:  

  18:         if (initializable != null)

  19:         {

  20:             initializable.BeginInit();

  21:         }

  22:  

  23:         if (listeners.PostLoadEventListeners.OfType<SupportInitializeInterceptor>().Any() == false)

  24:         {

  25:             listeners.PostLoadEventListeners = listeners.PostLoadEventListeners.Concat(new IPostLoadEventListener[] { this }).ToArray();

  26:         }

  27:  

  28:         return (proxy);

  29:     }

  30:  

  31:     #region IPostLoadEventListener Members

  32:  

  33:     void IPostLoadEventListener.OnPostLoad(PostLoadEvent @event)

  34:     {

  35:         var initializable = @event.Entity as ISupportInitialize;

  36:  

  37:         if (initializable != null)

  38:         {

  39:             initializable.EndInit();

  40:         }

  41:     }

  42:  

  43:     #endregion

  44: }

Then, before creating a session factory, we need to register it in the Configuration instance:

   1: var sessionFactory = cfg.SetInterceptor(new SupportInitializeInterceptor()).BuildSessionFactory();

Now, if your entity implements ISupportInitialize, NHibernate will automagically call its methods at the proper time. As simple as this! Winking smile

Lesser-Known NHibernate Features – Generating Database Scripts

As you may know, NHibernate knows how to generate the database for you from its model. Any of the supported databases will do.

What you may not know is that you can simply generate the SQL scripts that would be used to either generate the database from scratch or just update it to match the model as it is. For that you use the SchemaExport and SchemaValidator classes.

SchemaValidator takes a Configuration instance and basically throws an exception if, when its Validate method is called, the database does not match the model.

SchemaExport has far more options; it can:

  • Create or drop a database model based on the entity model;
  • Output the SQL to a file, either executing it at the same time or not;
  • Execute the SQL to another arbitrary database connection;
  • Execute a custom action after each line of the generated SQL script.

An example:

   1: var validator = new SchemaValidator(cfg);

   2:  

   3: try

   4: {

   5:     validator.Validate();

   6: }

   7: catch

   8: {

   9:     var export = new SchemaExport(cfg).SetOutputFile("Script.sql");

  10:  

  11:     //send the script to standard output and execute it

  12:     export.Execute(useStdOut: true, execute: true, justDrop: false);

  13:     //or

  14:     //send all lines of the script to the System.Console.WriteLine method

  15:     export.Execute(scriptAction: System.Console.WriteLine, execute: false, justDrop: false);

  16: }

NHibernate Pitfalls: Versioned Entities Are Not Batcheable

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

This is a problem similar to the one with native id generators. Basically, because there are different versioning strategies – timestamp, counter, native, etc, – NHibernate needs to issue a SELECT after a versioned entity is INSERTEd or UPDATEd. While this wouldn’t be necessary when NHibernate manages the version on the “client side”, like:

   1: UPDATE MyEntity SET Version = 2 WHERE ...;

   2: //or

   3: UPDATE MyEntity SET Version = GETDATE() WHERE ...;

But not if the version is handled on the database side, like when using SQL Server’s ROWVERSION/TIMESTAMP columns or Oracle’s ORA_ROWSCN pseudo-columns. In these cases, NHibernate needs to issue a SELECT after each INSERT or UPDATE:

   1: UPDATE MyEntity SET ... WHERE ...;

   2: SELECT Version FROM MyEntity WHERE ...;

This breaks batching, because it needs to be done immediately after each INSERT/UPDATE.