如何在LINQ查询中对ICollection关联字段正确应用Where筛选并控制返回结果
如何在LINQ查询中对ICollection关联字段正确应用Where筛选并控制返回结果
看起来你遇到了两个核心问题:一是怎么正确筛选出包含指定尺寸的产品,二是怎么让返回的ProductSizes集合只保留符合条件的尺寸,而不是把该产品的所有尺寸都带回来对吧?我来一步步帮你梳理清楚。
首先先解释你之前遇到的错误:
你一开始写的
Where(p => p.ProductSizes.Where(p => p.Size.Name == "42"))报错,是因为外层Where需要的是一个布尔值(判断这个产品是否符合条件),但内层的Where返回的是一个IEnumerable<ProductSize>集合,类型不匹配,所以编译器报错。你换成Any是对的,Any会返回布尔值——判断这个产品的ProductSizes里有没有符合条件的尺寸,但它的作用只是筛选出符合条件的产品,不会过滤产品自带的ProductSizes集合,所以你才会看到返回的还是所有尺寸。
接下来分两种场景给你解决方案:
场景1:只想筛选出包含尺寸"42"的产品,返回该产品所有尺寸
如果你的需求只是拿到所有有"42"尺寸的产品,不管它还有没有其他尺寸,那你之前的Any写法是正确的,只需要保留这个筛选条件即可:
var result = _tebpooshContext.Products .Where(p => p.ProductCategoryId == 1 && p.ProductTypeId == 2 && p.ProductSizes.Any(ps => ps.Size.Name == "42")) // 用Any判断产品是否包含目标尺寸 .Include(p => p.Picture) .Include(p => p.ProductSizes) .ThenInclude(ps => ps.Size) .Select(p => new TileProductDto { Image = p.Picture?.Url.Replace("SYSTEM_TYPE_SYNCSERVER", "URL"), // 注意加空判断,避免空引用 Name = p.Name.Trim(), Price = p.Price.ToString(), ProductSizes = p.ProductSizes.ToList(), ProductId = p.Id.ToString(), // 这里你之前拼写错了,应该是ProductId }) .Take(9) .ToList();
场景2:筛选出包含尺寸"42"的产品,且返回的ProductSizes只保留"42"
如果你的需求是不仅要筛选出有"42"尺寸的产品,还要让返回的ProductSizes集合里只有"42"这个尺寸,那有两种实现方式:
方式一:在Select投影时过滤关联集合
这种方式兼容性好,适用于所有EF版本:
var result = _tebpooshContext.Products .Where(p => p.ProductCategoryId == 1 && p.ProductTypeId == 2 && p.ProductSizes.Any(ps => ps.Size.Name == "42")) // 先筛选出符合条件的产品 .Include(p => p.Picture) .Include(p => p.ProductSizes) .ThenInclude(ps => ps.Size) .Select(p => new TileProductDto { Image = p.Picture?.Url.Replace("SYSTEM_TYPE_SYNCSERVER", "URL"), Name = p.Name.Trim(), Price = p.Price.ToString(), // 在Select里对ProductSizes进行过滤,只保留尺寸为"42"的项 ProductSizes = p.ProductSizes.Where(ps => ps.Size.Name == "42").ToList(), ProductId = p.Id.ToString(), }) .Take(9) .ToList();
方式二:使用Filtered Include(EF Core 5.0+支持)
如果你的项目用的是EF Core 5.0及以上版本,可以用Filtered Include,直接在数据库层面就过滤掉不需要的关联数据,性能更好:
var result = _tebpooshContext.Products .Where(p => p.ProductCategoryId == 1 && p.ProductTypeId == 2 && p.ProductSizes.Any(ps => ps.Size.Name == "42")) .Include(p => p.Picture) // 直接在Include的时候就过滤ProductSizes,只加载尺寸为"42"的项 .Include(p => p.ProductSizes.Where(ps => ps.Size.Name == "42")) .ThenInclude(ps => ps.Size) .Select(p => new TileProductDto { Image = p.Picture?.Url.Replace("SYSTEM_TYPE_SYNCSERVER", "URL"), Name = p.Name.Trim(), Price = p.Price.ToString(), // 这里的ProductSizes已经是过滤后的集合了 ProductSizes = p.ProductSizes.ToList(), ProductId = p.Id.ToString(), }) .Take(9) .ToList();
最后再提醒几个小细节:
- 你的
Picture是可空类型(public virtual Picture? Picture { get; set; }),所以访问Url的时候一定要加?.空传播运算符,避免空引用异常。 - 你之前代码里的
ProdutId拼写错误,应该改成ProductId,避免后续使用时出问题。
备注:内容来源于stack exchange,提问作者user3671271
相关产品推荐
相关产品推荐

