Home > Net >  How do I compare an entity in my local context with the one in the database in EF Core?
How do I compare an entity in my local context with the one in the database in EF Core?

Time:02-10

EF Core 6 and .NET 6.

Suppose all my entities have a LastUpdateAt property, which is a DateTime that gets updated every time an entity is added or modified.

I get an entity from the context and show it to the user (web page, WPF window, whatever). At some point, the user clicks a Save button.

Before I save, I want to check if the entity has been updated by someone else since I got my copy. However, I'm struggling to see how to do this.

If I query the context, it just gives me back the entity I already have (including any changes my user has made).

If I refresh the entity, it overwrites the one in my context, losing my user's changes.

How do I check if the database version has a newer time stamp than the one in my context?

Thanks

CodePudding user response:

Moving the discussion here since I need to paste longer text. In this article it's said, during SaveChanges(), if the DATABASE version was modified in the mean time it will throw DbUpdateConcurrencyException. In that exception you have all 3 values and YOU can decide on how to resolve the conflict:

Resolving a concurrency conflict involves merging the pending changes from the current DbContext with the values in the database. What values get merged will vary based on the application and may be directed by user input.

There are three sets of values available to help resolve a concurrency conflict:

Current values are the values that the application was attempting to write to the database. Original values are the values that were originally retrieved from the database, before any edits were made. Database values are the values currently stored in the database.

CodePudding user response:

If you are loading an entity, keeping a DbContext instance open, updating that entity, then saving to the same DbContext instance then by default you are relying on EF to manage concurrency. This follows a "last in wins". You can let EF manage the concurrency by adding a [ConcurrencyCheck] on the LastUpdateAt property or using a Row Version via [Timestamp]. This will cause EF to fail updating if the underlying data has been updated. From there you have to decide how you want to handle it.

If you want to perform the concurrency check yourself then there are a couple of options.

  1. Structure your code to shorten the lifespan of the DbContext using either detached entities or projected View Models. This will generally have flow-on benefits to your code performance as the original longer-lived DbContext can easily find ways to cause bloat, or accumulate "poisoned" entities if alive too long. Automapper is a great tool to assist here where you can use ProjectTo to get the view models, then Map(source, destination) to copy the values across afterward. In this way you load the data including the last modified at value, make your changes, then when saving, you load the data, validate the modified at etc. then copy the values across and save.

  2. Scope a DbContext instance to check the data before saving.

.

private DateTime getFooLastUpdateAt(int fooId)
{
    using(var context = new AppDbContext())
    {
         var lastUpdateAt = context.Foos
             .Where(x => x.FooId == fooId)
             .Select(x => x.LastUpdateAt)
             .Single();
         return lastUpdateAt;
    }
}

This could use an injected DbContext factory or such to create the DbContext instance..

  •  Tags:  
  • Related