我有一个方法可以过滤表并返回与文件管理器匹配的行。 这是方法:
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来执行查询。这个例外的原因是什么?
答案 0 :(得分:1)
这里的问题是LINQ不知道如何将复杂的对象/类转换为SQL代码。
通常,如果您要尝试过滤掉调用并将它们与内存中的对象进行比较,那么您需要确保LINQ知道如何处理这些调用(例如,仅使用基本类型的集合): / p>
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);