How to deep-copy instance's properties when updating an entity in the database?

101 Views Asked by At

Using Entity Framework Core in ASP.NET - I decided to encapsulate all the Address information into a class with the [Owned](https://learn.microsoft.com/en-us/ef/core/modeling/owned-entities) attribute:

[Microsoft.EntityFrameworkCore.Owned]
public class Address 
{
   public string? Street { get; set; }
   public string? City { get; set; }
   public string? Zipcode { get; set; }
   ...

   public void UpdateInfo(string? street, string? city, string? zipcode) 
   {
      this.Street = street;
      this.City = city;
      this.Zipcode = zipcode;
   }
}

And my entity have a property of type Address:

public class Person 
{
   ...
   public int Id { get; set; }
   public Address Info { get; set; }
   ...
}

I'm encountering a problem when updating my entity's address information using my UpdateInfo method and DbContext.Update.
When calling the method below, the updated data is not saved into the database:

public void UpdatePerson(int id)
{
    Person person = _dbContext.Persons.Find(id);
    person.Info.UpdateInfo("First Avenue", "New York City", "10001");

    _dbContext.Persons.Update(person);
    _dbContext.SaveChanges();
}

After some investigation I noticed that DbContext.Update makes a shallow copy of my Person instance, and if I instead do something like the code below, the data in the database will be updated correctly:

public void UpdatePerson_ForceDeepCopy(int id)
{
    Person person = _dbContext.Persons.Find(id);

    person.Info = new Address()
    {
        Street = "'First Avenue",
        City = "New York City", 
        Zipcode = "10001"
    };

    _dbContext.Persons.Update(person);
    _dbContext.SaveChanges();
}

I don't like this solution so much, I would much rather updating the existing instance of Address over creating a new one.

Does anyone know if it's possible, and if so, how can I configure my DbContext to perform a deep copy of my Owned instances when I use DbContext.Update?

Thanks.

1

There are 1 best solutions below

6
david-ao On

You could modify your first method to explicitly set the Info property as modified:

SaveChanges will only update properties marked as modified. Set IsModified to true to force EF Core to update a given property value, or set it to false to prevent EF Core from updating the property value.

public void UpdatePerson(int id)
{

   Person person = _dbContext.Persons.Find(id);
   person.Info.UpdateInfo("First Avenue", "New York City", "10001");
   _dbContext.Entry(person).Property(s => s.Info).IsModified = true;
   _dbContext.SaveChanges();
}

But anyway I would use the second method you showed, if we are really talking about the address of a person. If the db context is set to track entities, then this line is not needed: _dbContext.Persons.Update(person);

if you really need not to instantiate an "Address" (maybe this is just an example) then my first suggestion should work.