NHibernate won't let me insert a model in a session if it was part of a failed transaction on that session

342 Views Asked by At

Why can't I just insert the model after I get an error back from the database when trying to insert it the first time:

Report report = null;
using (var session = SessionFactory.OpenSession()) {
    try {
        using (var transaction = session.BeginTransaction()) {
            report = new Report();
            session.SaveOrUpdate(report);//Exception: Name field required
            transaction.Commit();
        }
    }
    catch { }

    try {
        using (var transaction = session.BeginTransaction()) {
            report.Name = "theName";
            session.SaveOrUpdate(report);

            //Causes Exception:
            //Row was updated or deleted by another transaction (or unsaved-value 
            //mapping was incorrect): [ReportViewer.DataAccess.Models.Report#22]                          
            transaction.Commit();
        }
    }
    catch { }
}

But when I am updating an existing model, and I get an error, I can make my fixes (in this case set a Name) and just try to update again:

Report report = null;
using (var session = SessionFactory.OpenSession()) {
    using (var transaction = session.BeginTransaction()) {
        report = new Report();
        report.Name = "theName";
        session.SaveOrUpdate(report);
        transaction.Commit();
    }
}
using (var session = SessionFactory.OpenSession()) {

    //get entity saved from previous session
    report = session.Get<Report>(report.Id);

    try {
        using (var transaction = session.BeginTransaction()) {
            report.Name = null;
            session.SaveOrUpdate(report);//Exception: Name field required
            transaction.Commit();
        }
    }
    catch { }

    try {
        using (var transaction = session.BeginTransaction()) {

            //updates and does not give an error
            report.Name = "theName";
            session.SaveOrUpdate(report);
            transaction.Commit();
        }
    }
    catch { }
}
2

There are 2 best solutions below

0
On BEST ANSWER

As Oskar said, you should discard an NHibernate session after an exception occurs. However, the reason the insert fails is that you have already made the report persistent by calling SaveOrUpdate on it (you should use Save here). When you call SaveOrUpdate again on the same instance, NHibernate throws an exception because the object is already persistent. Rewriting the code as follows will probably allow the insert to succeed (but it's not recommended):

try {
    using (var transaction = session.BeginTransaction()) {
        report.Name = "theName";                   
        transaction.Commit();
    }
}

In the update example, calling SaveOrUpdate has no effect because the object became persistent when NHibernate loaded it. Understanding NHibernate's instance states and how to work with persistent objects is fundamental and widely misunderstood.

A far better approach is to validate your objects before saving them to the database.

0
On

When an exception triggered by the database occurs, the NHibernate session must be closed (disposed). It is not guaranteed to be consistent (internally or with the DB state) after an exception.

See the chapter on exception handling in the NHibernate reference.