TopBanana9000
TopBanana9000

Reputation: 898

EF Core is attempting to update a record that doesn't exist

UPDATE: There was code that I did not see that until after I created this post that was causing my insert to fail. All works as expected now. Apologies, and thank you for the time spent helping.

I am having an issue where EF is attempting to update a record that doesn't exist. I need the record to be inserted.

Sample code below:

DoStuff(List<ParentObj> listParent, List<OtherParent> listOtherParent)
{
    foreach(var op in otherParent)
    {
        var updateThisParentRecord = listParent.FirstOrDefault(x => x.Id == op.Id);
        if(updateThisParentRecord != null)
        {
            updateThisParentRecord.ChildRecordList.Add(new ChildRecord
            {
                //set relevant props not PK as it is an identity column
                OtherChildObject = new OtherChildObject 
                {
                    //set relevant props not PK as it is an identity column
                }
            });
        }
    }
    await _parentObjectContext.SaveChangesAsync();
}

Model code:

public partial class ParentObj
{
    public ParentObj()
    {
        ChiledRecordList = new HashSet<ChildRecord>();
    }

    public int Id {get;set;}
    public virtual ICollection<ChildRecord> ChildRecordList {get;set;}
}

public partial class ChildRecord
{
    public int Id {get;set;}
    public int ParentId {get;set;}
    public int OtherChildObject {get;set;}

    public virtual OtherChildObject OtherChildObject {get;set;}
    public virtual ParentObj {get;set;}
}

public partial class OtherChildObject
{
    public OtherChildObj()
    {
        ParentObj = new HashSet<ParentObj>();
        ChildRecord = new Hashset<ChildRecord>();
    }

    public long Id {get;set;}
    //now that I have written this out, the below line seems strange and may 
    //be keyed wrong?
    public virtual ICollection<ParentObj> ParentObj {get;set;}
    public virtual ICollection<ChildRecord> ChildRecord {get;set;}
}

When saving the below exception is thrown:

"Database operation expected to affect 1 row(s) but actually affected 0 row(s). Data may have been modified or deleted since entities were loaded. See http://go.microsoft.com/fwlink/?LinkId=527962 for information on understanding and handling optimistic concurrency exceptions."

This is what is in the entry list of the exception: {{Id: 1000} Modified EntityType: ChildRecord}

The generated SQL from EF rightfully creates OtherChildObject but it is attempting to update the ChildRecord which doesn't exist. Does anyone know what is going on? Thanks in advance

Upvotes: 1

Views: 1967

Answers (1)

sprengo
sprengo

Reputation: 168

Breaking change from DetectChanges honors store-generated key values:

Old behavior

Before EF Core 3.0, an untracked entity found by DetectChanges would be tracked in the Added state and inserted as a new row when SaveChanges is called.

New behavior

Starting with EF Core 3.0, if an entity is using generated key values and some key value is set, then the entity will be tracked in the Modified state. This means that a row for the entity is assumed to exist and it will be updated when SaveChanges is called. If the key value isn't set, or if the entity type isn't using generated keys, then the new entity will still be tracked as Added as in previous versions.

Why

This change was made to make it easier and more consistent to work with disconnected entity graphs while using store-generated keys.

Mitigations

This change can break an application if an entity type is configured to use generated keys but key values are explicitly set for new instances. The fix is to explicitly configure the key properties to not use generated values. For example, with the fluent API:

modelBuilder
    .Entity<Blog>()
    .Property(e => e.Id)
    .ValueGeneratedNever();

Or with data annotations:

[DatabaseGenerated(DatabaseGeneratedOption.None)]
public string Id { get; set; }

If you are explicitly setting the Id of your ChildObjects and the entity is using generated key values then they will be tracked in the Modified state.

Upvotes: 4

Related Questions