![](/img/trans.png)
[英]Entity Framework Core saving geography::Point throws "The supplied value is not a valid instance of data type geography"
[英]Entity Framework Core 3.1 with NetTopologySuite.Geometries.Point: SqlException: The supplied value is not a valid instance of data type geography
我有一个看起来像这样的模型:
public class Facility
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public NetTopologySuite.Geometries.Point Location { get; set; }
}
添加点的测试代码:
var testFacility = new Facility();
testFacility.Location = new NetTopologySuite.Geometries.Point(13.003725d, 55.604870d) { SRID = 3857 };
//Other values tested with the same error error
//testFacility.Location = new NetTopologySuite.Geometries.Point(13.003725d, 55.604870d);
//testFacility.Location = new NetTopologySuite.Geometries.Point(55.604870d, 13.003725d);
//var geometryFactory = NtsGeometryServices.Instance.CreateGeometryFactory(srid: 3857);
//var currentLocation = geometryFactory.CreatePoint(new Coordinate(13.003725d, 55.604870d));
//testFacility.Location = currentLocation;
db.Facilities.Add(testFacility);
//Exception on Save
db.SaveChanges();
我正在使用以下 NuGets,版本3.1.0
Microsoft.AspNetCore.Identity.EntityFrameworkCore
Microsoft.EntityFrameworkCore.SqlServer
Microsoft.EntityFrameworkCore.Tools
Microsoft.EntityFrameworkCore.SqlServer.NetTopologySuite
我保存的例外情况如下:
SqlException: 传入的表格数据流 (TDS) 远程过程调用 (RPC) 协议流不正确。 参数 7(“@p6”):提供的值不是数据类型 geography 的有效实例。 检查源数据是否存在无效值。 无效值的一个示例是比例大于精度的数字类型数据。
根据所有文档,经度应该是 X,纬度应该是 Y,所以我认为这不是问题。 我试图反转坐标以防万一,但我遇到了与您在我尝试过的示例中看到的相同的错误。
https://docs.microsoft.com/en-us/ef/core/modeling/spatial
纬度 = Y 经度 = X
https://gis.stackexchange.com/a/68856/71364
我找不到任何明显的错误。 设置了 Optionsbuilder,该表是使用 Data Type geography
创建的,它与DbGeography
for Entity Framework 6 非常配合。
var optionsBuilder = new DbContextOptionsBuilder<ApplicationDbContext>();
optionsBuilder.UseSqlServer("Server=(localdb)\\mssqllocaldb;Database=TestDb;Trusted_Connection=True;MultipleActiveResultSets=true",
x => x.UseNetTopologySuite());
var db = new ApplicationDbContext(optionsBuilder.Options);
我在 SQL Server 的文档中可以看到,没有针对单个Point
处理的特定情况。
https://docs.microsoft.com/en-us/ef/core/modeling/spatial#sql-server
我保存的坐标来自谷歌地图,因此使用EPSG 3857
。
我错过了什么?
TLDR
SQL Server sys.spatial_reference_systems
不存在 SRID
更改为4326
存在,它将起作用:
select *
from sys.spatial_reference_systems
where spatial_reference_id = '4326'
长答案:
Google Maps API 使用EPSG 3857
但 Google Maps Web 应用程序使用EPSG 4326
https://developers.google.com/maps/documentation/javascript/markers
https://www.google.com/maps/@55.604933,13.003662,14z
因此,应该像这样创建和保存 Google Maps Web Application 中的一个点:
var testFacility = new Facility();
testFacility.Location = new NetTopologySuite.Geometries.Point(13.003725d, 55.604870d) { SRID = 4326 };
db.Facilities.Add(testFacility);
db.SaveChanges();
然而,将EPSG 4326
坐标投影到EPSG 3857
坐标系有点棘手。 Microsoft 建议使用ProjNet4GeoAPI
所以我决定使用它。
https://docs.microsoft.com/en-us/ef/core/modeling/spatial#srid-ignored-during-client-operations
我已经验证它在这里工作:
http://epsg.io/transform#s_srs=4326&t_srs=3857&x=13.003725&y=55.604870
示例转换:
var x = 13.003725d;
var y = 55.604870d;
var epsg3857ProjectedCoordinateSystem = ProjNet.CoordinateSystems.ProjectedCoordinateSystem.WebMercator;
var epsg4326GeographicCoordinateSystem = ProjNet.CoordinateSystems.GeographicCoordinateSystem.WGS84;
var coordinateTransformationFactory = new ProjNet.CoordinateSystems.Transformations.CoordinateTransformationFactory();
var coordinateTransformation = coordinateTransformationFactory.CreateFromCoordinateSystems(epsg4326GeographicCoordinateSystem, epsg3857ProjectedCoordinateSystem);
var epsg4326Coordinate = new GeoAPI.Geometries.Coordinate(x, y);
var epsg3857Coordinate = coordinateTransformation.MathTransform.Transform(epsg4326Coordinate);
完整的示例程序:
要让它运行:
代码:
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Design;
using NetTopologySuite;
using NetTopologySuite.Geometries;
using ProjNet.CoordinateSystems;
using ProjNet.CoordinateSystems.Transformations;
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace TestConsoleAppEFGeo
{
public class ApplicationDbContextFactory : IDesignTimeDbContextFactory<ApplicationDbContext>
{
public ApplicationDbContext CreateDbContext(string[] args)
{
var optionsBuilder = new DbContextOptionsBuilder<ApplicationDbContext>();
optionsBuilder.UseSqlServer("Server=(localdb)\\mssqllocaldb;Database=TestApp;Trusted_Connection=True;MultipleActiveResultSets=true",
x => x.UseNetTopologySuite());
return new ApplicationDbContext(optionsBuilder.Options);
}
}
public class ApplicationDbContext : DbContext
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
public virtual DbSet<Facility> Facilities { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
}
}
public class Facility
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int Id { get; set; }
public NetTopologySuite.Geometries.Point Location { get; set; }
}
class Program
{
static void Main(string[] args)
{
var applicationDbContextFactory = new ApplicationDbContextFactory();
var db = applicationDbContextFactory.CreateDbContext(null);
var x = 13.003725d;
var y = 55.604870d;
var srid = 4326;
if (!db.Facilities.AnyAsync(x => x.Id == 1).Result)
{
var testFacility = new Facility();
var geometryFactory = NtsGeometryServices.Instance.CreateGeometryFactory(srid);
var currentLocation = geometryFactory.CreatePoint(new NetTopologySuite.Geometries.Coordinate(x, y));
testFacility.Id = 1;
testFacility.Location = currentLocation;
var testFacility2 = new Facility();
testFacility2.Id = 2;
testFacility2.Location = new Point(x, y) { SRID = srid };
db.Facilities.Add(testFacility);
db.Facilities.Add(testFacility2);
//Will throw an exception
//var testFacility3 = new Facility();
//testFacility3.Id = 3;
//testFacility3.Location = new Point(1447568.0454157612d, 7480155.2276327936d) { SRID = 3857 };
//db.Facilities.Add(testFacility3);
db.SaveChanges();
}
var facility1 = db.Facilities.FirstAsync(x => x.Id == 1).Result;
var facility2 = db.Facilities.FirstAsync(x => x.Id == 2).Result;
if(facility1.Location == facility2.Location)
{
Console.WriteLine("facility1.Location is equal to facility2.Location");
}
else
{
Console.WriteLine("facility1.Location is NOT equal to facility2.Location");
}
//Test conversion
//Show coordinate: http://epsg.io/map#srs=4326&x=13.003725&y=55.604870&z=14&layer=streets
//Conversion: http://epsg.io/transform#s_srs=4326&t_srs=3857&x=13.0037250&y=55.6048700
//Google Maps - https://www.google.se/maps shows EPSG:4326 when viewing a location
//https://epsg.io/3857 - Google Maps API is EPSG:3857 however
//Example: https://developers.google.com/maps/documentation/javascript/markers
var epsg3857ProjectedCoordinateSystem = ProjectedCoordinateSystem.WebMercator;
var epsg4326GeographicCoordinateSystem = GeographicCoordinateSystem.WGS84;
var coordinateTransformationFactory = new CoordinateTransformationFactory();
var coordinateTransformation = coordinateTransformationFactory.CreateFromCoordinateSystems(epsg4326GeographicCoordinateSystem, epsg3857ProjectedCoordinateSystem);
var epsg4326Coordinate = new GeoAPI.Geometries.Coordinate(facility1.Location.Coordinate.X, facility1.Location.Coordinate.Y);
var epsg3857Coordinate = coordinateTransformation.MathTransform.Transform(epsg4326Coordinate);
}
}
}
更多信息在这里:
确保您的 Lat 和 Long 位置正确,并且您提供了有效的 SRID。
它可能已经在 4326 中,快乐的日子,易于存储,sql 应该让您存储它(API 可能使用3857
但以度数而不是米提供位置的纬度/经度,实际上您已经在4326
获得了纬度/经度)
假设您在SRID=3857
中获得纬度/经度,并希望尝试以这种方式存储它:
检查你有一个相当于3857
的 SRID 版本,它可以在你的数据库中工作
SELECT * FROM sys.spatial_reference_systems
WHERE authorized_spatial_reference_id
IN('3857', '900913', '3587', '54004', '41001', '102113', '102100', '3785')
例如,如果您碰巧有900913尝试在没有转换的经纬度插入上使用它,如果您有它,我将基于将超链接“替代代码”的属性与EPSG:3857进行比较的假设
我不知道它是否会起作用,这完全不是我的知识领域。
假设您没有得到 SQL 行,那么您必须将3857
转换为4326
以存储在您的数据库中...
如何将3857
转换为4326
以便您可以存储它:
通过 NuGet 安装 ProjNet4GeoAPI 并使用以下代码:
using GeoAPI.Geometries;
using ProjNet.CoordinateSystems;
using ProjNet.CoordinateSystems.Transformations;
...
// setup
var epsg3857 = ProjectedCoordinateSystem.WebMercator;
var epsg4326 = GeographicCoordinateSystem.WGS84;
var convertTo4326 = new CoordinateTransformationFactory()
.CreateFromCoordinateSystems(epsg3857, epsg4326);
// input 6415816.17/171048.38 (Brussels lat/lon in meters SRID 3857)
// N.B. method called needs the values as lon/lat (x/y), not lat/lon
var coordIn3857 = new GeoAPI.Geometries.Coordinate(171048.38, 6415816.17);
var coordIn4326 = convertTo4326.MathTransform.Transform(coordIn3857);
// output 49.82379612579344/1.5365537407788388 (Brussels lat/lon in degrees SRID 4326)
现在去把它保存到你的数据库中
testFacility.Location = new NetTopologySuite.Geometries.Point(1.536553, 49.823796)
{ SRID = 4326 };
要在另一个方向转换并从存储的4326
值中使用3857
,很容易找出或查看 Ogglas 的答案
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.