首页 > 解决方案 > .NET Core - IEnumerable 与 IQueryable 产生不同的空间/地理结果

问题描述

我们将 ASP.NET Core 5 后端与 SQL Server v15 和 Entity Framework 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; }
}    

然后你从输入中存储商店的经度和纬度(双精度)让我们说:

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));

但令我惊讶的是,距离完全消失了!我希望距离以度为单位返回,我将不得不将其转换为公里。但这些数字完全不存在(如 83130)。

所以我尝试了这个:

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

而这一次的距离与预期一致(例如 1.245 度)。

那里发生了什么?我需要保留结果,IQueryable以便可以有效地进行进一步的过滤和转换。如何获得正确的距离而无需转换IQueryableIEnumerable

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

标签: c#sql-serverentity-frameworklinqasp.net-core

解决方案


啊好吧,谜团解开了。感谢 Dominik 的@dominik-berse 建议,我进一步挖掘。

当查询从 LINQ 转换为 SQL 时,被映射的函数不必是等价的。在这种情况下,NetTopologySuite.Geometries.Distance() 被转换为 geography::STDistance() - 这取决于 SRID 模型的类型,以不同的单位返回距离,如果是 4326 模型(由 GPS 使用),它是米。

当查询转换为对象时,直接调用对象的方法,在本例中为 NetTopologySuite.Geometries.Distance()。

而且这两个值可以完全不同。


推荐阅读