NET核心-在IEnumerable和IQueryable中产生不同的空间/地理结果



我们使用ASP。NET Core 5后端与SQL Server v15和实体框架V5,有一些有趣的事情我一直在努力,考虑一下:

您有一个如下表:

CREATE TABLE [dbo].[ContactDetails](
[Id] [int] IDENTITY(1,1) NOT NULL,
[Name] [nvarchar](128) NOT NULL,
...
[Geolocation] [geography] NULL
)

该表被其他表使用,例如Stores。,以存储联系人详细信息。

表ContactDetails随后自动搭建如下:

public partial class ContactDetail
{
public ContactDetail()
{
Stores = new HashSet<Store>();
}
public int Id { get; set; }
public string Name { get; set; }
public Geometry Geolocation { get; set; }
}    

然后,您根据输入存储Stores的经度和纬度(双(,比如说:

Geometry Store.ContactDetails.Geolocation = new NetTopologySuite.Geometries.Point(store.longitude, store.latitude) { SRID = 4326 };

现在从用户那里得到一个位置,它是以相同的方式创建的

Geometry userLocation = new NetTopologySuite.Geometries.Point(user.longitude, user.latitude) { SRID = 4326 };

你想知道每个商店离用户有多远。你会怎么做?

我做到了:

DbContext dbContext; //...somehow constructed
var dbSet = dbContext.Set<Store>();
var distances = dbSet.Where(...).Select(store => store.ContactDetails.Geolocation.Distance(userLocation));

但令我惊讶的是,距离完全偏离了!我预计距离是以度为单位返回的,我将不得不将其转换为Kms。但这些数字是完全错误的(比如83130(。

所以我尝试了这个:

var anotherDistances = dbSet.Where(...).AsEnumerable().Select(store => store.ContactDetails.Geolocation.Distance(userLocation));

这一次的距离和预期的一样(例如1.245度(。

那里发生了什么?我需要将结果保持为IQueryable,这样我就可以有效地进行进一步的过滤和转换。如何在不必将IQueryable转换为IEnumerable的情况下获得正确的距离?

我无法从其他问题中找到答案,我怀疑Linq无法正确地将查询转换为SQL,并且AsEnumerable()之后的查询在内存中加载对象时能够使用正确的类型推断和空间正确的方法。但这只是一些模糊的理解。如果你能帮我理解这一点,我将不胜感激。

是否可以使用IQuery<>.ToString()来显示正在创建的实际SQL查询?也许这会给你指明正确的方向。此外,也许这个答案很有趣。

此外,您可能需要查看正在使用的数据库。根据文件:

如果EF Core通过SQL对操作进行服务器评估,则结果单位将由数据库确定。

好吧,谜团解开了。感谢Dominik的@Dominik-berse建议,我更深入地研究了一下。

当查询从LINQ转换为SQL时,映射的函数不必是等效的。在这种情况下,NetTopologySuite。几何图形。Distance((被翻译为geography::STDistance(((-根据SRID模型的类型,它以不同的单位返回距离,在4326模型(GPS使用(的情况下,它是米。

当查询转换为对象时,将直接调用对象的方法,在本例中为NetTopologySuite。几何图形。距离((。

这两个值可能完全不同。

最新更新