查询父子的记录where条件在孙子身上
Query Parent's record where condition on a Grandchild
我想 select 所有 Site_Report
条目,其中 Asset_Calcert
至少有一个条件为 Asset_Calcert.Cert_type == 3
的条目。关系是 1-0..* 如下所示。
如果想对多个实体设置条件,我该怎么做,在我的例子中,我想对父子和孙子都设置条件。即 Site_report.report_status == 2
和 Asset_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);
}
错误
Cannot implicitly convert type 'System.collection.Generic .IEnumerable' to 'bool'.
Error CS1662 Cannot convert lambda expression to intended delegate type because some of the return types in the block are not implicitly convertible to the delegate return type
模态 为简洁起见只添加必要的字段
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; }
}
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 肯定会告诉您吗?
您写道:我想 select 所有 Site_Report 条目,其中 Asset_Calcert 至少有一个条件为 Asset_Calcert.Cert_type == 3
或稍微改写一下:
Requirement: I want all (or some) properties of all SiteReports that have at least one ReportAsset that has at least one AssetCalCert where AssetCalCert.CertType equals 3
如果你想要 select "a sequence within a sequence" 的属性,你可以使用 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(),
};
When querying data, always use Select and select only the properties that you plan to use. Only use Include if you plan to update the fetched item.
如果 SiteReport [10] 有 1000 个 ReportAssets,那么每个 ReportAsset 都会有一个值为 10 的 SiteReport 的外键。传输相同的值 10 超过 1000 次是一种浪费。
匿名类型
请注意,我使用了匿名类型:
siteReport => new
{
...
}
当然我可以用 new SiteReport()
代替。但如果我使用它,我会传输几个我不打算使用的变量。当然我可以跳过填充我不使用的属性,但是如果我的读者得到一个 SiteReport
对象,他们可能期望所有值都被填充。
因此虽然使用匿名类型效率更高,但缺点是您不能将匿名类型用作 return 值。如果您需要这样做,最好的方法是将代表数据库 table 的 class 与代表获取数据的 class 分开:(适配器设计模式?或外观?)
class AdaptedSiteReport()
{
... only properties that queriers plan to use
}
并且在 LINQ 语句中
siteReport => new AdaptedSiteReport() {...}
将实际数据库与获取的表示分离的优点是您可以更改内部数据库表示而无需用户注意。
我想 select 所有 Site_Report
条目,其中 Asset_Calcert
至少有一个条件为 Asset_Calcert.Cert_type == 3
的条目。关系是 1-0..* 如下所示。
如果想对多个实体设置条件,我该怎么做,在我的例子中,我想对父子和孙子都设置条件。即 Site_report.report_status == 2
和 Asset_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);
}
错误
Cannot implicitly convert type 'System.collection.Generic .IEnumerable' to 'bool'.
Error CS1662 Cannot convert lambda expression to intended delegate type because some of the return types in the block are not implicitly convertible to the delegate return type
模态 为简洁起见只添加必要的字段
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; }
}
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 肯定会告诉您吗?
您写道:我想 select 所有 Site_Report 条目,其中 Asset_Calcert 至少有一个条件为 Asset_Calcert.Cert_type == 3
或稍微改写一下:
Requirement: I want all (or some) properties of all SiteReports that have at least one ReportAsset that has at least one AssetCalCert where AssetCalCert.CertType equals 3
如果你想要 select "a sequence within a sequence" 的属性,你可以使用 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(),
};
When querying data, always use Select and select only the properties that you plan to use. Only use Include if you plan to update the fetched item.
如果 SiteReport [10] 有 1000 个 ReportAssets,那么每个 ReportAsset 都会有一个值为 10 的 SiteReport 的外键。传输相同的值 10 超过 1000 次是一种浪费。
匿名类型
请注意,我使用了匿名类型:
siteReport => new
{
...
}
当然我可以用 new SiteReport()
代替。但如果我使用它,我会传输几个我不打算使用的变量。当然我可以跳过填充我不使用的属性,但是如果我的读者得到一个 SiteReport
对象,他们可能期望所有值都被填充。
因此虽然使用匿名类型效率更高,但缺点是您不能将匿名类型用作 return 值。如果您需要这样做,最好的方法是将代表数据库 table 的 class 与代表获取数据的 class 分开:(适配器设计模式?或外观?)
class AdaptedSiteReport()
{
... only properties that queriers plan to use
}
并且在 LINQ 语句中
siteReport => new AdaptedSiteReport() {...}
将实际数据库与获取的表示分离的优点是您可以更改内部数据库表示而无需用户注意。