3

我有以下代码

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

4 回答 4

2

从技术上讲,有一些方法可以做到这一点,但是由于 Linq 不能直接将您的自定义代码转换为 SQL,因此它们需要将所有结果返回到 Linq,然后评估自定义方法。

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

于 2012-10-10T15:08:15.397 回答
1

我不太明白你在说什么;如果您只在一个地方使用此代码,并且它是查询中的唯一子句,那么将计算移至单独的方法并不会真正改变太多。

相反,如果您希望在多个地方重复使用距离计算代码并且不想重复它,或者如果您想要在一个查询中调用多个检查并且不希望该查询得到太笨重了,你可以试试例如

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(...);

它不如将距离计算作为自己的方法好——例如,您需要一种用于“接近”的方法和一种用于“更远”的单独方法——但它应该在 L2E 中工作,因为它本质上与链接您的原始Where条款,您说这有效。

于 2012-10-10T15:43:43.673 回答
0

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

您必须device先从数据库中全选才能成功执行此查询。

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

于 2012-10-10T15:04:01.123 回答
0

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

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

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

于 2012-10-10T15:09:18.220 回答