我有一个实体Person,它有一个实体Car。cars实体有多个属性,其中一个是颜色。我想要有一辆车的所有人colorlist所有被选中的颜色。如果一个人有很多车,我应该可以这样写
PersonQuery.Include(c => c.Cars).Where(q=>q.Cars.Any(colors=>ListOfColors.Contains(colors.Color)));
和我想用Where替换Any,但这是不正确的(!?)。我错过了什么?
如果您希望将每个适用的Person返回的Cars筛选为仅匹配过滤颜色的汽车,那么EF6不支持此功能。您的查询将返回人和他们的车,人有一个匹配的颜色,但它将返回所有的汽车人。
边注:当使用Linq表达式时,标识符的选择应该只是一个占位符值(即"x")或反映正在查询的项,而不是您正在查询的内容。例如,当编写PersonQuery.Where(c => ...
时,您正在查询"Persons"不是"汽车",而是"汽车";将是比"&;c&;"更容易混淆的标识符。展开得到.Where(person => person.Cars.Any(car => ...))
在EF Core 5中有对过滤器的支持,包括:
var persons = context.Persons
.Include(p => p.Cars
.Where(c => ListOfColors.Contains(c.Color))
.Where(p => p.Cars.Any(c=>ListOfColors.Contains(c.Color)))
.ToList();
在EF 6中,您最好选择Cars,然后可以构建适用的Person列表:
var cars = context.Cars
.Include(c => c.Person)
.Where(c => ListOfColors.Contains(c.Color))
.ToList();
var persons = cars.Select(c => c.Person).ToList();
然而,对这种方法的一个警告是,它应该只在一个未加载任何其他数据的DbContext中完成。如果由于任何原因DbContext已经在为一个适用的person记录跟踪另一种颜色的汽车,那么该车将自动与返回的person相关联,因此您可以轻松地找到自己与不匹配的奇怪汽车。
如果您正在运行一个DbContext作用域到请求,并且希望100%安全,您应该考虑投影结果,而不是直接处理实体:
var results = context.Persons
.Where(p => p.Cars.Any(c => ListOfColors.Contains(c.Color)))
.Select(p => new PersonViewModel
{
PersonId = p.Id,
Name = p.Name,
MatchingCars = p.Cars
.Where(c => ListOfColors.Contains(c.Color))
.Select(c => new CarViewModel
{
CarId = c.Id,
Brand = c.Brand,
Model = c.Model,
// ...
}).ToList()
}.ToList();
这将确保返回的数据始终是匹配颜色的汽车。