将LINQ实体分成多个部分

时间:2012-10-10 15:00:13

标签: c# linq entity-framework lambda linq-to-entities

我有以下代码

query = query.Where(device => GetDeviceDistanceFromGeocode(geocode, device) <= distanceMetres);

private static double GetDeviceDistanceFromGeocode(Geocode geocode, Device device)
{
    return Math.Pow(
        Math.Pow(device.Address.Geocode.Easting - geocode.Easting, 2) +
        Math.Pow(device.Address.Geocode.Northing - geocode.Northing, 2)
        , 0.5);
}

然而它抛出异常,因为linq无法识别我的函数,迫使我一次性完成整个查询表达式。

Exception[LINQ to Entities does not recognize the method 
'Double DistanceOfDeviceFromGeocode(Geocode, Device)' method, 
and this method cannot be translated into a store expression.] 

是否可以将查询表达式分解为多个部分,就像我在这里尝试一样?当查询太大时,它的可读性不高。


编辑:

以下是评论中要求的完整查询。

return query.Where(device => 
            Math.Pow(
                Math.Pow(device.Address.Geocode.Easting - geocode.Easting, 2) + 
                Math.Pow(device.Address.Geocode.Northing - geocode.Northing, 2)
                , 0.5) 
                <= distanceMetres);

基本上我不认为这是非常易读的,所以想把它分解成多个部分,但是从提供的链接看起来似乎不太可能。

在c ++中,我可以将其中的一部分打破到宏中,但不幸的是,这不是c#的一个特性。


以下建议我已经将代码更新到了非常好的代码并且提高了代码的可读性!

    return query.Where( DevicesWithinDistanceFromGeocode(distanceMetres, geocode) );
}

public Expression<Func<Device, bool>> DevicesWithinDistanceFromGeocode(double distance, Geocode geocode)
{
    return device => (  SqlFunctions.SquareRoot(
                            SqlFunctions.Square((double)(device.Address.Geocode.Easting - geocode.Easting)) +
                            SqlFunctions.Square((double)(device.Address.Geocode.Northing - geocode.Northing))
                        ) <= distance );
}

4 个答案:

答案 0 :(得分:2)

技术上there are ways to do it,但是由于Linq无法将您的自定义代码直接转换为SQL,因此需要将所有结果返回给Linq,然后评估自定义方法。

由于您在Where子句中使用它,我假设您希望检索相对较少的记录。在这种情况下,我建议在SQL中编写条件,在存储过程或标量值函数中,然后在Linq-to-Entities查询中使用该sproc / function。

答案 1 :(得分:1)

我无法清楚地看到你所得到的东西;如果您只在一个地方使用此代码,并且它是查询中唯一的条款,那么将计算转移到单独的方法中并没有太大的改变。

相反,如果您希望在多个地方重复使用距离计算代码并且不想重复它,或者如果您想要在一个查询中调用多个支票并且不要&# 39;我希望这个查询过于笨拙,你可以尝试例如

public static IQueryable<Device> WhereCloseToGeocode(
    this IQueryable<Device> source,
    Geocode geocode,
    double distanceMetres)
{
    return source.Where(device => Math.Pow(
        Math.Pow(device.Address.Geocode.Easting - geocode.Easting, 2) +
        Math.Pow(device.Address.Geocode.Northing - geocode.Northing, 2),
        0.5) <= distanceMetres);  
}

public static IQueryable<Device> OtherCondition(
    this IQueryable<Device> source, ...)
{
   ...
}


...
devices = devices
    .WhereCloseToGeocode(geocode1, 3)
    .WhereCloseToGeocode(geocode2, 7)
    .OtherCondition(...);

它不如将距离计算作为自己的方法那么好 - 例如,你需要一种方法来使&#34;更接近&#34;和#34;进一步的&#34; - 但它应该在L2E中工作,因为它与链接你原来的Where条款基本相同,你说这个条款有效。

答案 2 :(得分:0)

您的问题是,您在方法中使用device。 Linq to Entities必须将您的Linqquery转换为SQLQuery。

您必须先从数据库中选择所有device才能成功使用此查询。

查找静态类SqlMethods以查找此问题的其他示例:)

答案 3 :(得分:0)

这可能非常昂贵(取决于你的桌子的大小),但是会起作用:

query = query.ToList().Where(device => GetDeviceDistanceFromGeocode(geocode, device) <= distanceMetres);

在这种方法中,您首先评估查询,然后针对客户端的每条记录运行您的方法。