空间数据
空间数据表示对象的物理位置和形状。 许多数据库都支持此类型的数据,因此可以同时索引和查询该数据与其他数据。 常见方案包括查询距某个位置指定距离内的对象,或选择边界包含指定位置的对象。 EF Core 支持使用 NetTopologySuite 空间库映射到空间数据类型。
安装
为在 EF Core 中使用空间数据,需安装相应的支持 NuGet 包。 需安装的包取决于所使用的提供程序。
EF Core 提供程序 | 空间 NuGet 包 |
---|---|
Microsoft.EntityFrameworkCore.SqlServer | Microsoft.EntityFrameworkCore.SqlServer.NetTopologySuite |
Microsoft.EntityFrameworkCore.Sqlite | Microsoft.EntityFrameworkCore.Sqlite.NetTopologySuite |
Microsoft.EntityFrameworkCore.InMemory | NetTopologySuite |
Npgsql.EntityFrameworkCore.PostgreSQL | Npgsql.EntityFrameworkCore.PostgreSQL.NetTopologySuite |
Pomelo.EntityFrameworkCore.MySql | Pomelo.EntityFrameworkCore.MySql.NetTopologySuite |
Devart.Data.MySql.EFCore | Devart.Data.MySql.EFCore.NetTopologySuite |
Devart.Data.Oracle.EFCore | Devart.Data.Oracle.EFCore.NetTopologySuite |
Devart.Data.PostgreSql.EFCore | Devart.Data.PostgreSql.EFCore.NetTopologySuite |
Devart.Data.SQLite.EFCore | Devart.Data.SQLite.EFCore.NetTopologySuite |
Teradata.EntityFrameworkCore | Teradata.EntityFrameworkCore.NetTopologySuite |
NetTopologySuite
NetTopologySuite (NTS) 是 .NET 的空间库。 EF Core 支持在模型中使用 NTS 类型映射到数据库中的空间数据类型。
若要支持通过 NTS 映射到空间类型,请调用提供程序的 DbContext 选项构建器上的 UseNetTopologySuite 方法。 例如,在 SQL Server 中可以通过以下方式对其进行调用。
options.UseSqlServer(
@"Data Source=(localdb)\MSSQLLocalDB;Initial Catalog=WideWorldImporters;ConnectRetryCount=0",
x => x.UseNetTopologySuite());
空间数据类型有多种。 使用哪种类型取决于要允许的形状类型。 以下是可在模型中用于属性的 NTS 类型的层次结构。 它们位于 NetTopologySuite.Geometries
命名空间内。
- 几何
- Point
- LineString
- 多边形
- GeometryCollection
- MultiPoint
- MultiLineString
- MultiPolygon
警告
NTS 不支持 CircularString、CompoundCurve 和 CurePolygon。
使用基本几何类型支持通过属性指定任意类型的形状。
经度和纬度
NTS 中的坐标以 X 和 Y 值表示。 若要表示经度和纬度,请使用 X 表示经度,使用 Y 表示纬度。 请注意,这与这些值的常见的 latitude, longitude
格式相反。
查询数据
以下实体类可用于映射到 Wide World Importers 示例数据库中的表。
[Table("Cities", Schema = "Application")]
public class City
{
public int CityID { get; set; }
public string CityName { get; set; }
public Point Location { get; set; }
}
[Table("Countries", Schema = "Application")]
public class Country
{
public int CountryID { get; set; }
public string CountryName { get; set; }
// Database includes both Polygon and MultiPolygon values
public Geometry Border { get; set; }
}
在 LINQ 中,可用作数据库函数的 NTS 方法和属性将转换为 SQL。 例如,以下查询中会转换 Distance 和 Contains 方法。 参阅提供程序文档,了解支持哪些方法。
// Find the nearest city
var nearestCity = db.Cities
.OrderBy(c => c.Location.Distance(currentLocation))
.FirstOrDefault();
// Find the containing country
var currentCountry = db.Countries
.FirstOrDefault(c => c.Border.Contains(currentLocation));
反向工程
空间 NuGet 包还支持具有空间属性的反向工程模型,但需先安装该包才能运行 Scaffold-DbContext
或 dotnet ef dbcontext scaffold
。 如果不这样做,你将收到未找到列的类型映射警告,并且这些列将被跳过。
客户端操作期间忽略的 SRID
NTS 忽略操作期间的 SRID 值。 它假定一个平面坐标系。 这意味着如果以经度和纬度为单位指定坐标,则部分客户端评估的值(例如距离、长度和面积)将以度为单位,而不是米。 若要获得更有意义的值,首先需要使用 ProjNet(用于 GeoAPI)等库将坐标投射到另一个坐标系。
注意
使用较新的 ProjNet NuGet 包,而不是名为 ProjNet4GeoAPI 的旧包。
如果 EF Core 通过 SQL 对操作进行服务器评估,则结果的单位将由数据库决定。
以下示例使用 ProjNet 计算两个城市之间的距离。
public static class GeometryExtensions
{
private static readonly CoordinateSystemServices _coordinateSystemServices
= new CoordinateSystemServices(
new Dictionary<int, string>
{
// Coordinate systems:
[4326] = GeographicCoordinateSystem.WGS84.WKT,
// This coordinate system covers the area of our data.
// Different data requires a different coordinate system.
[2855] =
@"
PROJCS[""NAD83(HARN) / Washington North"",
GEOGCS[""NAD83(HARN)"",
DATUM[""NAD83_High_Accuracy_Regional_Network"",
SPHEROID[""GRS 1980"",6378137,298.257222101,
AUTHORITY[""EPSG"",""7019""]],
AUTHORITY[""EPSG"",""6152""]],
PRIMEM[""Greenwich"",0,
AUTHORITY[""EPSG"",""8901""]],
UNIT[""degree"",0.01745329251994328,
AUTHORITY[""EPSG"",""9122""]],
AUTHORITY[""EPSG"",""4152""]],
PROJECTION[""Lambert_Conformal_Conic_2SP""],
PARAMETER[""standard_parallel_1"",48.73333333333333],
PARAMETER[""standard_parallel_2"",47.5],
PARAMETER[""latitude_of_origin"",47],
PARAMETER[""central_meridian"",-120.8333333333333],
PARAMETER[""false_easting"",500000],
PARAMETER[""false_northing"",0],
UNIT[""metre"",1,
AUTHORITY[""EPSG"",""9001""]],
AUTHORITY[""EPSG"",""2855""]]
"
});
public static Geometry ProjectTo(this Geometry geometry, int srid)
{
var transformation = _coordinateSystemServices.CreateTransformation(geometry.SRID, srid);
var result = geometry.Copy();
result.Apply(new MathTransformFilter(transformation.MathTransform));
return result;
}
private class MathTransformFilter : ICoordinateSequenceFilter
{
private readonly MathTransform _transform;
public MathTransformFilter(MathTransform transform)
=> _transform = transform;
public bool Done => false;
public bool GeometryChanged => true;
public void Filter(CoordinateSequence seq, int i)
{
var x = seq.GetX(i);
var y = seq.GetY(i);
var z = seq.GetZ(i);
_transform.Transform(ref x, ref y, ref z);
seq.SetX(i, x);
seq.SetY(i, y);
seq.SetZ(i, z);
}
}
}
var seattle = new Point(-122.333056, 47.609722) { SRID = 4326 };
var redmond = new Point(-122.123889, 47.669444) { SRID = 4326 };
// In order to get the distance in meters, we need to project to an appropriate
// coordinate system. In this case, we're using SRID 2855 since it covers the
// geographic area of our data
var distanceInDegrees = seattle.Distance(redmond);
var distanceInMeters = seattle.ProjectTo(2855).Distance(redmond.ProjectTo(2855));
注意
4326 指的是 WGS 84,一种在 GPS 和其他地理系统中使用的标准。
其他资源
特定于数据库的信息
务必阅读提供程序文档,了解有关使用空间数据的其他信息。
其他资源
- NetTopologySuite 文档
- .NET 数据社区 Standup 会话,重点讨论空间数据和 NetTopologySuite。