【问题标题】:Executing Linq query throws exception执行 Linq 查询抛出异常
【发布时间】:2016-04-06 20:18:43
【问题描述】:

我有一种方法可以过滤表并返回与文件管理器匹配的那些行。 方法如下:

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

【问题讨论】:

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


    【解决方案1】:

    这里的问题是 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);
    

    【讨论】:

    • 感谢您的回答。我已经尝试按城市 ID 进行比较(如我的答案中更新的代码所示),但我仍然得到相同的异常。
    • 它有时可以帮助实际获取您正在比较的集合并将其放入内存:var cities = f.Cities.Select(c =&gt; c.ZipCode.City.Id).ToArray();,然后在您的后续查询中使用它:realestates = realestates.Where(x =&gt; cities.Any(c =&gt; c == x.Id);
    • 成功了!将过滤器城市 ID 存储为列表而不是仅将城市本身存储为列表使查询成功完成。我确实觉得有必要这样做很奇怪。无论如何,非常感谢您的帮助!
    猜你喜欢
    • 1970-01-01
    • 2011-10-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-12-15
    • 1970-01-01
    • 2015-09-02
    • 2020-03-12
    相关资源
    最近更新 更多