EF Core Eager Loading nested collections
I'm trying to load a related modal in Entity Framework Core but for some reason there's a nested collection being loaded when I haven't asked for it in my Include()
call.
Here's my two models -
Driver.cs
public partial class Driver : IBaseEntity
{
public short DriverId { get; set; }
public string Surname { get; set; }
public string Initials { get; set; }
public byte DriverStatusTypeId { get; set; }
public DriverStatusType DriverStatusType { get; set; }
}
DriverStatusType.cs
public partial class DriverStatusType
{
public DriverStatusType()
{
Drivers = new HashSet<Driver>();
}
public byte DriverStatusTypeId { get; set; }
public string DriverStatusTypeName { get; set; }
public string Description { get; set; }
public ICollection<Driver> Drivers { get; set; }
}
DriversService.cs
public class DriverService : IDriverService
{
public DriverService(MyContext context)
{
Context = context;
}
public MyContext Context { get; }
public async Task<IEnumerable<Driver>> GetAllDrivers()
{
var drivers = await Context
.Drivers
.Include(d => d.DriverStatusType)
.toListAsync();
return drivers;
}
public async Task<Driver> GetDriverById(int id)
{
var driver = await Context
.Drivers
.Include(d => d.DriverStatusType)
.Where(d => d.DriverId == id)
.FirstOrDefaultAsync();
return driver;
}
}
Now when I call the GetDriverById(int id)
method from my controller I get back what I'm expecting -
{
"driverId": 1,
"surname": "Stark",
"initials": "T",
"driverStatusTypeId": 2,
"driverStatusType": {
"driverStatusTypeId": 2,
"driverStatusTypeName": "Available",
"description": "This driver is available",
"drivers":
}
}
However the GetAllDrivers()
method is returning the nested drivers
collection which means the data I'm getting back is huge -
[
{
"driverId": 1,
"surname": "Stark",
"initials": "T",
"displayText": "Tony Stark",
"driverStatusTypeId": 2,
"driverStatusType": {
"driverStatusTypeId": 2,
"driverStatusTypeName": "Available",
"description": "This driver is available",
"drivers": [
{
"driverId": 2,
"surname": "Rogers",
"initials": "S",
"driverStatusTypeId": 2
},
{
"driverId": 3,
"surname": "Romanoff",
"initials": "N",
"driverStatusTypeId": 2
},
{
"driverId": 4,
"surname": "Banner",
"initials": "B",
"driverStatusTypeId": 2
},
...
I thought the idea of eager loading was to only include the related models you specify in the include statement but it seems this is not the case. Could someone explain what's happening here?
c# entity-framework-core
add a comment |
I'm trying to load a related modal in Entity Framework Core but for some reason there's a nested collection being loaded when I haven't asked for it in my Include()
call.
Here's my two models -
Driver.cs
public partial class Driver : IBaseEntity
{
public short DriverId { get; set; }
public string Surname { get; set; }
public string Initials { get; set; }
public byte DriverStatusTypeId { get; set; }
public DriverStatusType DriverStatusType { get; set; }
}
DriverStatusType.cs
public partial class DriverStatusType
{
public DriverStatusType()
{
Drivers = new HashSet<Driver>();
}
public byte DriverStatusTypeId { get; set; }
public string DriverStatusTypeName { get; set; }
public string Description { get; set; }
public ICollection<Driver> Drivers { get; set; }
}
DriversService.cs
public class DriverService : IDriverService
{
public DriverService(MyContext context)
{
Context = context;
}
public MyContext Context { get; }
public async Task<IEnumerable<Driver>> GetAllDrivers()
{
var drivers = await Context
.Drivers
.Include(d => d.DriverStatusType)
.toListAsync();
return drivers;
}
public async Task<Driver> GetDriverById(int id)
{
var driver = await Context
.Drivers
.Include(d => d.DriverStatusType)
.Where(d => d.DriverId == id)
.FirstOrDefaultAsync();
return driver;
}
}
Now when I call the GetDriverById(int id)
method from my controller I get back what I'm expecting -
{
"driverId": 1,
"surname": "Stark",
"initials": "T",
"driverStatusTypeId": 2,
"driverStatusType": {
"driverStatusTypeId": 2,
"driverStatusTypeName": "Available",
"description": "This driver is available",
"drivers":
}
}
However the GetAllDrivers()
method is returning the nested drivers
collection which means the data I'm getting back is huge -
[
{
"driverId": 1,
"surname": "Stark",
"initials": "T",
"displayText": "Tony Stark",
"driverStatusTypeId": 2,
"driverStatusType": {
"driverStatusTypeId": 2,
"driverStatusTypeName": "Available",
"description": "This driver is available",
"drivers": [
{
"driverId": 2,
"surname": "Rogers",
"initials": "S",
"driverStatusTypeId": 2
},
{
"driverId": 3,
"surname": "Romanoff",
"initials": "N",
"driverStatusTypeId": 2
},
{
"driverId": 4,
"surname": "Banner",
"initials": "B",
"driverStatusTypeId": 2
},
...
I thought the idea of eager loading was to only include the related models you specify in the include statement but it seems this is not the case. Could someone explain what's happening here?
c# entity-framework-core
Aredrivers
already in memory from a previous call? What is the scope of your DbContext? What is the SQL query generated by EF forGetAllDrivers()
– Adam Vincent
Nov 26 '18 at 12:34
add a comment |
I'm trying to load a related modal in Entity Framework Core but for some reason there's a nested collection being loaded when I haven't asked for it in my Include()
call.
Here's my two models -
Driver.cs
public partial class Driver : IBaseEntity
{
public short DriverId { get; set; }
public string Surname { get; set; }
public string Initials { get; set; }
public byte DriverStatusTypeId { get; set; }
public DriverStatusType DriverStatusType { get; set; }
}
DriverStatusType.cs
public partial class DriverStatusType
{
public DriverStatusType()
{
Drivers = new HashSet<Driver>();
}
public byte DriverStatusTypeId { get; set; }
public string DriverStatusTypeName { get; set; }
public string Description { get; set; }
public ICollection<Driver> Drivers { get; set; }
}
DriversService.cs
public class DriverService : IDriverService
{
public DriverService(MyContext context)
{
Context = context;
}
public MyContext Context { get; }
public async Task<IEnumerable<Driver>> GetAllDrivers()
{
var drivers = await Context
.Drivers
.Include(d => d.DriverStatusType)
.toListAsync();
return drivers;
}
public async Task<Driver> GetDriverById(int id)
{
var driver = await Context
.Drivers
.Include(d => d.DriverStatusType)
.Where(d => d.DriverId == id)
.FirstOrDefaultAsync();
return driver;
}
}
Now when I call the GetDriverById(int id)
method from my controller I get back what I'm expecting -
{
"driverId": 1,
"surname": "Stark",
"initials": "T",
"driverStatusTypeId": 2,
"driverStatusType": {
"driverStatusTypeId": 2,
"driverStatusTypeName": "Available",
"description": "This driver is available",
"drivers":
}
}
However the GetAllDrivers()
method is returning the nested drivers
collection which means the data I'm getting back is huge -
[
{
"driverId": 1,
"surname": "Stark",
"initials": "T",
"displayText": "Tony Stark",
"driverStatusTypeId": 2,
"driverStatusType": {
"driverStatusTypeId": 2,
"driverStatusTypeName": "Available",
"description": "This driver is available",
"drivers": [
{
"driverId": 2,
"surname": "Rogers",
"initials": "S",
"driverStatusTypeId": 2
},
{
"driverId": 3,
"surname": "Romanoff",
"initials": "N",
"driverStatusTypeId": 2
},
{
"driverId": 4,
"surname": "Banner",
"initials": "B",
"driverStatusTypeId": 2
},
...
I thought the idea of eager loading was to only include the related models you specify in the include statement but it seems this is not the case. Could someone explain what's happening here?
c# entity-framework-core
I'm trying to load a related modal in Entity Framework Core but for some reason there's a nested collection being loaded when I haven't asked for it in my Include()
call.
Here's my two models -
Driver.cs
public partial class Driver : IBaseEntity
{
public short DriverId { get; set; }
public string Surname { get; set; }
public string Initials { get; set; }
public byte DriverStatusTypeId { get; set; }
public DriverStatusType DriverStatusType { get; set; }
}
DriverStatusType.cs
public partial class DriverStatusType
{
public DriverStatusType()
{
Drivers = new HashSet<Driver>();
}
public byte DriverStatusTypeId { get; set; }
public string DriverStatusTypeName { get; set; }
public string Description { get; set; }
public ICollection<Driver> Drivers { get; set; }
}
DriversService.cs
public class DriverService : IDriverService
{
public DriverService(MyContext context)
{
Context = context;
}
public MyContext Context { get; }
public async Task<IEnumerable<Driver>> GetAllDrivers()
{
var drivers = await Context
.Drivers
.Include(d => d.DriverStatusType)
.toListAsync();
return drivers;
}
public async Task<Driver> GetDriverById(int id)
{
var driver = await Context
.Drivers
.Include(d => d.DriverStatusType)
.Where(d => d.DriverId == id)
.FirstOrDefaultAsync();
return driver;
}
}
Now when I call the GetDriverById(int id)
method from my controller I get back what I'm expecting -
{
"driverId": 1,
"surname": "Stark",
"initials": "T",
"driverStatusTypeId": 2,
"driverStatusType": {
"driverStatusTypeId": 2,
"driverStatusTypeName": "Available",
"description": "This driver is available",
"drivers":
}
}
However the GetAllDrivers()
method is returning the nested drivers
collection which means the data I'm getting back is huge -
[
{
"driverId": 1,
"surname": "Stark",
"initials": "T",
"displayText": "Tony Stark",
"driverStatusTypeId": 2,
"driverStatusType": {
"driverStatusTypeId": 2,
"driverStatusTypeName": "Available",
"description": "This driver is available",
"drivers": [
{
"driverId": 2,
"surname": "Rogers",
"initials": "S",
"driverStatusTypeId": 2
},
{
"driverId": 3,
"surname": "Romanoff",
"initials": "N",
"driverStatusTypeId": 2
},
{
"driverId": 4,
"surname": "Banner",
"initials": "B",
"driverStatusTypeId": 2
},
...
I thought the idea of eager loading was to only include the related models you specify in the include statement but it seems this is not the case. Could someone explain what's happening here?
c# entity-framework-core
c# entity-framework-core
asked Nov 26 '18 at 9:36
Chris EdgingtonChris Edgington
1,18911224
1,18911224
Aredrivers
already in memory from a previous call? What is the scope of your DbContext? What is the SQL query generated by EF forGetAllDrivers()
– Adam Vincent
Nov 26 '18 at 12:34
add a comment |
Aredrivers
already in memory from a previous call? What is the scope of your DbContext? What is the SQL query generated by EF forGetAllDrivers()
– Adam Vincent
Nov 26 '18 at 12:34
Are
drivers
already in memory from a previous call? What is the scope of your DbContext? What is the SQL query generated by EF for GetAllDrivers()
– Adam Vincent
Nov 26 '18 at 12:34
Are
drivers
already in memory from a previous call? What is the scope of your DbContext? What is the SQL query generated by EF for GetAllDrivers()
– Adam Vincent
Nov 26 '18 at 12:34
add a comment |
1 Answer
1
active
oldest
votes
I thought the idea of eager loading was to only include the related models you specify in the include statement but it seems this is not the case. Could someone explain what's happening here?
You are right, that's not the case. The idea of eager loading is to ensure the related data you specify is loaded. It doesn't mean/guarantee that related data won't be included.
It's partially explained in the Loading Related Data section of the EF Core documentation:
Tip
Entity Framework Core will automatically fix-up navigation properties to any other entities that were previously loaded into the context instance. So even if you don't explicitly include the data for a navigation property, the property may still be populated if some or all of the related entities were previously loaded.
The navigation property fix-up means that anytime entity instance is materialized, all related navigation properties are updated to reflect it, for instance Driver
is added to Driver.DriverStatusType.Drivers
and vice versa.
Note that when using tracking queries, this might happen after the non including query is materialized (ToList()
) because change tracker keeps track of object references and automatically updates them when you perform other tracking queries.
Another effect of that fix-up process is that when you include one of the ends of the relationship, the inverse navigation property of the other end is automatically populated.
So even if the first case the Drivers
property should be populated and contain single item. And this is what actually happening in my clean tests, don't know why you are getting difference - may be the serializer is hiding it?
Anyway, all that means that you can't really control the content of the navigation properties. The only way you can control exactly what are you returning is to use special DTO/ViewModel etc. classes and projection (Select
).
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53478225%2fef-core-eager-loading-nested-collections%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
I thought the idea of eager loading was to only include the related models you specify in the include statement but it seems this is not the case. Could someone explain what's happening here?
You are right, that's not the case. The idea of eager loading is to ensure the related data you specify is loaded. It doesn't mean/guarantee that related data won't be included.
It's partially explained in the Loading Related Data section of the EF Core documentation:
Tip
Entity Framework Core will automatically fix-up navigation properties to any other entities that were previously loaded into the context instance. So even if you don't explicitly include the data for a navigation property, the property may still be populated if some or all of the related entities were previously loaded.
The navigation property fix-up means that anytime entity instance is materialized, all related navigation properties are updated to reflect it, for instance Driver
is added to Driver.DriverStatusType.Drivers
and vice versa.
Note that when using tracking queries, this might happen after the non including query is materialized (ToList()
) because change tracker keeps track of object references and automatically updates them when you perform other tracking queries.
Another effect of that fix-up process is that when you include one of the ends of the relationship, the inverse navigation property of the other end is automatically populated.
So even if the first case the Drivers
property should be populated and contain single item. And this is what actually happening in my clean tests, don't know why you are getting difference - may be the serializer is hiding it?
Anyway, all that means that you can't really control the content of the navigation properties. The only way you can control exactly what are you returning is to use special DTO/ViewModel etc. classes and projection (Select
).
add a comment |
I thought the idea of eager loading was to only include the related models you specify in the include statement but it seems this is not the case. Could someone explain what's happening here?
You are right, that's not the case. The idea of eager loading is to ensure the related data you specify is loaded. It doesn't mean/guarantee that related data won't be included.
It's partially explained in the Loading Related Data section of the EF Core documentation:
Tip
Entity Framework Core will automatically fix-up navigation properties to any other entities that were previously loaded into the context instance. So even if you don't explicitly include the data for a navigation property, the property may still be populated if some or all of the related entities were previously loaded.
The navigation property fix-up means that anytime entity instance is materialized, all related navigation properties are updated to reflect it, for instance Driver
is added to Driver.DriverStatusType.Drivers
and vice versa.
Note that when using tracking queries, this might happen after the non including query is materialized (ToList()
) because change tracker keeps track of object references and automatically updates them when you perform other tracking queries.
Another effect of that fix-up process is that when you include one of the ends of the relationship, the inverse navigation property of the other end is automatically populated.
So even if the first case the Drivers
property should be populated and contain single item. And this is what actually happening in my clean tests, don't know why you are getting difference - may be the serializer is hiding it?
Anyway, all that means that you can't really control the content of the navigation properties. The only way you can control exactly what are you returning is to use special DTO/ViewModel etc. classes and projection (Select
).
add a comment |
I thought the idea of eager loading was to only include the related models you specify in the include statement but it seems this is not the case. Could someone explain what's happening here?
You are right, that's not the case. The idea of eager loading is to ensure the related data you specify is loaded. It doesn't mean/guarantee that related data won't be included.
It's partially explained in the Loading Related Data section of the EF Core documentation:
Tip
Entity Framework Core will automatically fix-up navigation properties to any other entities that were previously loaded into the context instance. So even if you don't explicitly include the data for a navigation property, the property may still be populated if some or all of the related entities were previously loaded.
The navigation property fix-up means that anytime entity instance is materialized, all related navigation properties are updated to reflect it, for instance Driver
is added to Driver.DriverStatusType.Drivers
and vice versa.
Note that when using tracking queries, this might happen after the non including query is materialized (ToList()
) because change tracker keeps track of object references and automatically updates them when you perform other tracking queries.
Another effect of that fix-up process is that when you include one of the ends of the relationship, the inverse navigation property of the other end is automatically populated.
So even if the first case the Drivers
property should be populated and contain single item. And this is what actually happening in my clean tests, don't know why you are getting difference - may be the serializer is hiding it?
Anyway, all that means that you can't really control the content of the navigation properties. The only way you can control exactly what are you returning is to use special DTO/ViewModel etc. classes and projection (Select
).
I thought the idea of eager loading was to only include the related models you specify in the include statement but it seems this is not the case. Could someone explain what's happening here?
You are right, that's not the case. The idea of eager loading is to ensure the related data you specify is loaded. It doesn't mean/guarantee that related data won't be included.
It's partially explained in the Loading Related Data section of the EF Core documentation:
Tip
Entity Framework Core will automatically fix-up navigation properties to any other entities that were previously loaded into the context instance. So even if you don't explicitly include the data for a navigation property, the property may still be populated if some or all of the related entities were previously loaded.
The navigation property fix-up means that anytime entity instance is materialized, all related navigation properties are updated to reflect it, for instance Driver
is added to Driver.DriverStatusType.Drivers
and vice versa.
Note that when using tracking queries, this might happen after the non including query is materialized (ToList()
) because change tracker keeps track of object references and automatically updates them when you perform other tracking queries.
Another effect of that fix-up process is that when you include one of the ends of the relationship, the inverse navigation property of the other end is automatically populated.
So even if the first case the Drivers
property should be populated and contain single item. And this is what actually happening in my clean tests, don't know why you are getting difference - may be the serializer is hiding it?
Anyway, all that means that you can't really control the content of the navigation properties. The only way you can control exactly what are you returning is to use special DTO/ViewModel etc. classes and projection (Select
).
answered Nov 26 '18 at 13:39
Ivan StoevIvan Stoev
103k774126
103k774126
add a comment |
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53478225%2fef-core-eager-loading-nested-collections%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Are
drivers
already in memory from a previous call? What is the scope of your DbContext? What is the SQL query generated by EF forGetAllDrivers()
– Adam Vincent
Nov 26 '18 at 12:34