Hi,
I have a strange error which may or may not be related to transactions. Here is my scenario....
I have 2 entities with typical one-to-many parent child relationship. One "Document" can have many "DocumentAudit" objects.
Parent mapping:
Code:
<?xml version="1.0"?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" auto-import="true" assembly="Business">
<class name="Business.Domain.Document" table="DOCUMENT" lazy="false">
<id name="Id" type="int" column="DocumentId">
<generator class="native" />
</id>
<property name="FileName" column="FileName" type="string" length="220" />
<bag name="DocumentAudits" cascade="all" lazy="true" inverse="true">
<key column="DocumentId" />
<one-to-many class="Business.Domain.DocumentAudit" />
</bag>
</class>
</hibernate-mapping>
And here is the child:
Code:
<?xml version="1.0"?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" auto-import="true" assembly="Business">
<class name="Business.Domain.DocumentAudit" table="DOCUMENT_AUDIT" lazy="true">
<cache usage="read-write"/>
<id name="Id" type="int" column="DocumentAuditId">
<generator class="native" />
</id>
<property name="Action" column="Action" type="string" length="50" />
<property name="Actor" column="Actor" type="string" length="50" />
<many-to-one name="Document" class="Business.Domain.Document" fetch="select">
<column name="DocumentId" unique="true" />
</many-to-one>
</class>
</hibernate-mapping>
I have a generic data access class called BaseBroker which has Persist and Delete methods as follows:
Code:
public void Persist(T obj)
{
using (ITransaction txn = nHibernateDB.Session.BeginTransaction())
{
try
{
nHibernateDB.Session.Persist(obj);
txn.Commit();
}
catch
{
txn.Rollback();
throw;
}
}
}
public void Delete(T obj)
{
using (ITransaction txn = nHibernateDB.Session.BeginTransaction())
{
try
{
nHibernateDB.Session.Delete(obj);
txn.Commit();
}
catch
{
txn.Rollback();
throw;
}
}
}
All seems OK and I've got some test fixtures covering everyday adding and deleting of objects. All working fine.
HOWEVER, I've run into a very strange error. There is a unique index on the Document.FileName column. I've written a test which needs to verify that attempting to insert a Document with an existing FileName generates the expected exception. So the steps in the test are as follows:
1. Create new Document "doc1" with 1 new DocumentAudit child.
2. Call Persist(doc1)
3. Create new Document "doc2" with 1 new DocumentAudit child, and the SAME FileName as doc1
4. Call Persist(doc2) -> should generate a unique index exception, catch and verify it
5. Cleanup: call Delete(doc1) to delete the first instance that was persisted successfully
What's happening when I run this test is this...it all goes as expected right up to the last step when it tries to clean up and delete doc1. Instead of deleting doc1 I get the following exception:
Test method BusinessTests.Brokers.DocumentBrokerTests.InsertDuplicateFileNameTest threw exception: NHibernate.Exceptions.GenericADOException: could not insert: [Business.Domain.DocumentAudit][SQL: INSERT INTO DOCUMENT_AUDIT (Action, Actor, DocumentId) VALUES (?, ?, ?); select SCOPE_IDENTITY()] ---> System.Data.SqlClient.SqlException: Cannot insert the value NULL into column 'DocumentId', table 'dbo.DOCUMENT_AUDIT'; column does not allow nulls. INSERT fails.
The statement has been terminated..
So I've put some debug into the code (in the form of Console.WriteLine in various places). Look at the Console output (my debug bits start with "==>"):
==> About to create 1st instance
NHibernate: INSERT INTO DOCUMENT (FileName) VALUES (@p0); select SCOPE_IDENTITY();@p0 = 'test.txt'
NHibernate: INSERT INTO DOCUMENT_AUDIT (Action, Actor, DocumentId) VALUES (@p0, @p1, @p2); select SCOPE_IDENTITY();@p0 = 'create', @p1 = 'testuser', @p2 = 39
==> About to create 2nd instance
NHibernate: INSERT INTO DOCUMENT (FileName) VALUES (@p0); select SCOPE_IDENTITY();@p0 = 'test.txt'
==> Caught and handled exception
==> About to delete 1st instance
NHibernate: INSERT INTO DOCUMENT_AUDIT (Action, Actor, DocumentId) VALUES (@p0, @p1, @p2); select SCOPE_IDENTITY();@p0 = 'create', @p1 = 'testuser', @p2 = NULL
So as you can see the bit the generated the error was the last statement - @p2 (the DocumentId) is null.
The bizarre thing is that after the Exception is caught and handled (and the transaction rolled back) the Delete statement should be executed. But it doesn't do that, it's like it's still got a "queue" of SQL statements it needs to execute, as it tries to insert into DOCUMENT_AUDIT!
It's almost like the transaction rollback didn't flush the pending SQL statements???
I hope somebody can help!!
Paul