Hi.
I have some problems when using an IUserType. When I insert an entity NHibernate first executes an insert-statement and then an update-statement. If I do not use an IUserType it just executes an insert-statement as expected.
I have made a small example to reproduce the problem.
NHibernate version:
1.2.0.1001
Mapping documents:
Code:
<class name="TestClass" table="TestClass" lazy="false">
<id name="Id" type="Int32">
<column name="id" />
<generator class="assigned" />
</id>
<property name="Description" type="NHibernateDataMapper.Test.TestEnumType, NHibernateDataMapper">
<column name="description" />
</property>
</class>
The SQL (SQL Server 2000) to create the table:
Code:
CREATE TABLE TESTCLASS
(
id numeric,
description varchar,
)
And here is the source code:
Code:
public enum TestEnum
{
AValue,
BValue
}
public class TestClass
{
public int id;
public TestEnum description;
public int Id
{
get { return id; }
set { id = value; }
}
public TestEnum Description
{
get { return description; }
set { description = value; }
}
}
public class TestEnumType : IUserType
{
public bool Equals(object x, object y)
{
return x == y;
}
public object NullSafeGet(IDataReader rs, string[] names, object owner)
{
string value = (string)rs[rs.GetOrdinal(names[0])];
if (value == "A")
{
return TestEnum.AValue;
}
if (value == "B")
{
return TestEnum.BValue;
}
throw new ArgumentException("Cannot map value");
}
public void NullSafeSet(IDbCommand cmd, object value, int index)
{
IDataParameter par = (IDataParameter)cmd.Parameters[index];
switch ((TestEnum)value)
{
case TestEnum.AValue:
par.Value = "A";
break;
case TestEnum.BValue:
par.Value = "B";
break;
}
}
public object DeepCopy(object value)
{
return value;
}
public SqlType[] SqlTypes
{
get { return new SqlType[] { new StringSqlType() }; }
}
public Type ReturnedType
{
get { return typeof(TestEnum); }
}
public bool IsMutable
{
get { return false; }
}
}
When I try to insert a TestClass instance using code like this:
Code:
...
TestClass testClass = new TestClass();
testClass.Id = 1;
testClass.Description = TestEnum.AValue;
session.Save(testClass);
an insert is done, but it is always followed by an update. If I change the TestClass to use a simple string instead of the enum, only an insert is generated. Is this a bug in NHibernate or is something wrong with my IUserType implementation?
I have also tried to make an IUserType which just maps a string to itself. Here things work too (just one insert). So I suppose it might have something to do with the enum?
Thanks in advance,
Jacob Ilsø
P.S. I have attached an excerpt of the log file in case this helps:
Code:
3093 [4868] DEBUG NHibernate.Impl.SessionImpl (null) - opened session
3124 [4868] DEBUG NHibernate.Transaction.AdoTransaction (null) - begin
3140 [4868] DEBUG NHibernate.Connection.DriverConnectionProvider (null) - Obtaining IDbConnection from Driver
3546 [4868] DEBUG NHibernate.Impl.SessionImpl (null) - generated identifier: 1
3578 [4868] DEBUG NHibernate.Impl.SessionImpl (null) - saving [NHibernateDataMapper.Test.TestClass#1]
3609 [4868] DEBUG NHibernate.Transaction.AdoTransaction (null) - commit
3640 [4868] DEBUG NHibernate.Impl.SessionImpl (null) - flushing session
3656 [4868] DEBUG NHibernate.Impl.SessionImpl (null) - Flushing entities and processing referenced collections
3687 [4868] DEBUG NHibernate.Persister.Entity.AbstractEntityPersister (null) - NHibernateDataMapper.Test.TestClass.Description is dirty
3703 [4868] DEBUG NHibernate.Impl.SessionImpl (null) - Updating entity: [NHibernateDataMapper.Test.TestClass#1]
3718 [4868] DEBUG NHibernate.Impl.SessionImpl (null) - Processing unreferenced collections
3749 [4868] DEBUG NHibernate.Impl.SessionImpl (null) - scheduling collection removes/(re)creates/updates
3749 [4868] DEBUG NHibernate.Impl.SessionImpl (null) - Flushed: 1 insertions, 1 updates, 0 deletions to 1 objects
3781 [4868] DEBUG NHibernate.Impl.SessionImpl (null) - Flushed: 0 (re)creations, 0 updates, 0 removals to 0 collections
3812 [4868] DEBUG NHibernate.Impl.Printer (null) - listing entities:
3828 [4868] DEBUG NHibernate.Impl.Printer (null) - NHibernateDataMapper.Test.TestClass{Description=AValue, Id=1}
3843 [4868] DEBUG NHibernate.Impl.SessionImpl (null) - executing flush
3874 [4868] DEBUG NHibernate.Persister.Entity.SingleTableEntityPersister (null) - Inserting entity: [NHibernateDataMapper.Test.TestClass#1]
3921 [4868] DEBUG NHibernate.Impl.BatcherImpl (null) - Opened new IDbCommand, open IDbCommands :1
3937 [4868] DEBUG NHibernate.Impl.BatcherImpl (null) - Building an IDbCommand object for the SqlString: INSERT INTO TestClass (description, id) VALUES (:description, :id)
3953 [4868] DEBUG NHibernate.Persister.Entity.SingleTableEntityPersister (null) - Dehydrating entity: [NHibernateDataMapper.Test.TestClass#1]
3968 [4868] DEBUG NHibernate.Type.Int32Type (null) - binding '1' to parameter: 1
3999 [4868] DEBUG NHibernate.SQL (null) - INSERT INTO TestClass (description, id) VALUES (@p0, @p1)
4015 [4868] DEBUG NHibernate.SQL (null) - @p0 = 'A'
4031 [4868] DEBUG NHibernate.SQL (null) - @p1 = '1'
4140 [4868] DEBUG NHibernate.Impl.BatcherImpl (null) - Closed IDbCommand, open IDbCommands :0
4156 [4868] DEBUG NHibernate.Persister.Entity.SingleTableEntityPersister (null) - Updating entity: [NHibernateDataMapper.Test.TestClass#1]
4187 [4868] DEBUG NHibernate.Impl.BatcherImpl (null) - Opened new IDbCommand, open IDbCommands :1
4218 [4868] DEBUG NHibernate.Impl.BatcherImpl (null) - Building an IDbCommand object for the SqlString: UPDATE TestClass SET description = :description WHERE id = :id
4234 [4868] DEBUG NHibernate.Persister.Entity.SingleTableEntityPersister (null) - Dehydrating entity: [NHibernateDataMapper.Test.TestClass#1]
4249 [4868] DEBUG NHibernate.Type.Int32Type (null) - binding '1' to parameter: 1
4281 [4868] DEBUG NHibernate.SQL (null) - UPDATE TestClass SET description = @p0 WHERE id = @p1
4296 [4868] DEBUG NHibernate.SQL (null) - @p0 = 'A'
4312 [4868] DEBUG NHibernate.SQL (null) - @p1 = '1'
4328 [4868] DEBUG NHibernate.Impl.BatcherImpl (null) - Closed IDbCommand, open IDbCommands :0
4359 [4868] DEBUG NHibernate.Impl.SessionImpl (null) - post flush
4374 [4868] DEBUG NHibernate.Impl.SessionImpl (null) - transaction completion
4390 [4868] DEBUG NHibernate.Transaction.AdoTransaction (null) - running AdoTransaction.Dispose()
4406 [4868] DEBUG NHibernate.Impl.SessionImpl (null) - closing session
4421 [4868] DEBUG NHibernate.Impl.SessionImpl (null) - disconnecting session
4453 [4868] DEBUG NHibernate.Connection.ConnectionProvider (null) - Closing connection
4484 [4868] DEBUG NHibernate.Impl.SessionImpl (null) - transaction completion