0

I can add an entity but when I try to update Entity Framework doesn't work. This is what I'm doing:

  1. Get model in action filter in Web API. I lookup some data about this model in the database and use that to change/hydrate different parts of the model. Yes, it has to be exactly that way.

  2. Controller runs. Controller does nothing except pass the model to the repository to be saved.

  3. Repository checks if a model with that ID exists in the database. If it does it tries to attach and then save.

Code:

_db.Airplanes.Attach(airplane);
// _db.Entry(airplane).State = EntityState.Modified; // doesn't do anything
_db.SaveChanges();

It doesn't work. Instead, it throws this error message:

System.Data.Entity.Core.OptimisticConcurrencyException: 'Store update, insert, or delete statement affected an unexpected number of rows (0). Entities may have been modified or deleted since entities were loaded. See http://go.microsoft.com/fwlink/?LinkId=472540 for information on understanding and handling optimistic concurrency exceptions.'

Perhaps the problem is that I originally got the entity way back in the Action Filter and it has since gone out of context or something? In any event I don't know why Entity Framework won't let me just add it back.

Edit: I'm being forced to edit this to explain how this isn't solved by another question. ...umm, it isn't. The burden of proof should be on the person making the claim.

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
user875234
  • 2,399
  • 7
  • 31
  • 49
  • What's wrong to put the complete method code instead of few lines? – TanvirArjel Apr 23 '19 at 15:59
  • I'm adding this as a comment instead of venting directly in the question. C# is too verbose. It didn't used to be but now it is. We have regular .NET, .NET Standard, and .NET Core. There are multiple versions of many of the .NET frameworks across each of these. The frameworks themselves have changes over time. Google keyword searches seem to lean towards pages that have been successful for users in the **past**. I'm drowning in misinformation. – user875234 Apr 23 '19 at 15:59
  • 2
    Does `airplane` have the correct entity id? – Neil Apr 23 '19 at 16:01
  • BTW, why are you using `.Attach`? It's 'unusual'. Generally, you would find/update/save. Attach bypasses a lot of things that try to make updates easier. – Neil Apr 23 '19 at 16:02
  • @TanvirArjel that is the complete code for the repository. The rest of it is kind of strewn about all over the place but I've done my best to explain everything I can think of that would be relevant without getting into the minutiae. – user875234 Apr 23 '19 at 16:03
  • @Niel yep, right ID. The ID is 1 (there's only one in the database right now). – user875234 Apr 23 '19 at 16:04
  • In ANY concurrent system, you have to allow for the fact that some other process has changed the record you are about to change. DbUpdateConcurrencyException is the way this information is passed to your code. You must handle it. – Neil Apr 23 '19 at 16:04
  • Possible duplicate of [Entity Framework: "Store update, insert, or delete statement affected an unexpected number of rows (0)."](https://stackoverflow.com/questions/1836173/entity-framework-store-update-insert-or-delete-statement-affected-an-unexpec) – Neil Apr 23 '19 at 16:05
  • @Niel, I'm using attach because I get the model from the controller and I just want to save or update it. This API actually saves things wholesale. That is, it saves the entire airplane all at once. There's no need to, say, just update the engines. It always updates everything (the engines, the tires, the ailerons, the wings, etc.). That's a requirement of the API. So doing find/update/save would be a lot of mapping one object to another. I'd prefer to not have to map objects to each other. – user875234 Apr 23 '19 at 16:09
  • @Niel, no it isn't. Ive already checked that question. It's a conceptual answer, not a hard answer, and it doesn't provide any new information. Plus a lot of the other answers focus on ASP.NET MVC which is not part of this problem. – user875234 Apr 23 '19 at 16:11
  • This reminds me of similar issues with datatables if you don't call savechanges and try to modify again. Which is exactly related to the line you commented. Just not Modified flag which is set as soon as you change. I think you are on the right track. – Alex M Apr 23 '19 at 16:20

1 Answers1

0

The answer was that in my action filter I was calling AirplaneRepository to get the ID of the airplane. Although I only used the ID I guess it was still adding the entire airplane to its black box (entity framework's black box). The solution was to detach the airplane from EF.

AirplaneRepository.cs

async Task<Airplane> GetAirplane(int partId, bool detachFromEF = false)
{
    var airplane = await _db.Airplane.SingleOrDefaultAsync(c => c.partId == partId);

    if(detachFromEF)
    {
        _db.Entry(airplane).State = EntityState.Detached; // <-- the fix
    }

    return airplane;
}

async Task<int?> GetAirplaneId(int partId)
{
    var airplane = await GetAirplane(partId, true);
    return airplane == null ? (int?)null : airplane.AirplaneId;
}

}

user875234
  • 2,399
  • 7
  • 31
  • 49