How do I tell the Find method of a DBContext that it should eagerly load the navigation property/entity?
I have the following code which deletes the association to a related Secondary entity:
Person primary = db.People.Find(Id);
if (primary == null)
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
// This line is required to load the related entity
db.Entry(primary).Reference("Secondary").Load();
primary.Secondary = null;
db.SaveChanges();
I had to add the line db.Entry(primary).Reference("Secondary").Load();
to get it working. I understand this is because the entity framework is using lazy loading. Can I override this in the Find method though and get rid of the extra line by using an Eager version of the Find method?
Eager loading is done by the Include
method:
db.People.Include(p => p.Secondary)
Which can be followed by FirstOrDefault(p => p.Id == id)
, for example.
Find
is a DbSet
method that first tries to find the requested entity in the context's cache. Only when it's not found there, the entity is fetched from the database.
Because of this special behavior (of Find
), Include
and Find
can't be mixed. It would be hard to define what to do when the root entity is found in the cache, but the entities to be included aren't (or only partly). Should they be fetched from the database? That would imply that Find
+ Include
would always have to query the database for the included entities, because it can't rely on the local cache to be complete. That would defeat the purpose of Find
. Or should find only include entities from the local cache if the root entity is in the local cache? That would make the result of the method ambiguous.
In ASP.Net MVC (or Web API) action methods, Find
will hardly ever be useful, because most of the times, a new context will be created and entities will be fetched from the database once. In other words, there's nothing in the cache to be returned. You may want to use the method for its succinctness, but the effect, regarding database roundtrips, is the same as FirstOrDefault()
.
To answer your original question about how to use the Find method with explicit loading (from here)?
using (var context = new BloggingContext())
{
var post = context.Posts.Find(2);
// Load the blog related to a given post.
context.Entry(post).Reference(p => p.Blog).Load();
// Load the blog related to a given post using a string.
context.Entry(post).Reference("Blog").Load();
var blog = context.Blogs.Find(1);
// Load the posts related to a given blog.
context.Entry(blog).Collection(p => p.Posts).Load();
// Load the posts related to a given blog
// using a string to specify the relationship.
context.Entry(blog).Collection("Posts").Load();
}
To add to @GertArnolds post, if you use dependency injection where you load a DbContext once per Scoped
Lifestyle, which essentially is: (from here):
Only one instance will be created by the container per web request. Use this lifestyle in ASP.NET Web Forms and ASP.NET MVC applications.
Then the DbContext sticks around for a while.
Or you can use
var result = db.Person.Include(c=>c.Secondary).FirstOrDefault(entity=>entity.Id == Id);
Use using System.Data.Entity;
for the Linq capability in the Include, otherwise you can just use the string "Secondary" like so: .Include("Secondary")
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With