首页 > 解决方案 > 当我试图到达最近的地方时,我在运行时遇到了这个错误

问题描述

当我尝试使用实体框架工作到达最近的地方时,我在运行时遇到了这个错误

注意:我的 sql 数据库中的纬度和经度数据类型是浮点数,我在实体模型中将其映射为双精度

public JsonResult GetNearstCity(double latitude,double longitude)
    {
        db.Configuration.ProxyCreationEnabled = false;
        var coord = new GeoCoordinate(latitude, longitude);
        var nearest = db.Cities.Select(x => new GeoCoordinate(x.Latitude,x.Longtiude))
                               .OrderBy(x => x.GetDistanceTo(coord))
                               .First();
        return Json(nearest, JsonRequestBehavior.AllowGet);
    }

'Only parameterless constructors and initializers are supported in LINQ to Entities.'

标签: asp.net-mvcentity-frameworkgeolocation

解决方案


EF 希望将 Linq 表达式中的所有内容转换或跟踪为 SQL。SQL 不知道什么是 GeoCoordinate 对象,因此 EF 无法使用参数构造一个。您可以告诉 EF 使用默认构造函数构造一个对象,因为 EF 将从适用的表中请求字段,然后在执行查询后设置属性。

例如,这将起作用:

var nearest = db.Cities.Select(x => new GeoCoordinate 
    {
        Latitude = x.Latitude,
        Longitude = x.Longitude
    });

这要求纬度和经度是地理坐标上的公共属性。

您将面临的下一个问题是您也无法GetDistanceTo(coord)在 linq 表达式中调用。同样,EF 将无法将此方法发送到 SQL,因为 SQL 将不知道此方法是什么。这里有两种选择:

选项 1:如果数据量合理,您可以让 EF 对所有城市执行整体查询,然后从那时起您处理的是 Linq2Object,而不是 Linq2EF,因此您可以在 GeoCoordinate 上调用类函数:

var nearest = db.Cities.Select(x => new GeoCoordinate 
    {
        Latitude = x.Latitude,
        Longitude = x.Longitude
    }).ToList()
    .OrderBy(x => x.GetDistanceTo(coord))
    .First();

通过调用.ToList()EF 将执行我们的查询以返回所有城市。从那里,您可以使用类上的函数进行进一步计算。这种方法的缺点是您会将所有城市加载到内存中。如果您有大量数据,这将对性能和资源造成限制。

选项 2:将方法翻译成 SQL 可以理解的东西。此选项允许 EF 将您的计算转换为 SQL 可以执行的东西,它在 DB 端完成工作,然后只返回您想要的单个记录。您无需在 GeoCoordinate 实体上的方法中使用逻辑,而是在 Linq 表达式内进行计算。就像是:

var nearest = db.Cities.Select(x => new GeoCoordinate 
    {
        Latitude = x.Latitude,
        Longitude = x.Longitude
    })
    .OrderBy(x => Math.Abs(x.Latitude - coord.Latitude) + Math.Abs(x.Longitude - coord.Longitude))
    .First();

这可能不是您要用于查找最接近的 2 点的公式,它只是类似公式的一个示例。Math.Abs()是一种 .Net 方法,但 EF 确实知道如何将其转换为 SQL。将实体内部的逻辑用于该GetDistanceTo(coord)方法并将其向上移动到 Linq 表达式中,您应该就差不多了。


推荐阅读