简体   繁体   中英

Why is Entity Framework trying to update more than one row when I save my changes?

I have the method below and when the save changes is run Entity Framework tries to update more than one row? Even though I'm only modifying a date on one record and fetching one record to modify.

Error

Microsoft.EntityFrameworkCore.DbUpdateConcurrencyException: Database operation expected to affect 1 row(s) but actually affected 2 row(s). Data may have been modified or deleted since entities were loaded.

Method

    public static async Task<HttpResult> PostRecordCoworkerStartDateAsync(MpidDbContext context, int id, RecordCoworker recordCoworker)
    {
        // just testing to see how many are returned
        int testCount = context.RecordsCoworkers.Where(m => m.Id == id && m.IsActive == true).ToList().Count();

        RecordCoworker recordCoworkerToUpdate = context.RecordsCoworkers.SingleOrDefault(m => m.Id == id && m.IsActive == true);

        recordCoworkerToUpdate.StartDate = recordCoworker.StartDate;

        try
        {
            await context.SaveChangesAsync();
        } catch(Exception ex)
        {
            var i = ex;
        }

        return HttpResult.NoContent;
    }

Somehow when the query gets run it strips out parameters need to update correctly, for ex. 'IsActive' field. I've captured the query below.

SET NOCOUNT ON;
UPDATE [Records_Coworkers] SET [StartDate] = @p0
WHERE [RecordID] = @p1 AND [CoworkerID] = @p2;
SELECT @@ROWCOUNT;

basically, I just want to update the one record where 'IsActive' is true. But the query, because it strips off the 'IsActive' property will try and update two or more rows.

Just to explain why:

  1. You ask the database for all active users with the specific id and take the first result.
  2. Entity Framework sends the request and materializes the result into an object.
  3. You change the property.
  4. Entity framework will set the state of the object to changed.
  5. You call SaveChanges to signal EF that it sends back the changes to the database.
  6. As it is known which id the object has, it is directly addressed in the update statement.

Those are two operations: SELECT and UPDATE . Those are performed separatly as Entity Framework does not know: What you do in the meantime (until you call SaveChanges ) to the entity?

Assigning [ConcurrencyCheck] to the property tells Entity Framework that it should ensure that at the time of the UPDATE the value of property IsActive should be the same as during the SELECT . When there are different applications writing to that database this ensures none of the other applications changed the entity during the time between SELECT and UPDATE .

You should remove the int testCount line. Or request them as Untracked.

I found out why.

In my entity I needed to add a data attribute to the 'IsActive' property

    [Required]
    [ConcurrencyCheck]
    public bool IsActive { get; set; }

In my case the error was caused by not having a real ID column.

What I believed to be an ID - and configured in Entity Framework with the [Key] attribute - had duplications in the database, so even when I used it for filtering I updated multiple rows.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM