title: Ensuring inserts after a call to a custom NHibernate IIdentifierGenerator
body: <h2>The setup</h2>
  
  <p>Some of the "old old old" tables of our database use an exotic primary key generation scheme [1] and I'm trying to overlay this part of the database with NHibernate. This generation scheme is mostly hidden away in a stored procedure called, say, 'ShootMeInTheFace.GetNextSeededId'.</p>
  
  <p>I have written an <code>IIdentifierGenerator</code> that calls this stored proc:</p>
  
  <pre><code>public class LegacyIdentityGenerator : IIdentifierGenerator, IConfigurable
  {
      // ... snip ...
      public object Generate(ISessionImplementor session, object obj)
      {
          var connection = session.Connection;
  
          using (var command = connection.CreateCommand())
          {
              SqlParameter param;
  
              session.ConnectionManager.Transaction.Enlist(command);
  
              command.CommandText = "ShootMeInTheFace.GetNextSeededId";
              command.CommandType = CommandType.StoredProcedure;
  
              param = command.CreateParameter() as SqlParameter;
              param.Direction = ParameterDirection.Input;
              param.ParameterName = "@sTableName";
              param.SqlDbType = SqlDbType.VarChar;
              param.Value = this.table;
              command.Parameters.Add(param);
  
              // ... snip ...
  
              command.ExecuteNonQuery();
  
              // ... snip ...
  
              return ((IDataParameter)command
                  .Parameters["@sTrimmedNewId"]).Value as string);
       }
  }
  </code></pre>
  
  <h2>The problem</h2>
  
  <p>I can map this in the XML mapping files and it works great, BUT....</p>
  
  <p><strong>It doesn't work when NHibernate tries to batch</strong> inserts, such as in a cascade, or when the session is not <code>Flush()</code>ed after every call to <code>Save()</code> on a transient entity that depends on this generator.</p>
  
  <p>That's because NHibernate seems to be doing something like</p>
  
  <pre><code>for (each thing that I need to save)
  {
      [generate its id]
      [add it to the batch]
  }
  
  [execute the sql in one big batch]
  </code></pre>
  
  <p>This doesn't work because, since the generator is asking the database every time, NHibernate just ends up getting the same ID generated multiple times, <em>since it hasn't actually saved anything yet.</em></p>
  
  <p>The other NHibernate generators like <code>IncrementGenerator</code> seem to get around this by asking the database for the seed value once and then incrementing the value <em>in memory</em> during subsequent calls in the same session. I would rather not do this in my implementation if I have to, since all of the code that I need is sitting in the database already, just waiting for me to call it correctly.</p>
  
  <ul>
  <li>Is there a way to make NHibernate actually issue the INSERT after each call to generating an ID for entities of a certain type? Fiddling with the batch size settings don't seem to help.</li>
  <li>Do you have any suggestions/other workarounds besides re-implementing the generation code in memory or bolting on some triggers to the legacy database? I guess I could always treat these as "assigned" generators and try to hide that fact somehow within the guts of the domain model....</li>
  </ul>
  
  <p>Thanks for any advice.</p>
  
  <h2>The update: 2 months later</h2>
  
  <p>It was suggested in the answers below that I use an <code>IPreInsertEventListener</code> to implement this functionality. While this sounds reasonable, there were a few problems with this.</p>
  
  <p>The first problem was that setting the <code>id</code> of an entity to the <code>AssignedGenerator</code> and then not actually assigning anything in code (since I was expecting my new <code>IPreInsertEventListener</code> implementation to do the work) resulted in an exception being thrown by the <code>AssignedGenerator</code>, since its <code>Generate()</code> method essentially does nothing but check to make sure that the <code>id</code> is not null, throwing an exception otherwise. This is worked around easily enough by creating my own <code>IIdentifierGenerator</code> that is like <code>AssignedGenerator</code> without the exception.</p>
  
  <p>The second problem was that returning null from my new <code>IIdentifierGenerator</code> (the one I wrote to overcome the problems with the <code>AssignedGenerator</code> resulted in the innards of NHibernate throwing an exception, complaining that a null id was generated. Okay, fine, I changed my <code>IIdentifierGenerator</code> to return a sentinel string value, say, "NOT-REALLY-THE-REAL-ID", knowing that my <code>IPreInsertEventListener</code> would replace it with the correct value.</p>
  
  <p>The third problem, and the ultimate deal-breaker, was that <code>IPreInsertEventListener</code> runs so late in the process that you need to update both the actual entity object as well as an array of state values that NHibernate uses. <a href="http://ayende.com/Blog/archive/2009/04/29/nhibernate-ipreupdateeventlistener-amp-ipreinserteventlistener.aspx" rel="noreferrer">Typically this is not a problem and you can just follow Ayende's example.</a> But there are three issues with the <code>id</code> field relating to the <code>IPreInsertEventListeners</code>:</p>
  
  <ul>
  <li>The property is not in the <code>@event.State</code> array but instead in its own <code>Id</code> property.</li>
  <li>The <code>Id</code> property does not have a public <code>set</code> accessor.</li>
  <li>Updating only the entity but not the <code>Id</code> property results in the "NOT-REALLY-THE-REAL-ID" sentinel value being passed through to the database since the <code>IPreInsertEventListener</code> was unable to insert in the right places.</li>
  </ul>
  
  <p>So my choice at this point was to use reflection to get at that NHibernate property, or to really sit down and say "look, the tool just wasn't meant to be used this way."</p>
  
  <p>So I went back to my original <code>IIdentifierGenreator</code> and made it work for lazy flushes: it got the high value from the database on the first call, and then I re-implemented that ID generation function in C# for subsequent calls, modeling this after the <code>Increment</code> generator:</p>
  
  <pre><code>private string lastGenerated;
  
  public object Generate(ISessionImplementor session, object obj)
  {
      string identity;
  
      if (this.lastGenerated == null)
      {
           identity = GetTheValueFromTheDatabase();
      }
      else
      {
           identity = GenerateTheNextValueInCode();
      }
  
      this.lastGenerated = identity;
  
      return identity;
  }
  </code></pre>
  
  <p>This seems to work fine for a while, but like the <code>increment</code> generator, we might as well call it the TimeBombGenerator. If there are multiple worker processes executing this code in non-serializable transactions, or if there are multiple entities mapped to the same database table (it's an old database, it happened), then we will get multiple instances of this generator with the same <code>lastGenerated</code> seed value, resulting in duplicate identities.</p>
  
  <p>@#$@#$@.</p>
  
  <p>My solution at this point was to make the generator cache a dictionary of <code>WeakReference</code>s to <code>ISessions</code> and their <code>lastGenerated</code> values. This way, the <code>lastGenerated</code> is effectively local to the lifetime of a particular <code>ISession</code>, not the lifetime of the <code>IIdentifierGenerator</code>, and because I'm holding <code>WeakReferences</code> and culling them out at the beginning of each <code>Generate()</code> call, this won't explode in memory consumption. And since each <code>ISession</code> is going to hit the database table on its first call, we'll get the necessary row locks (assuming we're in a transaction) we need to prevent duplicate identities from happening (and if they do, such as from a phantom row, only the <code>ISession</code> needs to be thrown away, not the entire process).</p>
  
  <p>It is ugly, but more feasible than changing the primary key scheme of a 10-year-old database. FWIW.</p>
  
  <hr />
  
  <p>[1] If you care to know about the ID generation, you take a substring(len - 2) of all of the values currently in the PK column, cast them to integers and find the max, add one to that number, add all of that number's digits, and append the sum of those digits as a checksum. (If the database has one row containing "1000001", then we would get max 10000, +1 equals 10001, checksum is 02, resulting new PK is "1000102". <strong>Don't ask me why.</strong></p>