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 语句中返回两个纬度/经度坐标之间的计算距离的主要内容,如果未能解决你的问题,请参考以下文章