1

I have a project which uses Entity Framework extensively. And it all works fine.

However, there is one update that I am trying to make and it does not update, nor is an error thrown.

The basic process is that I take an existing entity, work with it to create a new (and different type) of entity. The new entity saves just fine, but the existing one does not.

Typically when I have encountered this, it means that the entity is not attached to the context. However, it is, and I have tried attaching it again as well as "Adding" it, but I have had no luck.

        if (fileRowEntity != null)
        {
            this.Context.FileRowEntities.Attach(fileRowEntity);
            fileRowEntity.FileRowStatusId = (int)FileRowStatus.Converted;
            fileRowEntity.EdiDocument = ediDocument;
        }

        ediDocument.InsertedDate = DateTime.Now;
        ediDocument.EdiDocumentGuid = Guid.NewGuid();
        ediDocument.DocumentMetatdata = null;
        this.Context.EdiDocuments.Add(ediDocument);
        var count = this.Context.SaveChanges();

The ediDocument is saved, but the fileRowEntity is not saved.

I am tearing my hair out trying to figure this out. I have tried a second explicit save on the fileRowEntity but it comes back with zero changes saved:

        if (fileRowEntity != null)
        {
            fileRowEntity.EdiDocumentId = ediDocument.EdiDocumentId;
            fileRowEntity.Column100 = "X";
            this.Context.FileRowEntities.Attach(fileRowEntity);
            count = this.Context.SaveChanges();
        }

count is always zero, and the database is not updated.

I don't know what else to try to debug this.

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Greg Gum
  • 33,478
  • 39
  • 162
  • 233
  • Attaching an entity does not mark it as modified... have you tried `Context.Entry(fileRowEntity).State = EntityState.Modified;`? (no need to attach it, this will attach the entity if it's detached aswell). Unless the entity is a proxy and/or it was loaded beforehand, EF will not know there were changes to it unless you tell it to, so it won't do any updates. Marking it as `Modified` explicitly should work. – Jcl Mar 20 '16 at 16:41
  • Thank you. Yes, that does indeed make the save work. Does this mean that the entity was in fact not in the same context? As far as I can tell, the same entity manager is used to retrieve the entities as save them. – Greg Gum Mar 20 '16 at 16:48
  • When an entity is loaded from the database, it's original values (as they were retrieved) are stored in the context (you can actually check them in `Context.Entry(entity).OriginalValues`)... when the dbcontext checks its changes (by calling `DetectChanges`), it stores the new values (you can check them in `Context.Entry(entity).CurrentValues`), and if any of those are modified, then an `UPDATE` is generated when saving changes. So if the original entity was retrieved from the database by that same context, then yes, it should detect the changes and save the modifications – Jcl Mar 20 '16 at 16:51
  • Then again, just the fact that it lets you attach the entity, proves that it was indeed not retrieved by that same context. I'm not 100% sure, but I think `Attach` throws an exception when you try to attach an already attached entity (modifying its state doesn't, so it's always safer). – Jcl Mar 20 '16 at 16:52
  • I'm posting all of this as an answer – Jcl Mar 20 '16 at 16:52

2 Answers2

3

Attaching an entity to a context does not mark it as modified.

In your case, the presumption is that the context instance where you are calling SaveChanges() on, is not the same that retrieved the fileRowEntity object, so it doesn't know that it was modified (or what was modified).

When a DbContext retrieves an object from the store, it stores a copy of its original values (unless you used AsNoTracking() on that query), and whenever it detects changes by a call to DetectChanges(), which you can make explicitly, but the normal DbContext implementation will call it by itself at many points), it'll store the new object values. If there are differences between those, the next call to SaveChanges will update the entity in the store/database.

When you attach an entity but don't mark it as modified, it doesn't know anything has changed, so you can explicitly mark the entity as modified:

Using Context.Entry(fileRowEntity).State = EntityState.Modified; should tell EF that your entity was modified, and it'll generate the update command when you call SaveChanges() for the whole entity (it'll send all the field values in the UPDATE SQL). Note that doing this also attaches the entity if it was not attached to the context (no need to attach it and then set its state).

You can also mark only the properties that were modified (or change the entity OriginalValues), this way your query will be optimized (it'll only update the fields that have actually changed). It's a bit cumbersome to track those changes yourself, but if you need that extra optimization, it's worth a shot (personally, unless there's a critical load on the database server and every bit counts, I wouldn't do this, but your choice)

Jcl
  • 27,696
  • 5
  • 61
  • 92
1

There is a second reason for the above described behavior: This code will turn off change tracking:

 Context.Configuration.AutoDetectChangesEnabled = false;

After I found this had been set in the constructor and removed it, all worked as expected.

Greg Gum
  • 33,478
  • 39
  • 162
  • 233
  • 1
    That's right. The default is automatic and I didn't think of the possibility that you could have disabled it. If you notice a slow behaviour after enabling automatic change detection, you can call DetectChanges explicitly. EF tends to do more detections than necessary :-) – Jcl Mar 20 '16 at 17:32
  • Good point. Again, thank you, I couldn't figure this one out :) – Greg Gum Mar 20 '16 at 17:34