.NET核心 - 实体框架 - 意外加载的意外行为包括()

时间:2017-06-27 17:42:02

标签: c# sql entity-framework .net-core eager-loading

初始查询

如果我运行以下查询,我会得到以下结果,CityTranslation.IdFkLanguageNavigation将是null。 另一方面,CityTranslation.IdFkCityNavigation将被填充。考虑到它们都处于同一层级。

    var data = await _context.City
                        .Include(x => x.CityTranslation)
                        .Include(c => c.IdFkCountryNavigation)
                        .ToListAsync();

我在ThenInclude()之后尝试使用.Include(x => x.CityTranslation),但它不会让我使用语言属性。

更改查询

通过下面的查询,我确实得到了一些非常意想不到的结果。通过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; }
}

1 个答案:

答案 0 :(得分:2)

这样就可以了,只需粘贴即可。

// 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();

根据具体情况,您需要在热切和显式加载之间进行选择,以获得最佳性能。