Entity Framework: I set the foreign key, SaveChanges then accesses the navigation property, but it does not load the associated object. 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 Create Control 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 was expecting a call to download the Client from the database, since there is a foreign key. Why didnโ€™t he?

EDIT The code here occurs when my controllers are dependent on DbContext . Shortly after I got this work, I reused the code to use repositories and units of work. Part of this movement was due to the fact that it was simply wrong to use Create when I wanted to use new . Then what happened was that I ran into the problem of how to ensure that proxies are created when using the repository template .

+33
entity-framework-5 ef-code-first lazy-loading
Mar 21 '13 at 16:22
source share
4 answers

To ensure that the lazy loading of the navigation property will work after you create the parent, you should not create a Survey with the new operator, but create it with a context instance, since it will create a dynamic proxy instance that can lazily load the associated Client . This is the DbSet<T>.Create() method 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 emphasize: this is not a string entity.ClientID = 4; or db.Surveys.Add(entity); or db.SaveChanges , which loads the client from the database, and the line Client c1 = entity.Client; (lazy loading).

+51
Mar 27 '13 at 20:44
source share

As @NicholasButler said, calling SaveChanges does what it says on the gesture - you can see it if you debug your code: the Intellitrace output will show the SQL that it generated for the insert / update that you save, but there will be no further selection.

Keep in mind that if you do not want to download (using the Include method), related objects are not loaded when performing a search, so it is quite reasonable that they will not be created and updated either.

Entity Framework (since version 4.1 and higher) supports lazy loading. This means that if it is enabled, a code of type Client c1 = entity.Client; should load this Client object. To be clear, this operation is not directly related to calling SaveChanges .

Pays to check if db.Configuration.LazyLoadingEnabled is db.Configuration.LazyLoadingEnabled to true . If not, try setting it to true and see if Client c1 = entity.Client; remains Client c1 = entity.Client; null

In short, calling SaveChanges does not cause loading, but if lazy loading is enabled, access to entity.Client should initiate loading of the object if it has not already been loaded.

Edit:

I should have even talked about this before, but you wonโ€™t get the lazy load on your Survey entity . The reason is that EF works with lazy loading magic, creating a class derived from yours, but overriding properties marked as virtual to support lazy loading. It does this when you do a search, so your entity will not be too lazy to load whatever it is.

Try this right after your call to SaveChanges :

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

This should be the behavior that you are after.

+10
Mar 27 '13 at 9:42 on
source share

You need to define all the properties of the Survey class as virtual in order to enable lazy loading.

See http://msdn.microsoft.com/en-us/library/vstudio/dd468057(v=vs.100).aspx for more details.

0
Mar 21 '13 at 16:29
source share

I was expecting a call to download the Client from the database, since there is a foreign key. Why didnโ€™t he?

It did not do this because you did not ask it. After calling SaveChanges() EF has no data in the reference string, and it will not make a potentially redundant database call to get it.

Calling db.Clients.Find(... tells EF to db.Clients.Find(... and retrieve a row from the database, so it returns an object.

-2
Mar 21 '13 at 16:59
source share



All Articles