查询父母对孙子的记录

时间:2019-04-11 10:06:03

标签: c# linq entity-framework-6

我想选择所有Site_Report项,其中Asset_Calcert至少有一个条件为Asset_Calcert.Cert_type == 3的项。关系为1-0 .. *,如下所示。

enter image description here

如果要对多个实体设置条件,我该如何处理,就我而言,我想对父级和子级都设置条件。即Site_report.report_status == 2Asset_Calcert.Cert_type == 3

我已经尝试过以下方法,但是这样做不正确,因为这种方法会出错。

IEnumerable<Site_Report> model;
using (var ctx = new ApplicationDbContext())
{
    model = ctx.Site_Report.Include(i => i.Published_By)
                            .Include(i => i.Handled_By)
                            .Include(i => i.Report_Assets.Select(c => c.Asset_Calcerts))
                            .Where(report => report.report_status == DBConstant.REPORT_STATUS_CONCLUDED)
    // Error ------>     // .Where(rp => rp.Report_Assets.Where(c => c.Asset_Calcerts.Any(d => d.cert_type == 3)))
                            .OrderByDescending(o => o.publish_date);
}

错误

  

无法将类型'System.collection.Generic .IEnumerable'隐式转换为'bool'。
  错误CS1662无法将lambda表达式转换为预期的委托类型,因为该块中的某些返回类型不能隐式转换为委托返回类型

模式仅为简洁起见添加必要的字段

    public class Site_Report
    {
        public int site_report_id { get; set; }
        public int report_status { get; set; }

        // Navigation Properties
        public virtual ICollection<Report_Asset> Report_Assets { get; set; }

        public Site_Report()
        {
            this.Report_Assets = new HashSet<Report_Asset>();
        }
    }

    public class Report_Asset
    {
        public int report_asset_id { get; set; }

        // Navigation Properties
        public int site_report_id { get; set; }
        public virtual Site_Report Site_Report { get; set; 

        public Report_Asset()
        {
            this.Asset_Calcerts = new HashSet<Asset_Calcert>();
        }
    }
    public class Asset_Calcert
    {
        public int asset_calcert_id { get; set; }
        public int cert_type { get; set; }

        // Navigation Properties
        public int report_asset_id { get; set; }
        public virtual Report_Asset Report_Asset { get; set; }
    }

1 个答案:

答案 0 :(得分:0)

Queryable.Where使用谓词参数。谓词通常以以下格式编写:

report => Some expression that takes report as input, and a Boolean as output

您的谓词是:

rp => rp.Report_Assets.Where(c => c.Asset_Calcerts.Any(d => d.cert_type == 3))

所以您的表情是:

rp.Report_Assets.Where(c => c.Asset_Calcerts.Any(d => d.cert_type == 3))

此表达式的值不是布尔值,而是IQueryable<ReportAssert>!当您将鼠标悬停在Visual Studio上时,Visual Studio肯定会告诉您吗?

您写道:我想选择所有Site_Report条目,其中Asset_Calcert至少有一个条件为Asset_Calcert.Cert_type == 3

或者稍微重新措辞:

  

要求:我希望所有具有至少一个ReportAsset且至少具有一个AssetCalCert且其中AssetCalCert.CertType等于3的ReportAsset的所有SiteReports的所有(或某些)属性

如果要选择“序列中的序列”的属性,请使用SelectMany而不是Select:

var result = myDbContext.SiteReports

   .Where(siteReport => siteReport.ReportAssets.SelectMany(reportAsset => reportAsset.AssetCalCerts)

       // I only want this siteReport if at least one of the AssertCalCerts
       // has a certType value equal to 3
       .Any(assertCalCert => assertCalCert.CertType == 3))

   // from the resulting SiteReports selecte the properties that you plan to use:
   .Select(siteReport => new
   {
       Id = siteReport.Id,
       Name = siteReport.Name,
       ...

       ReportAsserts = siteReport.ReportAssets.Select(reportAsset => new
       {
           Id = reportAssert.Id,
           ...

           AssetCalCerts = reportAsset.AssetCalCerts.Select(assetCalCert => new
           {
             ...
           })
           .ToList(),
       })
       .ToList(),
  };
  

查询数据时,请始终使用“选择”,并仅选择计划使用的属性。仅在计划更新获取的项目时才使用“包括”。

如果SiteReport [10]具有1000个ReportAsset,则每个ReportAsset都将具有一个值为10的SiteReport外键。将相同的值传输10超过1000次将是浪费的。

匿名类型

请注意,我使用了匿名类型:

siteReport => new
{
    ...
}

我当然可以使用new SiteReport()来代替。但是,如果我使用了该变量,那我将传输几个我不打算使用的变量。当然,我可以跳过填充不使用的属性,但是如果我的读者得到一个SiteReport对象,他们可能会期望所有值都被填充。

因此,尽管使用匿名类型更为有效,但缺点是您不能将匿名类型用作返回值。如果需要这样做,最好的方法是将代表数据库表的类与代表获取的数据的类分开:(适配器设计模式?还是门面?)

class AdaptedSiteReport()
{
    ... only properties that queriers plan to use
}

在LINQ语句中

siteReport => new AdaptedSiteReport() {...}

将实际数据库与获取的表示形式分离的优点是,您可以更改内部数据库表示形式,而无需用户注意。