Entity Framework Core: Update relation with Id only without extra call

silent_coder picture silent_coder · Jun 2, 2017 · Viewed 12.3k times · Source

I'm trying to figure out how to deal with 'Single navigation property case' described in this doc:

Let's say we have 2 models.

class School
{
   public ICollection<Child> Childrens {get; set;}
   ...
}

and

class Child
{
    public int Id {get; set;}
    ...
}

So it's many-to-one relationship created by convention, without explicit foreign key in a Child.

So the question is if we have Child instance and know School.Id is there a way to update this relation without extra call to database to obtain School instance.

Answer

Ivan Stoev picture Ivan Stoev · Jun 2, 2017

So the question is if we have Child instance and know School.Id is there a way to update this relation without extra call to database to obtain School instance.

Yes, it's possible. You can create a fake stub School entity instance with Id only, Attach it to the DbContext (this way telling the EF that it is existing), Attach the Child instance for the same reason, and then add the Child to the parent collection and call SaveChanges:

Child child = ...;
var schoolId = ...;

var school = new School { Id = schoolId };
context.Attach(school);
context.Attach(child);
school.Childrens.Add(child);
context.SaveChanges();

Update: Actually there is another cleaner way, since even if the entity has no navigation or FK property, EF Core allows you to access/modify the so called Shadow Properties

Shadow properties are properties that do not exist in your entity class. The value and state of these properties is maintained purely in the Change Tracker.

as soon as you know the name. Which in your case, without configuration would be by convention "SchoolId".

So no fake School entity instance is needed, just make sure the Child is attached and then simply set the shadow property through ChangeTracker API:

context.Attach(child);
context.Entry(child).Property("SchoolId").CurrentValue = schoolId;
context.SaveChanges();