执行Linq查询会抛出异常

时间:2016-04-06 20:18:44

标签: .net database entity-framework linq entity-framework-6

我有一个方法可以过滤表并返回与文件管理器匹配的行。 这是方法:

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来执行查询。这个例外的原因是什么?

1 个答案:

答案 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);