C# 包含路径表达式必须引用导航属性
关于我的问题我搜索了很多,但没有找到任何明确的解决办法。我只知道我不能将Where linq子句与Include一起使用,但如何进行此查询对我来说没有意义C# 包含路径表达式必须引用导航属性,c#,asp.net-mvc,linq,C#,Asp.net Mvc,Linq,关于我的问题我搜索了很多,但没有找到任何明确的解决办法。我只知道我不能将Where linq子句与Include一起使用,但如何进行此查询对我来说没有意义 var brands = await _context.Brands .Include(x => x.FoodCategories .Select(y => y.Products .Where(z => z.Sugar)
var brands = await _context.Brands
.Include(x => x.FoodCategories
.Select(y => y.Products
.Where(z => z.Sugar)
.Select(w => w.FileDetail)))
.ToListAsync();
实际上,我想在产品上应用Where语句,但我希望像这里一样在层次结构中使用实体。我怎么做我已经试过用不同的问题来回答自己,但我没有抓住要点。这是我的审判:
var brands = _context.Brands
.Select(b => new
{
b,
FoodCategories = b.FoodCategories
.Where(x => x.BrandId == b.BrandId)
.Select(c => new
{
c,
Products = c.Products
.Where(y => y.FoodCategoryId == c.FoodCategoryId &&
y.Sugar)
.Select(p => new
{
p,
File = p.FileDetail
})
})
})
.AsEnumerable()
.Select(z => z.b)
.ToList();
但是它并不是退回所有的产品,而是退回纯糖产品。为什么只退回糖产品。
但它并没有退回所有的产品,而是退回仅含糖的产品
当然是。因为你要求它只给你糖产品:
var brands = _context.Brands
.Select(b => new
{
b,
FoodCategories = b.FoodCategories
.Where(x => x.BrandId == b.BrandId)
.Select(c => new
{
c,
Products = c.Products
.Where(y => y.FoodCategoryId == c.FoodCategoryId
&& y.Sugar) //HERE!
.Select(p => new
{
p,
File = p.FileDetail
})
})
})
.AsEnumerable()
.Select(z => z.b)
.ToList();
如果你想要所有的产品;然后不要只过滤那些Sugar
设置为true的
这里有很多冗余代码
b.FoodCategories.Where(x => x.BrandId == b.BrandId)
b.FoodCategories
已经表达了该特定品牌的食品类别b
。您不需要Where
这同样适用于
c.Products.Where(y => y.FoodCategoryId == c.FoodCategoryId ... )
以下是您(第二个)代码片段的改进版本: 这应该更清楚地表明,不需要自定义
Select
逻辑。您所做的只是将相关实体加载到同名的属性中。您可以简单地依赖现有实体及其关系,没有理由再次定义相同的关系
这里需要自定义选择的唯一原因是:
- 您希望限制检索的列以降低数据大小(对于大型查询很有用)
- 您希望有选择地加载子对象,而不仅仅是所有相关的子对象。你的代码表明你想要这个,但是你说“但是它没有返回所有的产品项目”,所以我得出结论,你不想根据产品的糖含量来过滤产品
为什么您的Include
不起作用。
简而言之:不能在includes中使用Where
语句
Include
语句基于实体的结构,而Where
只过滤集合中的数据。一个与另一个无关
即使你认为这样做会很好,比如“仅当家长处于活动状态时才包括他们”,但这并不是include
的设计初衷。
Include
归结为“对于每个[type1],也加载它们相关的[type2]”。这将对查询将实例化的每个[type1]对象执行,并将加载每个相关的[type2]
重构上述代码段的下一步:
var brands = _context.Brands
.Include(b => b.FoodCategories)
.Include(b => b.FoodCategories.Select(fc => fc.Products))
.Include(b => b.FoodCategories.Select(fc => fc.Products.Select(p => p.FileDetail)))
.ToList();
包括为实体框架提供特定说明:
- 对于每个加载的品牌,加载其相关的食品类别
- 对于每个装载的食品类别,装载其相关产品
- 对于每个加载的产品,加载其相关的文件详细信息
请注意,它没有指示应该加载哪些品牌!这是一个重要的区别。Include
语句不会以任何方式过滤数据,它们只解释将要加载的每个条目需要检索哪些附加数据。
将加载哪些条目尚未定义。默认情况下,您将获得整个数据集,但在加载数据之前,您可以使用Where
语句应用进一步的筛选
这样想:
一家餐馆希望每位新顾客的母亲都允许为顾客提供甜点。因此,餐厅起草了一条规则:“每位顾客必须带上他们的母亲”。
这相当于db.Customers.Include(c=>c.Mother)
这并没有说明允许哪些顾客访问餐厅。它只规定任何来餐厅的顾客必须带上他们的母亲(如果他们没有母亲,他们将带上null
)
请注意,无论哪位顾客来餐厅,此规则都适用:
- 女士之夜:
db.Customers.Include(c=>c.Mother.Where(c=>c.IsFemale)
- 家长之夜:
db.Customers.Include(c=>c.Mother.Where(c=>c.Children.Any())
- 父亲名叫Bob night的人:
db.Customers.Include(c=>c.Mother)。其中(c=>c.father.Name==“Bob”)
注意第三个例子。即使过滤父实体,也只会加载父实体。完全可以过滤相关实体值上的项,而不实际加载实体本身(父项)
您可能会问自己“为什么选择
?”。这是一个很好的问题,因为这不是直观的
理想情况下,你会想做类似的事情
context.Brand.Include(b => b.FoodCategories.Products.FileDetails)
但由于语言的限制,这是不可能的FoodCategories
是一个列表
,它没有产品
属性
但是,FoodCategory
本身确实有一个Products
属性。这就是使用Select
的原因:它允许您访问列表元素类型的属性,而不是列表本身。
在内部,EF将解构您的Select
语句(这是一个表达式
),它将确定要加载的属性。不要太担心EF在幕后是如何工作的。它并不总是美丽的
Include/Select语法不是最漂亮的。尤其是当您深入到多个级别时,写入(和读取)变得非常麻烦
因此,我建议你颠倒你的方法(从最低的孩子开始,向上钻到父母)。
context.Brand.Include(b => b.FoodCategories.Products.FileDetails)
var brands = context.FileDetails
.Include(fd => fd.Product)
.Include(fd => fd.Product.FoodCategory)
.Include(fd => fd.Product.FoodCategory.Brand)
.Select(fd => fd.Product.FoodCategory.Brand)
context.Brands
.Include("FoodCategories")
.Include("FoodCategories.Products")
.Include("FoodCategories.Products.FileDetails")