.NET Core - Entity Framework - 预加载 Include() 的意外行为
.NET Core - Entity Framework - Unexpected Behaviour with Eager Loading Include()
初始查询
如果我 运行 以下查询我得到以下结果,CityTranslation.IdFkLanguageNavigation
将是 null
。
CityTranslation.IdFkCityNavigation
另一方面将被填充。考虑到它们都在同一层级。
var data = await _context.City
.Include(x => x.CityTranslation)
.Include(c => c.IdFkCountryNavigation)
.ToListAsync();
我尝试在 .Include(x => x.CityTranslation)
之后使用 ThenInclude()
,但它不允许我使用语言 属性。
修改后的查询
通过下面的查询,我确实得到了一些意想不到的结果。一旦我通过 data2
进行调试,我的 第一个查询结果 中的 Language
导航 属性 突然被填充!
这仅在我在第二个查询中调用 ToList()
时有效,否则它仍然不会被填充。我必须假设数据在整个 EF 上下文中共享,但我如何在不向数据库发送第二个查询的情况下利用此行为?我真的被这种行为惊呆了,如果有任何参考或解释,我将不胜感激。
var data = await _context.City
.Include(x => x.CityTranslation)
.Include(c => c.IdFkCountryNavigation).ToListAsync();
var data2 = _context.CityTranslation.Include(c => c.IdFkLanguageNavigation).ToListAsync();
上下文
我正在使用 Microsoft.EntityFrameworkCore.SqlServer
版本 1.1.2
城市
public partial class City
{
public City()
{
CityTranslation = new HashSet<CityTranslation>();
}
public int IdPkCity { get; set; }
public string Id { get; set; }
public string Code { get; set; }
public string Latitude { get; set; }
public string Longitude { get; set; }
public string TimeZone { get; set; }
public string Uri { get; set; }
public string Name { get; set; }
public string Language { get; set; }
public int? IdFkCountry { get; set; }
public virtual ICollection<CityTranslation> CityTranslation { get; set; }
public virtual Country IdFkCountryNavigation { get; set; }
城市翻译
public partial class CityTranslation
{
public int IdPkCityTranslation { get; set; }
public string Translation { get; set; }
public int IdFkCity { get; set; }
public int IdFkLanguage { get; set; }
public virtual City IdFkCityNavigation { get; set; }
public virtual Language IdFkLanguageNavigation { get; set; }
}
语言
public partial class Language
{
public Language()
{
AirportTranslation = new HashSet<AirportTranslation>();
CityTranslation = new HashSet<CityTranslation>();
CountryTranslation = new HashSet<CountryTranslation>();
}
public int IdPkLanguage { get; set; }
public string Name { get; set; }
public string Code { get; set; }
public virtual ICollection<AirportTranslation> ATranslation{ get; set; }
public virtual ICollection<CityTranslation> CTranslation { get; set; }
public virtual ICollection<CountryTranslation> C2Translation { get; set; }
}
这行得通,只需粘贴即可。
// Hits the database only once
var data = await _context.City
.Include(x => x.CityTranslation)
.ThenInclude(x => x.IdFkLanguageNavigation)
.ToListAsync();
现在,至于为什么你的第二个查询加载第一个查询的 Language
导航 属性,你必须研究 EntityFramework 是如何作为一个整体工作的,继续阅读显式加载。
这里有一个显式加载的例子。
// Hits the database once.
var data = await _context.City
.Include(x => x.CityTranslation)
.ToListAsync();
var cityTranslationIds = data.Select(x => x.CityTranslation.IdPkCityTranslation);
// Hits the database the second time.
// Language navigation property will be loaded onto the data variable above
_context.Language
.Where(x => cityTranslateIds.Contains(x.IdPkLanguage))
.Load();
// Your second query, what you did here is essentially the same as the above's Load(),
// but the Load() is better suited for your intention.
var data2 = await _context.CityTranslation
.Include(c => c.IdFkLanguageNavigation)
.ToListAsync();
根据情况,您需要在预加载和显式加载之间做出选择以获得最佳性能。
初始查询
如果我 运行 以下查询我得到以下结果,CityTranslation.IdFkLanguageNavigation
将是 null
。
CityTranslation.IdFkCityNavigation
另一方面将被填充。考虑到它们都在同一层级。
var data = await _context.City
.Include(x => x.CityTranslation)
.Include(c => c.IdFkCountryNavigation)
.ToListAsync();
我尝试在 .Include(x => x.CityTranslation)
之后使用 ThenInclude()
,但它不允许我使用语言 属性。
修改后的查询
通过下面的查询,我确实得到了一些意想不到的结果。一旦我通过 data2
进行调试,我的 第一个查询结果 中的 Language
导航 属性 突然被填充!
这仅在我在第二个查询中调用 ToList()
时有效,否则它仍然不会被填充。我必须假设数据在整个 EF 上下文中共享,但我如何在不向数据库发送第二个查询的情况下利用此行为?我真的被这种行为惊呆了,如果有任何参考或解释,我将不胜感激。
var data = await _context.City
.Include(x => x.CityTranslation)
.Include(c => c.IdFkCountryNavigation).ToListAsync();
var data2 = _context.CityTranslation.Include(c => c.IdFkLanguageNavigation).ToListAsync();
上下文
我正在使用 Microsoft.EntityFrameworkCore.SqlServer
版本 1.1.2
城市
public partial class City
{
public City()
{
CityTranslation = new HashSet<CityTranslation>();
}
public int IdPkCity { get; set; }
public string Id { get; set; }
public string Code { get; set; }
public string Latitude { get; set; }
public string Longitude { get; set; }
public string TimeZone { get; set; }
public string Uri { get; set; }
public string Name { get; set; }
public string Language { get; set; }
public int? IdFkCountry { get; set; }
public virtual ICollection<CityTranslation> CityTranslation { get; set; }
public virtual Country IdFkCountryNavigation { get; set; }
城市翻译
public partial class CityTranslation
{
public int IdPkCityTranslation { get; set; }
public string Translation { get; set; }
public int IdFkCity { get; set; }
public int IdFkLanguage { get; set; }
public virtual City IdFkCityNavigation { get; set; }
public virtual Language IdFkLanguageNavigation { get; set; }
}
语言
public partial class Language
{
public Language()
{
AirportTranslation = new HashSet<AirportTranslation>();
CityTranslation = new HashSet<CityTranslation>();
CountryTranslation = new HashSet<CountryTranslation>();
}
public int IdPkLanguage { get; set; }
public string Name { get; set; }
public string Code { get; set; }
public virtual ICollection<AirportTranslation> ATranslation{ get; set; }
public virtual ICollection<CityTranslation> CTranslation { get; set; }
public virtual ICollection<CountryTranslation> C2Translation { get; set; }
}
这行得通,只需粘贴即可。
// Hits the database only once
var data = await _context.City
.Include(x => x.CityTranslation)
.ThenInclude(x => x.IdFkLanguageNavigation)
.ToListAsync();
现在,至于为什么你的第二个查询加载第一个查询的 Language
导航 属性,你必须研究 EntityFramework 是如何作为一个整体工作的,继续阅读显式加载。
这里有一个显式加载的例子。
// Hits the database once.
var data = await _context.City
.Include(x => x.CityTranslation)
.ToListAsync();
var cityTranslationIds = data.Select(x => x.CityTranslation.IdPkCityTranslation);
// Hits the database the second time.
// Language navigation property will be loaded onto the data variable above
_context.Language
.Where(x => cityTranslateIds.Contains(x.IdPkLanguage))
.Load();
// Your second query, what you did here is essentially the same as the above's Load(),
// but the Load() is better suited for your intention.
var data2 = await _context.CityTranslation
.Include(c => c.IdFkLanguageNavigation)
.ToListAsync();
根据情况,您需要在预加载和显式加载之间做出选择以获得最佳性能。