如何使用空格搜索邮政编码的半径?

Ste*_*a D 12 c# entity-framework sql-server-2012 entity-framework-6

背景

我正在编写一个应用程序,它可以在邮政编码的某个半径范围内查找事件.您可以将其视为售票员,您可以在其中输入您的邮政编码,并显示半径为x的所有音乐会.

我有一个数据库表,其中包含邮政编码,每个邮政编码都有"纬度和经度".我还有一个'EventListings'表,其中每个'Event'都有一个ZipCode字段.

问题

目前,我在服务层的Linq-to-Entities查询中使用Haversine公式来查找哪些事件在半径范围内.现在,我将它用作where子句中的过滤器.我也想把它放在select子句中,所以我可以在网站上显示"这距离4.6英里",等等.

我无法将此代码移动到单独的C#方法中,因为Linq-to-Entities会抱怨它无法将其转换为sql,因此我也会在select语句中复制整个公式.这非常难看.我试着解决它.

我试过的

我编辑了Entity,并添加了一个特殊的标量属性"DistanceFromOrigin".然后,我创建了一个存储过程,它返回了所有实体数据,以及新字段"DistanceFromOrigin"的硬编码值(用于测试目的).

然后我才意识到我无法告诉实体框架在EventListings实体上使用我的sproc作为其select语句...... Phil提出了spatials,这就是我的用法.

如何使用Spatials搜索邮政编码范围内的事件?

Ste*_*a D 22

所以,使用菲尔的建议,我用空间重新写了很多这个.这很好,你只需要.NET4.5,EF5 +和sql server(我相信2008年及以上).我正在使用EF6 + Sql Server 2012.

建立

第一步是在Geography我的数据库EventListings表中添加一列(右键单击它 - >设计).我命名我的位置:

在此输入图像描述

接下来,由于我首先使用EDM和数据库,我不得不更新我的模型以使用我创建的新字段.然后我收到一个关于无法将Geography转换为double的错误,所以我要解决的是选择实体中的Location属性,转到其属性并将其类型从double更改为Geography:

在此输入图像描述

在半径内查询并使用位置添加事件

然后你要做的就是查询你的实体集合,如下所示:

 var events = EventRepository.EventListings
                             .Where(x => x.Location.Distance(originCoordinates) * 0.00062 <= radiusParam); 
Run Code Online (Sandbox Code Playgroud)

距离扩展方法获取从当前对象到您传入的"其他"对象的距离.此另一个对象属于类型DbGeography.你只需要调用静态方法,然后创建其中一只小狗,然后将经度和纬度作为一个点:

DBGeography originCoordinates = DBGeography.fromText("Point(" + originLongitude + " " + originLatitude + ")");
Run Code Online (Sandbox Code Playgroud)

这不是我创建originCoordinates的方式.我下载了一个单独的数据库,其中包含所有邮政编码及其纬度和经度的列表.我也添加了一个类型的列Geography.我将在这个答案结尾处展示.然后,我查询了zipcode上下文,以从ZipCode表中的Location字段获取DbGeography对象.

如果用户需要比邮政编码更具体的来源,我会调用Google Maps API(GeoCode更具体)并通过Web服务获取用户特定地址的纬度和经度,并从中创建DBGeography对象响应中的纬度和经度值.

我也在创建活动时使用谷歌API.在将我的实体添加到EF之前,我只是设置了这样的位置变量:

someEventEntity.Location = DBGeography.fromText("Point(" + longitudeFromGoogle+ " " + latitudeFromGoogle + ")");
Run Code Online (Sandbox Code Playgroud)

其他提示与技巧和疑难解答

我是如何获得距离扩展方法的?

要获得扩展方法Distance,必须添加对项目的引用:System.Data.Entity 执行此操作后,必须将using:添加using System.Data.Entity.Spatial;到您的类中.

Distance扩展方法返回一个距离的米计量单位(你可以改变它,我认为,但是这是默认值).在这里,我的半径参数是英里,所以我做了一些数学转换.

注意:有一个DBGeography班级System.Data.Spatial.这是错误的,它对我不起作用.我在互联网上找到的很多例子都使用了这个例子.

如何将Lat/Long转换为Geography Column

所以,如果你像我一样下载了包含所有Latitude&Longitude列的邮政编码数据库,然后意识到它没有地理列......这可能会有所帮助:

1)在表格中添加"位置"列.将类型设置为Geography.

2)执行以下sql

UPDATE [ZipCodes]
SET Location = geography::STPointFromText('POINT(' + CAST([Longitude] AS VARCHAR(20)) + ' ' + CAST([Latitude] AS VARCHAR(20)) + ')', 4326)
Run Code Online (Sandbox Code Playgroud)

如何查看地理项目的详细信息

因此,当您在插入一些DbGeography项后查询Sql Server Management Studio中的EventListings表时,您将看到该Location列包含十六进制值,如:0x1234513462346.如果要确保插入正确的值,这不是很有用.

要实际查看此字段的纬度和经度,您必须如下查询:

SELECT Location.Lat Latitude, Location.Long Longitude
FROM [EventListings]
Run Code Online (Sandbox Code Playgroud)