LINQ to SQL查询WHERE语句计算经纬度坐标距离的报错问题咨询
错误根因
你遇到的报错是因为LINQ to Entities需要把表达式树完全翻译为SQL执行,string.Format、Convert.ToDouble都是C#原生方法,没有对应的SQL转译规则,所以抛出不支持异常。
可行实现方案
场景1:实体仅存经纬度数值字段,没有预存地理类型
首先提前在查询外构造参考点(内存执行,不需要EF转译),表达式中仅使用EF原生支持的SqlFunctions类下的方法做类型转换,这类方法内置了SQL转译规则:
// 先在内存中构造参考坐标点,不需要EF翻译 var referencePoint = DbGeography.PointFromText( $"POINT({other.Longitude.Value} {other.Latitude.Value})", 4326); var things = _context.Things .AsNoTracking() .Where(t => // SqlFunctions.StringConvert是EF内置支持的方法,可转译为SQL的字符串转换逻辑 DbGeography.PointFromText( "POINT(" + SqlFunctions.StringConvert((double)t.Longitude.Value) + " " + SqlFunctions.StringConvert((double)t.Latitude.Value) + ")", 4326 ).Distance(referencePoint) * 0.000621371 <= 50 ) .ToList();
场景2:可修改表结构(推荐,性能最优)
给Thing表新增geography类型的Location字段,设置为持久化计算列,取值为geography::STPointFromText('POINT(' + CAST(Longitude AS VARCHAR(20)) + ' ' + CAST(Latitude AS VARCHAR(20)) + ')', 4326),同时创建空间索引。
实体新增对应DbGeography类型的Location属性,查询可以简化为:
var referencePoint = DbGeography.PointFromText( $"POINT({other.Longitude.Value} {other.Latitude.Value})", 4326); var things = _context.Things .AsNoTracking() .Where(t => t.Location.Distance(referencePoint) * 0.000621371 <= 50) .ToList();
该方案不需要每条数据动态构造地理点,还能走空间索引,查询性能比场景1高10倍以上。
注意事项
- WKT坐标点格式为
POINT(经度 纬度),不要颠倒经纬度顺序,否则距离计算结果会完全错误 - SRID 4326对应的距离返回单位为米,乘以
0.000621371转换为英里的逻辑是正确的 - 若使用EF Core,需引入
Microsoft.EntityFrameworkCore.SqlServer.NetTopologySuite包,使用NetTopologySuite.Geometries.Point类型表示坐标,EF Core可直接翻译Point的构造逻辑,无需拼接WKT。
内容的提问来源于stack exchange,提问作者Dan G
相关产品推荐
相关产品推荐

