C# 在 Linq 查询 WHERE 语句中返回两个纬度/经度坐标之间的计算距离

Posted

技术标签:

【中文标题】C# 在 Linq 查询 WHERE 语句中返回两个纬度/经度坐标之间的计算距离【英文标题】:C# Return a Calculated Distance between two Lat/Long Coordinates within a Linq Query WHERE statement 【发布时间】:2021-11-18 01:00:03 【问题描述】:

我试图通过将距离计算移到 LINQ to SQL 中来提高性能,而不是对可枚举执行计算。具体来说,我正在尝试以英里为单位计算距离并过滤 50 英里半径内的实体。

这是我的代码(抱歉缺乏可读性):

List<Thing> thing = _context.Things
  .AsNoTracking()
  .Where(t => (DbGeography.PointFromText(
      string.Format(
        "POINT(0 1",
        t.Latitude.Value, 
        t.Longitude.Value)
    ,4326)
   .Distance(
      DbGeography.PointFromText(
        string.Format(
          "POINT(0 1",
          other.Latitude.Value, 
          other.Longitude.Value),
      ,4326)
      * 0.000621371) <=50)
  .ToList();

最终,我需要将其重构为可在 LINQ 中使用的替代方案。这是错误:

 System.NotSupportedException : LINQ to Entities does not recognize the method 'System.String Format(System.String, System.Object, System.Object)' method, and this method cannot be translated into a store expression.

Stack Trace: 
DefaultTranslator.Translate(ExpressionConverter parent, MethodCallExpression call)
MethodCallTranslator.TypedTranslate(ExpressionConverter parent, MethodCallExpression linq)
TypedTranslator`1.Translate(ExpressionConverter parent, Expression linq)
ExpressionConverter.TranslateExpression(Expression linq)
ExpressionConverter.TranslateIntoCanonicalFunction(String functionName, Expression Expression, Expression[] linqArguments)
SpatialMethodCallTranslator.Translate(ExpressionConverter parent, MethodCallExpression call)
MethodCallTranslator.TypedTranslate(ExpressionConverter parent, MethodCallExpression linq)
TypedTranslator`1.Translate(ExpressionConverter parent, Expression linq)
ExpressionConverter.TranslateExpression(Expression linq)
ExpressionConverter.TranslateIntoCanonicalFunction(String functionName, Expression Expression, Expression[] linqArguments)
<27 more frames...>
ObjectContext.ExecuteInTransaction[T](Func`1 func, IDbExecutionStrategy executionStrategy, Boolean startLocalTransaction, Boolean releaseConnectionOnSuccess)
<>c__DisplayClass7.<GetResults>b__5()
DefaultSqlExecutionStrategy.Execute[TResult](Func`1 operation)
ObjectQuery`1.GetResults(Nullable`1 forMergeOption)
IEnumerable<T>.GetEnumerator>b__0()
LazyEnumerator`1.MoveNext()
List`1.ctor(IEnumerable`1 collection)
Enumerable.ToList[TSource](IEnumerable`1 source)

我也试过这个:

SqlGeography.Point(
  Convert.ToDouble(t.Latitude.Value),
  Convert.ToDouble(t.Longitude.Value),
  4326)
.STDistance(
  SqlGeography.Point(
    Convert.ToDouble(other.Latitude.Value), 
    Convert.ToDouble(other.Longitude.Value), 
    4326)) * 0.000621371) <= 50)

Same System.NotSupportedException 用于 Linq 到 SQL 的转换。

【问题讨论】:

我建议你稍微打开你的数据库表并将纬度存储在地理/点列上,然后使用 EF 支持空间docs.microsoft.com/en-us/ef/core/modeling/spatial 还需要注意的是,当您使用 XFromText 方法解析 WKT 时,点的顺序是 LONG LAT,而不是 LAT LONG,但是当您使用 geography::Point(lat, long,srid) 他们 LAT LONG。您的距离并不重要,因为您在这里拥有它,因为它们都(始终)错误地绕道但是如果您例如将所有坐标作为地理 Poonts 正确放入表格然后不小心创建了一个点千里之外,因为纬度/经度是倒置的.. 我对 LINQ-to-entities 没有太多经验,但我想它会尝试将您的语句转换为 SQL,并且找不到 String.Format 的等效 SQL 表达式。我还注意到您在POINT(0 0 中缺少) 【参考方案1】:

我通过连接 DbGeography.FromText() 中的字符串(从 DbGeography.PointFromText() 更改)解决了这个问题。也确实必须将 lat long 反转为 long lat。

DbGeography.FromText("POINT(" + t.Longitude.Value + " " + t.Latitude.Value + ")"
,4326)

【讨论】:

以上是关于C# 在 Linq 查询 WHERE 语句中返回两个纬度/经度坐标之间的计算距离的主要内容,如果未能解决你的问题,请参考以下文章

c# linq 动态多条件查询语句的写法

C# - 在 Linq Where Any 语句中使用的要列出的字符串

c# linq语句的where该怎样动态赋值?

C# SQL条件查询语句where中使用变量的用法

C#基础:LINQ 查询函数整理

LINQ 查询在 C# 中使用实体框架获取单列和单行值而不使用 Where