Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Retry Entity Framwork DbContext.SaveChanges after double inserting a key

I am using Entity Framework on Azure. After makes a few changes to my data I call the DbContext.SaveChanges .

In my code, Messages belong to Conversations. On receiving a message I add it to the Messages table and create its parent in the Conversations table. If I get two messages come in at once with the same parent, there is a possibility of double inserting values with the same primary key into the Conversations table.

The process:

I'll call some "insert conversation if it doesn't exist code":

 if (this.Context.Conversations.SingleOrDefault(fc => fc.ConversationId == conversation.ConversationId) == null)
        {
            Context.Entry(conversation).State = EntityState.Added; //public DbEntityEntry Entry(object entity);
        }

Later on I'll call this.Context.SaveChanges


I've been reading SQL Azure and Entity Framework Connection Fault Handling and I'm pretty sure I want to implement a Retry Policy With Transaction Scope.

  • How do I go about wrapping SaveChanges in a retry policy Some thoughts: How do I go about wrapping SaveChanges in a retry policy. Will this retry the code I ran earlier that checked if the conversation existed? How do I make my retry policy retry for non-transient faults (like PK violations)?

  • If a retry policy isn't possible , what is the entity framework approach to burying the "create or edit if exists" logic as far down as possible when saving a context? Is it best to just call a stored procedure in this special case?

like image 770
Nathan Cooper Avatar asked Dec 15 '14 18:12

Nathan Cooper


2 Answers

It sounds more like a concurrency retry pattern that you want:

using (var context = new BloggingContext()) 
{ 
    var blog = context.Blogs.Find(1); 
    blog.Name = "The New ADO.NET Blog"; 

    bool saveFailed; 
    do 
    { 
        saveFailed = false; 

        try 
        { 
            context.SaveChanges(); 
        } 
        catch (DbUpdateConcurrencyException ex) 
        { 
            saveFailed = true; 

            // Update the values of the entity that 
            //failed to save from the store 
            ex.Entries.Single().Reload(); 
        } 

    } while (saveFailed); 
}

Reference:

http://msdn.microsoft.com/en-gb/data/jj592904.aspx

like image 183
Colin Avatar answered Nov 13 '22 05:11

Colin


Sadly - you do not. That is not within the scope of what EF can do out of the box. The retry logic explicitly only handles transient connection issues. And there is no retry logic that magically fixes PK issues. There also is no "upsert" (update or insert) logic at all in EF.

You are on your own here - no sense to even ask the devs as they are busy with EF 7 and even drop features temporarily to make that happen (like inheritance). Maybe in a year or two, once EF has more functionality and is stable in v7... but no, nothing there at the moment.

You will have to sort that out in your own application logic.

like image 25
TomTom Avatar answered Nov 13 '22 06:11

TomTom