问题
From the documentation:
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.
Entities setup:
public class Page{
public Page () {
Event = new HashSet<Event>();
}
[Key]
public int Id { get; set; }
public string Title { get; set; }
public string Content { get; set; } // don't want to retrieve, too large
public ICollection<Event> Event { get; set; }
}
public class Event{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public string Type { get; set; }
public Page Page { get; set; }
}
The context is set up with a One-To-Many relationship.
These are the queries I run, one after the other:
var pages = _dbContext.Page.Select(page => new Page
{
Id = page.Id,
Title = page.Title
}).ToList();
var events = _dbContent.Event.ToList();
I expect each Page
to have the Events
collection populated (and vice-versa for Event
with the Page
reference), but the fix-up doesn't happen (Page
in Event
is null
, and Event
in Page
is null
).
If I replace the first query by this, then the fix-up works:
var pages = _dbContext.Page.ToList();
So it seems that with projection the fix-up doesn't happen. The reason I split this in 2 queries was to avoid using something like Include
which would make a huge join and duplicate plenty of data.
Is there any way around that? Do I need to do the fix-up manually myself?
回答1:
When you project into a new type yourself in the query, EF Core does not track the object coming out of the query even if they are of type an entity which is part of Model. This is by design.
Since in your case Page
s are not getting tracked, Events
have nothing to do fixup with. Hence you are seeing null navigation properties.
This behavior was same in previous version (EF6). The main reason for not tracking is, as in your case, you are creating new Page
without loading Content
. If we track the new entity then it will have Content
set to null
(default(string)). If you mark this whole entity as modified then SaveChanges
will end up saving null value in Content
column in database. This would cause data loss. Due to minor error could cause major issue like data loss, EF Core does not track entities by default. Another reason is weak entity types (or complex types in EF6) which share CLR type with other entities but uniquely identified through Parent
type, if you project out such entity then EF Core cannot figure out which entity type it is without parent information.
You could put those entities in changetracker by calling Attach
method, which will cause fix up and you will get desired behavior. Be careful not to save them.
In general the scenario you want is useful. This issue is tracking support for that in EF Core.
回答2:
I don't think that should work. Did you verify this behavior worked in previous versions of EntityFramework? Since, you aren't pulling out the full entity, and only properties of it, and then passing it into a new Entity, you are essentially just Selecting properties and creating a new Entity.
If you would like this to attach you can manually call the Attach Method after selecting your page
var pages = _dbContext.Page.Select(page => new Page
{
Id = page.Id,
Title = page.Title
}).ToList();
pages.ForEach(p => _dbContext.Page.Attach(p));
Keep in mind that if you call SaveChanges After this you will lose the unloaded properties, so only use this when calling Get
Methods
来源:https://stackoverflow.com/questions/49932792/ef-core-fix-up-when-querying-subset-of-columns