Explicit Loading in EF Core is a technique we query and load the related entities with an explicit call. Explicit loading works very similar to Lazy Loading, but the loading of the related entities happens only after an explicit call to the Load
or Query
method of the related entity’s
object. In eager loading, we query the related entities along with the main entity in a single Query. In Lazy loading, EF loads the load related entities whenever we access the related Property.DbContext.Entry(...)
API
Database:
The Database for this tutorial is taken from the chinook database.Source Code:
The source code of this project available in GitHub. It also contains the script of the database
Table of Contents
Explicit Loading in Entity Framework Core
To make an explicit call to load a related entity, we use the Load
method of the related entity’s DbContext.Entry(...)
object.
To Invoke load
method, first we use the DbContext.Entry
method is used to gain access to a tracked entity. From the Entry
, we can use the Reference
or Collection
method to get the reference to the navigational property. Then you can call the Load
method to get the data from the database.
In the following example query, we retrieve five Tracks
from the database. Inside the for loop, we first get the DbContext.Entry
of the Track
entity db.Entry(track)
Now, we can get the reference to the Album
using the reference method.db.Entry(track).Reference(t => t.Album)
Finally, use the Load
method (db.Entry(track).Reference(t => t.Album).Load();
) to send the query to the database and load the related Album
data.
Now, since the album is loaded, we can access its Entry
method (db.Entry(track.Album)
) and use it load the Artist
entity. (db.Entry(track.Album).Reference(t => t.Artist).Load();
)
1 2 3 4 5 6 7 8 9 10 11 12 13 | using (ChinookContext db = new ChinookContext()) { var Tracks = db.Track.Take(5).ToList(); foreach (var track in Tracks) { db.Entry(track).Reference(t => t.Album).Load(); db.Entry(track.Album).Reference(t => t.Artist).Load(); Console.WriteLine("{0} {1} {2}", track.Album.Title, track.Name, track.Album.Artist.Name); } } |
The corresponding SQL
1 2 3 4 5 6 7 8 9 10 11 12 | SELECT TOP(@__p_0) [t].[TrackId], [t].[AlbumId], [t].[Bytes], [t].[Composer], [t].[GenreId], [t].[MediaTypeId], [t].[Milliseconds], [t].[Name], [t].[UnitPrice] FROM [Track] AS [t] SELECT [a].[AlbumId], [a].[ArtistId], [a].[Title] FROM [Album] AS [a] WHERE [a].[AlbumId] = @__p_0 SELECT [a].[ArtistId], [a].[Name] FROM [Artist] AS [a] WHERE [a].[ArtistId] = @__p_0 |
Loading Collections
We use the reference
method if the navigation property is of reference
type. In the case of collection
navigation property, the collection
method is to be used
The Track
is a collection navigation property the entity Album
. The following example retrieves the Album
and then uses the load
method to explicitly load the
that belong that album. Since it is a collection navigation property we make use of the Tracks
Collection
method
1 2 3 | db.Entry(album).Collection(t => t.Track).Load(); |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | using (ChinookContext db = new ChinookContext()) { var Albums = db.Album.Take(5).ToList(); foreach (var album in Albums) { db.Entry(album).Collection(t => t.Track).Load(); db.Entry(album).Reference(t => t.Artist).Load(); Console.WriteLine("{0} {1}", album.Title, album.Artist.Name); foreach (var track in album.Track) { Console.WriteLine("\t\t\t{0}", track.Name); } } } |
The SQL Query
1 2 3 4 5 6 7 8 9 10 11 12 | SELECT TOP(@__p_0) [a].[AlbumId], [a].[ArtistId], [a].[Title] FROM [Album] AS [a] SELECT [t].[TrackId], [t].[AlbumId], [t].[Bytes], [t].[Composer], [t].[GenreId], [t].[MediaTypeId], [t].[Milliseconds], [t].[Name], [t].[UnitPrice] FROM [Track] AS [t] WHERE [t].[AlbumId] = @__p_0 SELECT [a].[ArtistId], [a].[Name] FROM [Artist] AS [a] WHERE [a].[ArtistId] = @__p_0 |
Explicit Loading with Filtering
Unfortunately you cannot use the filter while using the Load
method. For Example the following query results in an error.
1 2 3 | db.Entry(album).Collection(t => t.Track.Where(f => f.Name.StartsWith("A"))).Load(); |
To filter out the result of explicit loading, you can use the Query
method. The query method returns the instance of IQueryable
(or Query Variable). Hence you need to call Load()
(or ToList()
, FirstOrDefault()
etc) to execute the query.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | using (ChinookContext db = new ChinookContext()) { var Albums = db.Album.Take(5).ToList(); foreach (var album in Albums) { db.Entry(album).Collection(t => t.Track).Query().Where(f => f.Name.Contains("The")).Load(); db.Entry(album).Reference(t => t.Artist).Query().Load(); //Also works //db.Entry(album).Collection(t => t.Track).Query().Where(f => f.Name.Contains("The")).ToList(); //db.Entry(album).Reference(t => t.Artist).Query().ToList(); Console.WriteLine("{0} {1}", album.Title, album.Artist.Name); foreach (var track in album.Track) { Console.WriteLine("\t\t\t{0}", track.Name); } } } |
Virtual Property
You do not have the navigation properties as virtual.for the explicit loading to work.
References
- CollectionEntry.Load Method
- CollectionEntry.Query Method
- NavigationEntry.Load Method
- NavigationEntry.Query Method
Summary
Explicit Loading uses the Load
or Query
method of the
to load related data. Here you have full control over how & when the query is sent to the database. You can also filter the query using DbContext.Entry(...)
APIwhere
before sending it to the database using the Query
method.
I have a solution I am converting from .NET Core 2.2 to 3.1. I have upgraded my Entity Framework packages. However, when trying to use .Collection() or .Reference() after loading the entry, I can only pass a string to these methods. An overload to pass in a lambda is not available. Is there an extension package I need to add to my project to get the lambda versions of .Collection() and .Reference()?
This is the code I am using that builds:
var item = _dbContext.Users.First();
var entry = _dbContext.Entry(item).Collection(“Address”);
What I am trying to use but get an error in VS Code that it cannot convert a lambda expression to a string:
var item = _dbContext.Users.First();
var entry = _dbContext.Entry(item).Collection(x => x.Address);
Check if you also updated correctly to EF Core & EF Core SQL server to 3.1.3.
No Need for any extension package