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 Comment
isn'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 Comment
and Args.Comment
side by side. What is difference?
Anyway, if Comment
is somewhat your domain entity, you have to:
- Insert it;
- Make sure it is attached to the current
ISession
(you should have the session-attachedComment
entity object available); - Use exatly that entity (which is in repository already) for log entry (NHibernate will take care of relations).
精彩评论