我有一个方法,在该方法中,我过滤一个表,并返回与文件管理器匹配的行。方法如下:
public List<RealEstate> GetMatchingRealestatesQuery(RealestateFilter f, RealestateSiteDbContext context)
{
IQueryable<RealEstate> realestates = context.RealEstates.Where(x => x.Status == RealEstateStatus.Open);
realestates = realestates.Where(x => f.Cities.Any( y => x.ZipCode.City.Id == y.Id));
if (f.MaxRent > 0)
realestates = realestates.Where(x => x.Rent <= f.MaxRent);
if (f.MinArea > 0)
realestates = realestates.Where(x => x.Area >= f.MinArea);
if (f.MinRooms > 0)
realestates = realestates.Where(x => x.Rooms <= f.MinRooms);
realestates = realestates.Where(x => f.RealestateTypes.Has(x.Type));
realestates = realestates.Where(x => f.RentalPeriod.Has(x.RentalPeriod));
return realestates.ToList();
}
然而,每当我调用该方法时,我都会得到以下异常:
无法创建类型为"RealestateSiteModel.City"的常数值。此中仅支持基元类型或枚举类型上下文
我只是简单地构建一个IQueryable,然后通过调用.ToList来执行查询。这个异常的原因是什么?
这里的问题是LINQ不知道如何将复杂的对象/类转换为SQL代码。
通常,如果您要尝试过滤掉调用并将其与内存中的对象进行比较,则需要确保LINQ知道如何处理这些调用(例如,仅使用基元类型的集合):
public List<RealEstate> GetMatchingRealestatesQuery(RealestateFilter f, RealestateSiteDbContext context)
{
// This works just fine as status is going to be a boolean
var realestates = context.RealEstates.Where(x => x.Status == RealEstateStatus.Open);
// Here's where things get tricky as LINQ doesn't know what City is
// Is there some ID that you could use that might make this easier,
// such as x.ZipCode.City.CityId or something?
realestates = realestates.Where(x => f.Cities.Any( y => x.ZipCode.City == y));
// Other code omitted for brevity
return realestates.ToList();
}
如果这不可能,那么通常这类查询很少利用延迟执行,并且通常需要将整个集合存储在内存中,然后通过ToList()
调用在内存中进行过滤:
public List<RealEstate> GetMatchingRealestatesQuery(RealestateFilter f, RealestateSiteDbContext context)
{
// This will wipe out any deferred execution and perform the
// rest of your operations in-memory
var realestates = context.RealEstates.Where(x => x.Status == RealEstateStatus.Open).ToList();
// Other code omitted for brevity
return realestates;
}
同样,这种方法并不理想,因为你提取的数据远远超过了你的需要,但为了避免这种情况,你只需要准确地重组你正在查询的内容,并确保LINQ知道如何翻译它。
更新(实际修复)
该问题的实际解决方案涉及删除在以下行的lamdba调用中使用的实体的实际集合:
realestates = realestates.Where(x => f.Cities.Any( y => x.ZipCode.City.Id == y.Id));
由于LINQ不知道如何转换Cities
集合的属性并对其进行求值,所以这会失败。但是,您可以将要查询的对象集合存储在内存中,作为基元类型,然后您应该能够使用:
var cities = f.Cities.Select(c => c.ZipCode.City.Id).ToArray();
realestates = realestates.Where(x => cities.Any(c => c == x.Id);