It looks like NH gets MAX(ID) only once, at first insert and then stores this value internally, this causes me some problems when other processes inserts data. Then I have not actual ID and duplicate key exception is thrown.
Lets imagine we have table Cats
CREATE TABLE Cats(ID int, Name varchar(25))
Then we have corresponding mapping done with FluentNhibernate
public class CatMap : ClassMap<Cat>
{
public CatMap()
{
Id(m=>m.ID).GeneratedBy.Increment();
Map(m=>.Name);
}
}
All I want to achieve is to insert my Cat records with ID’s generated by NHibernate using SELECT MAX(ID) FROM Cats before any insert. Executing Session.Flush after any commit dosnt work. I’v done some investigation using SQL Server profiler, and this sql stetement is executed only once (at first insert) – other inserts doesnt force to retreive actual MAX(ID). I know that other algorithms like HiLo are better, but I cant replace it.
As you found out, the NHibernate Increment id generator was not intended for use in a multi-user environment. You state that using a HiLo generator is not an option so you’re left with these options:
use the Native generator and change the id column to use the database supported identity mechanism
use the Assigned generator and write code to determine the next valid id
create a Custom generator where you implement the IIdentifierGenerator interface to do what you need
Below is sample code for a custom generator that uses a generalized proc to get an ID for a given table. The main issue with this approach is that you must wrap the code in something like a Unit of Work pattern to ensure the ‘select max(id) …” and the insert are covered by the same database transaction. The IIdentifierGenerator link has the XML mapping you need to wire up this custom generator.