0

I'm using Entity Framework and I want to update a navigational property. For example, I have a Profile object and it has a navigational property to Gender. I want to change the Gender. I can update either the GenderID of the Profile object, which is the foreign key, or initialize the Gender with a new Gender object; so:

profile.GenderID = 2;

or

Profile.Gender = new Gender{ID=2, Name = "Female"};

Let's say I have a collection like Languages to represent a 1:n relationship. Do I have a list of LanguageIDs or List of Languages? How would I update the list without a hit to the DB?

If I choose the latter, what tells Entity Framework to commit either an update or an insert? Is it the primary key?

Tunaki
  • 132,869
  • 46
  • 340
  • 423
Eitan
  • 1,434
  • 6
  • 21
  • 53
  • you just need to update the foreign key field in your profile; no need to re instantiate Gender again. profile.GenderId = 2 would do – techspider Dec 31 '15 at 15:17
  • Thanks! I added to my question, what about a 1:n relationship where I have a list like languages. I don't have a list of foreign keys. Is there any way to update the list without a hit to the DB? – Eitan Dec 31 '15 at 15:24
  • *Do I have a list of LanguageIDs and List of Languages. How would I update the list without a hit to the DB? If I choose the latter* This is very confusing. What do you want to update? What is *the latter*? – Gert Arnold Jan 01 '16 at 00:49
  • Let's say I have a n:n relationship between Profile to Languages. I want to update the list of languages in the profile object. If it's a 1:n I can just update the foreignKey for example if a profile could only have on language I would just update LanguageID but for multiple languages how do I just update the IDs? – Eitan Jan 04 '16 at 14:51

1 Answers1

2

You have the following options:

1. Update the Foreign Key field in the parent object:

profile.GenderID = 2;

This has the effect of updating that field in the model, then after you call .SaveChanges(); on the context the database will be updated as will the navigation property on your object.


2. Update the navigation property directly to an EXISTING object already attached to the context:

for example:

var femaleEntity = context.Genders.First(g=>g.Name == "Female");
profile.Gender = femaleEntity;

in this instance the Navigation Property is updated right away but the .GenderId field is not updated on the profile object until you call context.SaveChanges(); whereupon EF will write the new value to the GenderId column in the database and update the .GenderId property on the profile object.


3. Update the navigation property with a NEW object:

Profile.Gender = new Gender{ID=2, Name = "Female"};

Here EF will attempt to ADD a new Gender row to the database with ID of 2 and Name = female. You almost certainly only want to do this if there is not already a 'Female' row in the Gender table of the database and if the Gender ID is an identity allocated by the database it will be overwritten with the allocated value upon calling .SaveChanges().


Ignoring the subtleties about when our objects are updated by EF, the first 2 options are roughly equivalent* but the 3rd option has a different behavior. It is up to us to know (or to determine) whether we need to link to an existing Gender object or to add a new one to the data store.

Exactly the same principle applies to your 1:Many relationships too:

profile.Languages = new List<Languages>
{
    new Language{Name = "Spanish"}
}

creates a new row in the languages table, whereas:

var spanish = context.Languages.First(l=>l.Name == "Spanish");
profile.Languages = new List<Languages>
{
    spanish
}

finds the existing row with name 'Spanish' then updates the navigational property.

If you have a list of language ids stored in a variable you want to update the navigation property with the associated language objects you could do:

var languagesToAdd = context.Languages.Where(l=>languageIds.Contains(l.ID)).ToList();
profile.Languages = languagesToAdd;

*Of course there is a performance penalty with option 2 if you don't already have an attached Gender object in memory because fetching the data, for example, by calling .First() as in my example, would (of course) need another round trip to the database.

Stewart_R
  • 13,764
  • 11
  • 60
  • 106
  • Thanks for the answer! Let's say I want to do option 1: Update the Foreign Key field in the parent object....but for an n:n relationship? I have a list of languageIDs (not language objects) and I want to update the Profile with my list of Language IDs. With the case of Gender, I have GenderID but with Languages I don't have LanguageIDs. – Eitan Jan 04 '16 at 19:22
  • When I call context.Languages.Where(...., it queries the database. I can't just update the IDs without the extra hit to the DB? – Eitan Jan 04 '16 at 19:49
  • You can mark all mapped properties virtual to have foreign key and navigation property always in sync! [See my question](http://stackoverflow.com/q/39095049/143684). Also, if not using this, updating both foreign key and navigation property to different things should throw an exception on `SaveChanges()`, as I've [read elsewhere](https://coding.abel.nu/2012/03/ef-code-first-navigation-properties-and-foreign-keys/). – ygoe Aug 26 '16 at 16:44