LINQ方法来加载子实体,该方法包括某些Parent的导航属性和仅选定的Grand Child的记录

时间:2019-05-02 21:36:31

标签: c# entity-framework linq

我有三个表Site_Report Report_AssetAsset_Calcert,它们之间具有一对多的关系。

Relationship diagram

我想将子实体作为模型加载,即Report_Asset,其中应包含某些父实体的属性。而且,我想从孙子的集合导航属性中,使用条件加载单个记录。

第一次尝试-这会导致错误。

Report_Asset model;
model = ctx.Report_Asset
           .Include(i => i.Site_Report)
           .Include(i => i.Site_Report.Handled_By)
           .Include(i => i.Site_Report.Published_By)
           .Include(i => i.Asset_Calcerts.Select(b => b.asset_calcert_id == assetCalcertId))
           .FirstOrDefault();
  

错误:“包含”路径表达式必须引用在类型上定义的导航属性。使用虚线路径作为参考导航属性,使用“选择”运算符作为集合导航属性。

第二次尝试-通过启动子孙实体并包括其父母。这种方法确实加载了结果集,但它仅包含子实体,即Report_Asset,没有来自Asset_Calcert集合的记录,而对于Site_Report则为null。

Report_Asset model;
model = ctx.Asset_Calcert.Include(i => i.Report_Asset)
                                 .Include(i => i.Report_Asset.Site_Report.Handled_By)
                                 .Include(i => i.Report_Asset.Site_Report.Published_By)
                                 .Where(i => i.asset_calcert_id == assetCalcertId)
                                 .Select(i => i.Report_Asset).FirstOrDefault();

我已经在DbContext()中设置了Configuration.LazyLoadingEnabled = false

需要指导,谢谢

2 个答案:

答案 0 :(得分:2)

您似乎想查询并返回一个实体,它是父级,然后是子级的子集。您可以使用EF进行查询,但是只能将查询简化为所需的结构。如果返回Report_Asset,它可以很容易地包含对其父项的引用,但是它将始终引用其完整的子集,EF不会在实体级别过滤子集。

最好准确地查看每个实体想要的字段,并选择仅包含这些字段的结构,但是从最基本的角度来看,这应该可以为您提供所需的内容:

var model = ctx.Report_Asset
    .Select( x = > new 
    {
       Report_Asset = x,
       Site_Report = x.Site_Report,
       Handled_By = x.Site_Report.Handled_By,
       Published_By = x.Site_Report.Published_By,
       Asset_Calcerts = x.Assert_Calcerts.Where(c => c.asset_calcert_id == assetCalcertId).ToList()
    }).FirstOrDefault();

calcert检查似乎是在寻找特定的单一证书,因此这样做可能更好:

var model = ctx.Report_Asset
    .Select( x = > new 
    {
       Report_Asset = x,
       Site_Report = x.Site_Report,
       Handled_By = x.Site_Report.Handled_By,
       Published_By = x.Site_Report.Published_By,
       Asset_Calcert = x.Assert_Calcerts.SingleOrDefailt(c => c.asset_calcert_id == assetCalcertId)
    }).FirstOrDefault();

如果您希望仅加载具有匹配Calcert的报表资产,并同时包含父级和适用的calcert:

var model = ctx.Report_Asset
    .Where(x => x.Asset_Calcerts.Any(c => c.asset_calcert_id == assetCalcertId))
    .Select( x = > new 
    {
       Report_Asset = x,
       Site_Report = x.Site_Report,
       Handled_By = x.Site_Report.Handled_By,
       Published_By = x.Site_Report.Published_By,
       Asset_Calcert = x.Asset_Calcerts.SingleOrDefailt(c => c.asset_calcert_id == assetCalcertId)
    }).FirstOrDefault();

在所有情况下,我建议在OrderBy之前添加一个FirstOrDefault子句,以确保使用可预测的顺序。

此方法的警告是,如果您访问返回的模型的实体并开始向下钻取它们的引用,则仍然会触发数据库中的延迟加载。例如,如果我使用:

model.Report_Asset.Asset_Calcerts

例如,

仍然会延迟加载该报告资产的相关实体,并列出该资产的所有证书。过滤后的集合/匹配项会急切加载到模型中。Asset_Calcert(而不是返回的Model_Asset实体)。

通常,尽管最好使用Select从各种实体中检索您关心的属性,而不是尝试选择整个实体或实体图。这就不需要使用Include了,只需将实体图中的确切信息告诉EF,它将构建一条SQL语句来有效地检索该信息。

答案 1 :(得分:0)

当使用大于1的级别时,最好使用字符串选项。 如果您不想使用字符串,则需要选择大于1的级别。

Report_Asset model;
model = ctx.Report_Asset
           .Include("Site_Report")
           .Include("Site_Report.Handled_By")
           .Include("Site_Report.Published_By")
           .Include("Asset_Calcerts")
           .FirstOrDefault(x => x.Asset_Calcerts.Any(y => y.asset_calcert_id == assetCalcertId);