Mansfield
Mansfield

Reputation: 15160

Entity Framework 5 SaveChanges() not rolling back attempted transaction

I have the following code (EntityContext.Current is a DbContext):

try {
    Setting s = new Setting
    {
        id = 1,
        user_id = 0
    };

    EntityContext.Current.Settings.Add(s);
    EntityContext.Current.SaveChanges(); //this violates a foreign key constraint and therefore throws an exception
} catch (Exception ex) {
    ErrorContext.Log(ex);
}

What I would expect (as per this answer) is that the pending changes would be rolled back when they have failed. However, In my error logging method, I see that they have not been.

Error e = new Error
{
    message = ex.Message
};

EntityContext.Current.Errors.Add(e);

var pending = ((IObjectContextAdapter)EntityContext.Current).ObjectContext.ObjectStateManager.GetObjectStateEntries(System.Data.EntityState.Added);
//this contains both my new error entity and my old setting entity which shouldn't be here

EntityContext.Current.SaveChanges();

When my application attempts to log the error in the database, it also tries (again) to save the setting with the invalid foreign key constraint, which then causes the addition of the error log record to fail.

This is the first time I've ran into this behavior with EF. I tried wrapping the first SaveChanges() in a transaction but the same issue occurred.

As per this answer my connection string does not contain Enlist=false either. I'm at a loss. Is this expected behavior? And how can I prevent this from happening?

Upvotes: 3

Views: 2295

Answers (1)

Slauma
Slauma

Reputation: 177153

This is expected behaviour. SaveChanges attempts to commit all changes in a single database transaction. If it fails the database transaction is rolled back and nothing will be written to the database.

But the failed transaction won't change the state of entities in the context. That's why your Setting entity is still in state Added.

Attaching your Error entity to the same context is a problem since you often won't be able to store it to the database, like in this example. I would consider to write the error log always in a new context with a dedicated method that just only opens a new context, adds the Error entity to this context and saves the changes. Then dispose it immediately.

Upvotes: 3

Related Questions