空间数据表示对象的物理位置和形状。 许多数据库为此类数据提供支持,以便可以与其他数据一起编制索引和查询数据。 常见方案包括查询给定距离某个位置内的对象,或选择其边框包含给定位置的对象。 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 |
FileBaseContext | 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
。
- 几何
- 点
- 线串
- 多边形
- GeometryCollection
- MultiPoint
- 多线字符串
- MultiPolygon
警告
NTS 不支持 CircularString、CompoundCurve 和 CurePolygon。
使用基本的 Geometry 类型,可以通过属性指定任何类型的形状。
经度和纬度
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 = await db.Cities
.OrderBy(c => c.Location.Distance(currentLocation))
.FirstOrDefaultAsync();
// Find the containing country
var currentCountry = await db.Countries
.FirstOrDefaultAsync(c => c.Border.Contains(currentLocation));
反向工程
空间 NuGet 包还支持具有空间属性的反向工程模型,但需在运行Scaffold-DbContext
或运行dotnet ef dbcontext scaffold
之前安装该包。 如果不这样做,您将收到有关未找到列类型映射的警告,并会跳过这些列。
在客户端操作期间忽略 SRID
NTS 在进行操作时忽略 SRID 值。 它假定平面坐标系。 这意味着,如果在经度和纬度方面指定坐标,则某些客户端评估的值(如距离、长度和面积)将以度而不是米为单位。 对于更有意义的值,首先需要使用 ProjNet(for GeoAPI)等库将坐标投影到另一个坐标系。
注释
使用较新的 ProjNet NuGet 包, 而不是 名为 ProjNet4GeoAPI 的旧包。
如果通过 SQL 经由 EF Core 对操作进行服务器评估,则结果的单位将由数据库确定。
下面是使用 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 Docs
- .NET 数据社区站立会话,侧重于空间数据和 NetTopologySuite。