How can i ignore a DbUpdateConcurrencyException with my Entity Framework code?
Asked Answered
B

2

11

Is there any way I can tell EF to not worry about the number of rows a DELETE or UPDATE do or don't do?

I'm trying to delete a row from the database, but because the row doesn't exist, EF throws an exception: DbUpdateConcurrencyException .. saying 0 rows were affected. This is right -> no rows were deleted. But that's totally fine .. cause there's no data.

I don't really want to do a round-trip to the DB to see if that row exists .. and if so .. then try and delete it.

If i try and swallow the exception in a try / catch block, then the rest of the items to be deleted do NOT get sent to the db, when I try to SaveChanges() ... which is bad.

eg.

Delete(new Foo(1));
Delete(new Foo(2));
Delete(new Foo(3));
SaveChanges(); // <-- Throws the exception.

// DB Trace : DELETE FROM Foo WHERE Id = 1;

and thats it.. there's no trace showing record 2 or 3 trying to get deleted .. because the exception stops everything :(

Any ideas?

UPDATE

How does Delete work? Here's the code... (simplified and strongly typed)

public void Delete(Foo foo)
{
    if (foo == null)
    {
        throw new ArgumentNullException("foo");
    }

    Foo attachedEntity = Context.Set<Foo>().Local.FirstOrDefault(x => x.Id > 0);

    if (attachedEntity != null)
    {
        // Entity already in object graph - remove entity.
        Context.Set<Foo>().Remove(attachedEntity);
    }
    else
    {
        // Entity not in object graph, attach and set EntityState to Deleted.
        Context.Entry(foo).State = EntityState.Deleted;
    }
}
Baa answered 17/2, 2011 at 1:15 Comment(5)
How does Delete function work?Rowan
@Rowan - edited OP with extra code.Baa
I understand that these deletes are a part of bigger operation and can't be made independently?Rowan
Even if they were made independently, the same problem exists -> trying to delete a DETACHED object which doesn't doesn't exist in the DB. I don't care if the row exists or not - > just try and remove it. If it's not there, no probs. if so, then delete. Done. /me slaps EF.Baa
Ever find a solution for this? It's a major pain when performing multiple operations in a single SaveChanges call.Herthahertz
D
2

I think the behavior of EF is correct - simply you must execute commands only for objects which are present in DB. It is not for scenarios like: "I will try it and we will see...". If you can't be sure that object exists in DB and you don't want to do round trip (which I think is the best idea because deleting detached object can have several other pitfalls especially if object participates in independent associations) you should use DbContext.Database.SqlCommand and run store procedure.

The correct way to handle DbUpdateConcurrencyException is described here => After each exception you should resolve confilicts (in your case it means remove problematic entity from DbContext) and execute SaveChanges again.

Dib answered 17/2, 2011 at 9:28 Comment(5)
Ah :) Earlier today I was exerimenting with using a try/catch around the Context.SaveChanges() to handle any DbUpdateConcurrencyException exceptions .. but all I was doing was a Refresh() .. then SaveChanges() again .. which of course didn't work (threw the same error). Can u update you answer to include HOW u remove the offending entity from the Context, please?Baa
@Pure.Krome: I would try to use ChangeTracker and set entity state to Detached.Dib
can u please elaborate on your previous comment, please?Baa
@Pure.Krome: Try to use exception.GetEntry(dbContext).State = EntityState.Detached; Where exception is catched DbUpdateConcurrencyException. If this doesn't work you will simply have to check that entity exists before you mark it as deleted.Dib
I disagree with the answer, the behavior of EF is not correct. Think of it this way - User A does SELECT, exists. User B does exist, exists. User A deletes, succeeds. User B deletes, fails with exception. In both cases the result should be the same - we're deleting the record. Sure, there's a conflict, but the result is the same - a deleted row. There's nothing to resolve. "Checking to see if it exists first" doesn't work - it's a race condition. Can't lock on reads in all cases, in our case we're using MySQL Cluster - no read locks.Herthahertz
A
-1

You can in fact ignore these kinds of issues by setting:

db.Configuration.ValidateOnSaveEnabled = false;

Where db is the DbContext instance.

Obviously you should know what you're doing when you do this, but then again, most approaches to updating a database that are not EF don't have any change tracking/validation and just issue update/insert/delete to the database naively without checking anything first, so you're basically just telling EF to behave more like that.

Aquarium answered 2/8, 2012 at 4:53 Comment(3)
Are you sure? ValidateOnSaveEnabled controls data annotation validation, not concurrency. My understanding is that (as of EF 4.3) there's no way to suppress the DbUpdateConcurrencyValidation for deletes.Herthahertz
I tested the above claim before posting; I created 2 DbContexts, used one to fetch an object, another to fetch, change, and save an object, and then finally attempted to change and save on the first one. I got an Exception; changed the above setting to false, ran again, no Exception. In addition, have you checked the documentation? It refers to validating the entity, not the schema: msdn.microsoft.com/en-us/library/…Aquarium
Doesn't work, a DbUpdateConcurrencyException still gets thrown. I tested with 2 instances of a DbContexts. Edited an entity in the first and set ValidateOnSaveEnabled to false, then deleted the entity in the second context and saved, then saved on the first but it throwns the exception.Converted

© 2022 - 2024 — McMap. All rights reserved.