地理位置之间的 Haversine LINQ 距离

Haversine LINQ distance between geo locations

我有这个工作代码:

IEnumerable<Message> dbCities = db.Cities;

dbCities = dbCities.Where(x => GetDistanceBetweenLocations(longitude, latitude, x.Longitude, x.Latitude) <= radius);

public int GetDistanceBetweenLocations(double lon1, double lat1, double lon2, double lat2)
{
        var sCoord = new GeoCoordinate(lon1, lat1);
        var eCoord = new GeoCoordinate(lon2, lat2);

        return Convert.ToInt32(Math.Round(sCoord.GetDistanceTo(eCoord)));
}

但是有点慢。我正在尝试移动数据库中的逻辑以获得更好的性能。

我从以下几件事开始:

dbCities = db.Cities.Where(x => new GeoCoordinate(longitude, latitude).GetDistanceTo(new GeoCoordinate(x.Longitude, x.Latitude)) <= radius);

并得到:LINQ to Entities 无法识别方法 'Double GetDistanceTo(System.Device.Location.GeoCoordinate)' 方法,并且此方法无法转换为存储表达式。

我知道 Haversine 公式,但无法使其与 linq 一起使用。

该项目使用 SQL 服务器数据库和代码优先方法。

错误消息不言而喻:EF 的 LINQ 提供程序不知道如何将名为 GetDistanceTo 的函数映射到它的 SQL 对应函数。

如果您希望由底层 DBMS 完成任务,您需要重写查询以仅使用 canonical functions. Alternatively, you can define your logic as an UDF in your DB (if your DB engine supports UDFs) and

但是,如果您想在应用程序中处理此问题,您可以"force"将您的 LINQ to Entites 查询转换为 LINQ to Objects 查询:

dbCities = db.Cities.AsEnumerable().Where(x => new GeoCoordinate(longitude, latitude).GetDistanceTo(new GeoCoordinate(x.Longitude, x.Latitude)) <= radius);

但是,注意:使用这种方法,每个城市记录都将从数据库中获取,供您的 GetDistanceTo 检查逻辑。

更新

从 2012 版开始,SQL 服务器提供了内置地理信息 API。它也可以calculate distances

您可以通过 DbGeography class. Have a look at this discussion 在 EF 中访问此 API。

如果您使用 EF Core,您需要一个解决方法,因为 DbGeography 尚未移植,预计会在 version 2.2.0 中移植。 (但是,前面提到的 UDF 映射方法应该有效。)