In my asp.net mvc app I have a Log object which tracks what happens in my forum. when a comment is added inside a transaction the following happens:
-
A new comment is added to the submissions table
-
A new log entry with a reference to the id of that comment is added to the logs table
Finally the transaction is committed and no exceptions are generated. But it turns out that when the transaction is committed, NHibernate first inserts the Log record and then the Comment record, therefore saving the log record with an incorrect comment id which is 0.
Here is some code to show what is happening:
using (IAtomicTransaction Transaction = UnitOfWork.BeginTransaction())
{
try
{
SubmissionRepository.AddComment(Comment, ParentSubmission);
LogRepository.AddCommentEntry(Comment);
Transaction.Commit();
}
catch
{
Transaction.Rollback();
throw;
}
}
UnitOfWork and AtomicTransaction are just wrappers around the ISession and ITransaction objects from the NHibernate APIs. And here is the log generated which confirms the problem:
2011-02-09 14:42:05,631 [15] DEBUG NHibernate.SQL - INSERT INTO logs (version, created_at, updated_at, comment_id) VALUES (?p0, ?p1, ?p2, ?p3); ?p0 = 9/02/2011 2:42:05 PM, ?p1 = 9/02/2011 3:41:52 AM, ?p2 = NULL, ?p3 = 0
2011-02-09 14:42:05,647 [15] DEBUG NHibernate.SQL - SELECT LAST_INSERT_ID()
2011-02-09 14:42:05,647 [15] DEBUG NHibernate.SQL - INSERT INTO submissions (version, created_at, updated_at, body) VALUES (?p0, ?p1, ?p2, ?p3);?p0 = 9/02/2011 2:42:05 PM, ?p1 = 9/02/2011 3:41:52 AM, ?p2 = 9/02/2011 3:41:52 AM, ?p3 = 'dfgdfgd dfg df'
2011-02-09 14:42:05,647 [15] DEBUG NHibernate.SQL - SELECT LAST_INSERT_ID()
What is the solution to this problem?
Update:
So it turned out that if I call Flush after every insert then the order will be correct.
using (IAtomicTransaction Transaction = UnitOfWork.BeginTransaction())
{
try
{
SubmissionRepository.AddComment(Comment, ParentSubmission);
UnitOfWork.CurrentSession.Flush();
LogRepository.AddCommentEntry(Comment);
UnitOfWork.CurrentSession.Flush();
Transaction.Commit();
}
catch
{
Transaction.Rollback();
throw;
}
}
Sure, when you try to add a comment and a log entry in a single transaction the ID of
Commentisn’t yet generated, unless you use “Manual ID assignment” approach or flush your session to persist changes you made.In your original post there is no “Flush” after you add the comment and you want to add the log for that comment (for which ID isn’t defined yet).
Not sure about why Nhibernate takes such a reverse order but seems like its at least an ID generation issue.
Another thing that caught my attention is that you use
CommentandArgs.Commentside by side. What is difference?Anyway, if
Commentis somewhat your domain entity, you have to:ISession(you should have the session-attachedCommententity object available);