Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Entity Framework: I set the foreign key, SaveChanges then access the navigation property, but it doesn't load the related entity. Why not?

I am using this Entity class with Entity Framework 5 Code First:

public class Survey {     [DatabaseGenerated(DatabaseGeneratedOption.Identity)]     public int ID { get; set; }      public string SurveyName { get; set; }      [Required]     public int ClientID { get; set; }      [ForeignKey("ClientID")]     public virtual Client Client { get; set; } } 

And in my Controller's Create method I do this:

    Survey entity = new Survey()     {         SurveyName = "Test Name",         ClientID = 4     };     db.Surveys.Add(entity);     db.SaveChanges();     Client c1 = entity.Client;                    //Why is this null?     Client c2 = db.Clients.Find(entity.ClientID); //But this isn't?      string s2 = c2.ClientName;     string s1 = c1.ClientName;   //null reference thrown here 

The Client navigation property remains null after SaveChanges. I expected the call to load the Client from the database because the foreign key exists. Why didn't it do that?

EDIT The code here comes from when my controllers were dependent on DbContext. Not long after I got this working I re-factored the code to use repositories and a unit of work. Part of that move was driven by the fact that it just felt wrong to use Create when I wanted to use new. What happened then was that I hit a problem with how to ensure proxies are created when using the repository pattern.

like image 802
Colin Avatar asked Mar 21 '13 16:03

Colin


People also ask

How do I use navigation property in Entity Framework?

A navigation property is an optional property on an entity type that allows for navigation from one end of an association to the other end. Unlike other properties, navigation properties do not carry data. A navigation property definition includes the following: A name.

How do I use SaveChanges in Entity Framework?

The SaveChanges method of the DbContext prepares the Insert , Update & Delete Queries. It does so by tracking the changes to each of the entities' Context is tracking. Whenever we query the database for entities, the Context retrieves them and mark the entity as Unchanged .

How do you set a foreign key in an entity?

The [ForeignKey(name)] attribute can be applied in three ways: [ForeignKey(NavigationPropertyName)] on the foreign key scalar property in the dependent entity. [ForeignKey(ForeignKeyPropertyName)] on the related reference navigation property in the dependent entity.

What does the DbContext SaveChanges () method return?

Returns. The number of state entries written to the underlying database. This can include state entries for entities and/or relationships.

How does the Entity Framework use foreign key values?

The database has only foreign key values, so when EF Core creates an entity instance from the database it uses the foreign key values to set reference navigations and add entities to collection navigations as appropriate. For example, consider a query for blogs and its associated posts and assets:

How are EF Core Navigations aligned with foreign key values?

See Relationships for more information on how to model and configure relationships. EF Core keeps navigations in alignment with foreign key values and vice versa. That is, if a foreign key value changes such that it now refers to a different principal/parent entity, then the navigations are updated to reflect this change.

Does Entity Framework load navigation property after adding object C #Entity-Framework?

Knowledge Base Online Examples entity framework does not load navigation property after adding object c#entity-frameworkentity-framework-6 Question I have MVC project and EF6 model.

What are navigation properties in EF relationships?

Relationships in EF. Every object can have a navigation property for every relationship in which it participates. Navigation properties allow you to navigate and manage relationships in both directions, returning either a reference object (if the multiplicity is either one or zero-or-one) or a collection (if the multiplicity is many).


2 Answers

To ensure that lazy loading of a navigation property will work after you've created the parent you must not create the Survey with the new operator but create it by means of the context instance because it will instantiate a dynamic proxy that is capable to lazily load the related Client. That's what the DbSet<T>.Create() method is for:

Survey entity = db.Surveys.Create();  entity.SurveyName = "Test Name"; entity.ClientID = 4;  db.Surveys.Add(entity); db.SaveChanges();  Client c1 = entity.Client; string s1 = c1.ClientName; // will work now if a Client with ID 4 exists in the DB 

Just to emphasize: It's not the line entity.ClientID = 4; or db.Surveys.Add(entity); or db.SaveChanges that loads the client from the DB, but the line Client c1 = entity.Client; (lazy loading).

like image 193
Slauma Avatar answered Oct 04 '22 21:10

Slauma


Like @NicholasButler said, calling SaveChanges does what it says on the tin - you can see this if you debug your code: the Intellitrace output will show the SQL it has generated for the insert/update you are persisting, but there will be no subsequent select.

Keep in mind that unless you are eager loading (using the Include method), related entities are not loaded when performing a retrieval, so it stands to reason that creating/updating them wouldn't either.

The Entity Framework (from I think versions 4.1 and up) supports lazy loading. What this means is that if it's enabled, code like Client c1 = entity.Client; should load up that Client object. To be clear, this operation is not directly related to the SaveChanges call.

It would pay to check whether db.Configuration.LazyLoadingEnabled is set to true. If not, try setting it to be true and see if Client c1 = entity.Client; is still null.

In short, calling SaveChanges does not trigger a load, but if lazy loading is enabled, accessing entity.Client should trigger a load of the entity if it hasn't already been loaded.

Edit:

I should've though of this earlier, but you aren't going to be getting lazy loading on your Survey entity object. The reason is that EF works its lazy loading magic by creating a class derived from your one but overriding the properties marked as virtual to support lazy loading. It does this when you perform a retrieval, so your entity object will not lazy load anything as it stands.

Try this just after your call to SaveChanges:

Survey entity2 = db.Surveys.Find(entity.ID); Client c1 = entity2.Client; 

This should exhibit the behaviour you are after.

like image 23
nick_w Avatar answered Oct 04 '22 21:10

nick_w