Freigeben über


Räumliche Daten

Räumliche Daten stellen die physische Position und das Shape von Objekten dar. Viele Datenbanken bieten Unterstützung für diese Art von Daten, sodass sie zusammen mit anderen Daten indiziert und abgefragt werden kann. Häufige Szenarien umfassen das Abfragen von Objekten innerhalb eines bestimmten Abstands von einer Position oder das Auswählen des Objekts, dessen Rahmen eine bestimmte Position enthält. EF Core unterstützt die Zuordnung zu räumlichen Datentypen mithilfe der räumlichen NetTopologySuite-Bibliothek.

Installieren

Um räumliche Daten mit EF Core zu verwenden, müssen Sie das entsprechende unterstützende NuGet-Paket installieren. Welches Paket Sie installieren müssen, hängt vom anbieter ab, den Sie verwenden.

EF-Core-Anbieter Spatial NuGet-Paket
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) ist eine räumliche Bibliothek für .NET. EF Core ermöglicht die Zuordnung zu räumlichen Datentypen in der Datenbank mithilfe von NTS-Typen in Ihrem Modell.

Um die Zuordnung zu räumlichen Typen über NTS zu ermöglichen, rufen Sie die UseNetTopologySuite-Methode im DbContext-Options-Generator des Anbieters auf. Wenn Sie beispielsweise SQL Server wie folgt aufrufen möchten.

options.UseSqlServer(
    @"Data Source=(localdb)\MSSQLLocalDB;Initial Catalog=WideWorldImporters;ConnectRetryCount=0",
    x => x.UseNetTopologySuite());

Es gibt mehrere räumliche Datentypen. Welchen Typ Sie verwenden, hängt von den Formen ab, die Sie zulassen möchten. Dies ist die Hierarchie von NTS-Typen, die Sie für Eigenschaften in Ihrem Modell verwenden können. Sie befinden sich im NetTopologySuite.Geometries Namespace.

  • Geometrie
    • Punkt
    • LineString
    • Vieleck
    • GeometryCollection
      • MultiPoint
      • MultiLineString
      • MultiPolygon

Warnung

CircularString, CompoundCurve und CurePolygon werden von NTS nicht unterstützt.

Mithilfe des Basisgeometrietyp kann jeder Typ von Form durch die Eigenschaft festgelegt werden.

Längengrad und Breitengrad

Koordinaten in NTS beziehen sich auf X- und Y-Werte. Um Längengrad und Breitengrad darzustellen, verwenden Sie X für Längengrad und Y für breitengrad. Beachten Sie, dass dies umgekehrt zum Format ist, in dem diese Werte normalerweise angezeigt werden.

Abfrage von Daten

Die folgenden Entitätsklassen können verwendet werden, um Tabellen in der Beispieldatenbank für Wide World Importers zuzuordnen.

[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; }
}

In LINQ werden die NTS-Methoden und -Eigenschaften, die als Datenbankfunktionen verfügbar sind, in SQL übersetzt. Die Methoden "Distance" und "Contains" werden beispielsweise in den folgenden Abfragen übersetzt. Sehen Sie sich die Dokumentation Ihres Anbieters an, für welche Methoden unterstützt werden.

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

Reverse Engineering

Die räumlichen NuGet-Pakete ermöglichen auch Reverse Engineering-Modelle mit räumlichen Eigenschaften, aber Sie müssen das Paket vor dem Ausführen Scaffold-DbContext oder dotnet ef dbcontext scaffoldinstallieren. Wenn Sie dies nicht tun, erhalten Sie Warnungen darauf, dass keine Typzuordnungen für die Spalten gefunden werden, und die Spalten werden übersprungen.

SRID wird während Clientvorgängen ignoriert

NTS ignoriert SRID-Werte während des Betriebs. Es wird angenommen, dass ein planares Koordinatensystem vorhanden ist. Dies bedeutet, dass wenn Sie Koordinaten in Bezug auf Längengrad und Breitengrad angeben, werden einige vom Client ausgewertete Werte wie Entfernung, Länge und Fläche in Grad und nicht in Metern angegeben. Für aussagekräftigere Werte müssen Sie zunächst die Koordinaten mithilfe einer Bibliothek wie ProjNet (für GeoAPI) auf ein anderes Koordinatensystem projizieren.

Hinweis

Verwenden Sie das neuere ProjNet NuGet-Paket, nicht das ältere Paket namens "ProjNet4GeoAPI".

Wenn ein Vorgang von EF Core über SQL ausgewertet wird, wird die Ergebniseinheit von der Datenbank bestimmt.

Hier ist ein Beispiel für die Verwendung von ProjNet zum Berechnen des Abstands zwischen zwei Städten.

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

Hinweis

4326 bezieht sich auf WGS 84, einen Standard, der in GPS und anderen geografischen Systemen verwendet wird.

Weitere Ressourcen

Datenbankspezifische Informationen

Lesen Sie unbedingt die Dokumentation Ihres Anbieters, um weitere Informationen zum Arbeiten mit räumlichen Daten zu erhalten.

Weitere Ressourcen