views:

321

answers:

2

I am experimenting with NHibernate (version 2.1.0.4000) with Fluent NHibernate Automapping.

My test set of entities persists fine with default integer IDs

I am now trying to use Guid IDs with the entities. Unfortunately changing the Id property to a Guid seems to stop NHibernate inserting objects.

Here is the entity class:

public class User
{
    public virtual int Id { get; private set; }
    public virtual string FirstName { get; set; }
    public virtual string LastName { get; set; }
    public virtual string Email { get; set; }
    public virtual string Password { get; set; }

    public virtual List<UserGroup> Groups { get; set; }

}

And here is the Fluent NHibernate configuration I am using:

        SessionFactory = Fluently.Configure()
                            //.Database(SQLiteConfiguration.Standard.InMemory)
                            .Database(MsSqlConfiguration.MsSql2008.ConnectionString(@"Data Source=.\SQLEXPRESS;Initial Catalog=NHibernateTest;Uid=NHibernateTest;Password=password").ShowSql())
                            .Mappings(m =>
                                m.AutoMappings.Add(
                                    AutoMap.AssemblyOf<TestEntities.User>()
                                            .UseOverridesFromAssemblyOf<UserGroupMappingOverride>()))
                            .ExposeConfiguration(x =>
                                {
                                    x.SetProperty("current_session_context_class","web");
                                })
                            .ExposeConfiguration(Cfg => _configuration = Cfg)
                            .BuildSessionFactory();

Here is the log output when using an integer ID:

16:23:14.287 [4] DEBUG NHibernate.Event.Default.DefaultSaveOrUpdateEventListener - saving transient instance
16:23:14.291 [4] DEBUG NHibernate.Event.Default.AbstractSaveEventListener - saving [TestEntities.User#<null>]
16:23:14.299 [4] DEBUG NHibernate.Event.Default.AbstractSaveEventListener - executing insertions
16:23:14.309 [4] DEBUG NHibernate.Event.Default.AbstractSaveEventListener - executing identity-insert immediately
16:23:14.313 [4] DEBUG NHibernate.Persister.Entity.AbstractEntityPersister - Inserting entity: TestEntities.User (native id)
16:23:14.321 [4] DEBUG NHibernate.AdoNet.AbstractBatcher - Opened new IDbCommand, open IDbCommands: 1
16:23:14.321 [4] DEBUG NHibernate.AdoNet.AbstractBatcher - Building an IDbCommand object for the SqlString: INSERT INTO [User] (FirstName, LastName, Email, Password) VALUES (?, ?, ?, ?); select SCOPE_IDENTITY()
16:23:14.322 [4] DEBUG NHibernate.Persister.Entity.AbstractEntityPersister - Dehydrating entity: [TestEntities.User#<null>]
16:23:14.323 [4] DEBUG NHibernate.Type.StringType - binding null to parameter: 0
16:23:14.323 [4] DEBUG NHibernate.Type.StringType - binding null to parameter: 1
16:23:14.323 [4] DEBUG NHibernate.Type.StringType - binding 'ertr' to parameter: 2
16:23:14.324 [4] DEBUG NHibernate.Type.StringType - binding 'tretret' to parameter: 3
16:23:14.329 [4] DEBUG NHibernate.SQL - INSERT INTO [User] (FirstName, LastName, Email, Password) VALUES (@p0, @p1, @p2, @p3); select SCOPE_IDENTITY();@p0 = NULL, @p1 = NULL, @p2 = 'ertr', @p3 = 'tretret'

and here is the output when using a Guid:

16:50:14.008 [4] DEBUG NHibernate.Event.Default.DefaultSaveOrUpdateEventListener - saving transient instance
16:50:14.012 [4] DEBUG NHibernate.Event.Default.AbstractSaveEventListener - generated identifier: d74e1bd3-1c01-46c8-996c-9d370115780d, using strategy: NHibernate.Id.GuidCombGenerator
16:50:14.013 [4] DEBUG NHibernate.Event.Default.AbstractSaveEventListener - saving [TestEntities.User#d74e1bd3-1c01-46c8-996c-9d370115780d]

This is where it silently fails, with no exception thrown or further log entries.

It looks like it is generating the Guid ID correctly for the new object, but is just not getting any further than that.

Is there something I need to do differently in order to use Guid IDs?

Thanks,

Dan.

A: 

I'm not completely sure about the auto-mapping situation.

However, one thing I can tell you is to be sure to set the Guid on new instances of your class to Guid.Empty when you construct them, rather than to Guid.NewGuid(). This signals to NHibernate that the object is new and should be inserted rather than updated; much the same as zero for an int property. NHibernate will back-update the property to a generated Guid on insertion.

public class Cat
{
    public Cat()
        : base()
    {
        this.Id = Guid.Empty;
    }

    public virtual Guid Id { get; set; }
    ...
}

In a manual mapping situation, the map would look like the following:

public class CatMap : ClassMap
{
    public CatMap()
        : base()
    {
        this.Id(e => e.Id);
        ...
    }
}
Travis Heseman
+1  A: 

You should check your application (non-mapping) code and be sure that you are committing your NHibernate ITransaction instance.

When using an integer ID and the native or identity generator, NHibernate must insert a new instance immediately so that the ID value is generated (even if the transaction hasn't been committed yet). When using an identifier strategy like guid.comb that does not depend on the database to generate IDs, NHibernate will not actually execute the insert until the enclosing transaction is committed.

Sean Carpenter
That solved it!I wasn't using Transaction.Begin() and Transaction.Commit() around my code at all and, with integer IDs, everything worked okay.Adding the transaction around the code makes it work with GUIDs as well.Many thanks,Dan.
DanK