-
Notifications
You must be signed in to change notification settings - Fork 3.4k
Description
Using EFCore/NTS/SpatiaLite creating a table from an entity containing an IPoint property built with .ForSqliteHasDimension(Ordinates.XYZ) works as expected but trying to save an entity to the table fails with '[point] violates Geometry constraint [geom-type or SRID not allowed]'. This failure occurs with both
Microsoft.EntityFrameworkCore.Sqlite.NetTopologySuite 2.2 or 3.0.0-preview.18572.1 .
Exception message:
Error Saving New Xyz Point Microsoft.EntityFrameworkCore.DbUpdateException: An error occurred while updating the entries. See the inner exception for details. ---> Microsoft.Data.Sqlite.SqliteException: SQLite Error 19: 'Xyzs.XyzPoint violates Geometry constraint [geom-type or SRID not allowed]'.
Stack trace:
at Microsoft.Data.Sqlite.SqliteException.ThrowExceptionForRC(Int32 rc, sqlite3 db)
at Microsoft.Data.Sqlite.SqliteCommand.ExecuteReader(CommandBehavior behavior)
at Microsoft.Data.Sqlite.SqliteCommand.ExecuteDbDataReader(CommandBehavior behavior)
at System.Data.Common.DbCommand.ExecuteReader()
at Microsoft.EntityFrameworkCore.Storage.Internal.RelationalCommand.Execute(IRelationalConnection connection, DbCommandMethod executeMethod, IReadOnlyDictionary`2 parameterValues)
at Microsoft.EntityFrameworkCore.Storage.Internal.RelationalCommand.ExecuteReader(IRelationalConnection connection, IReadOnlyDictionary`2 parameterValues)
at Microsoft.EntityFrameworkCore.Update.ReaderModificationCommandBatch.Execute(IRelationalConnection connection)
--- End of inner exception stack trace ---
at Microsoft.EntityFrameworkCore.Update.ReaderModificationCommandBatch.Execute(IRelationalConnection connection)
at Microsoft.EntityFrameworkCore.Update.Internal.BatchExecutor.Execute(DbContext _, ValueTuple`2 parameters)
at Microsoft.EntityFrameworkCore.Storage.Internal.NoopExecutionStrategy.Execute[TState,TResult](TState state, Func`3 operation, Func`3 verifySucceeded)
at Microsoft.EntityFrameworkCore.Update.Internal.BatchExecutor.Execute(IEnumerable`1 commandBatches, IRelationalConnection connection)
at Microsoft.EntityFrameworkCore.Storage.RelationalDatabase.SaveChanges(IReadOnlyList`1 entries)
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChanges(IReadOnlyList`1 entriesToSave)
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChanges(Boolean acceptAllChangesOnSuccess)
at Microsoft.EntityFrameworkCore.DbContext.SaveChanges(Boolean acceptAllChangesOnSuccess)
at ConsoleTest.Program.Main(String[] args) in C:\Code\SpatialiteXyzPointTestEfCore\ConsoleTest\Program.cs:line 95
Steps to reproduce
Create a new netcoreapp3.0 console project, add Microsoft.EntityFrameworkCore.Sqlite, Microsoft.EntityFrameworkCore.Sqlite.NetTopologySuite (either 2.2 or 3 preview) and mod_spatialite.
Create an entity with an IPoint property with .ForSqliteHasSrid(4326) and .ForSqliteHasDimension(Ordinates.XYZ).
Create the db, set the IPoint property and save - an error is thrown.
'Show Spatial Metadata' in the spatialite_gui on the generated column reports:
f_geometry_column xyzpoint
geometry_type 1001
coord_dimension 3
srid 4326
There are quite a few details here that I don't have much experience with but to try to eliminate general database/code errors and srid as issues the code sample also builds an XY point - which saves without error. I did wonder about the geometry_type of 1001 vs 1 for an xypoint is expected by EF...
The zipped solution and project included uses the code below to show the issue.
SpatialiteXyzPointTestEfCore.zip
using System;
using GeoAPI.Geometries;
using Microsoft.EntityFrameworkCore;
using NetTopologySuite;
using NetTopologySuite.Geometries;
namespace ConsoleTest
{
public class SpatialTestContext : DbContext
{
public DbSet<Xy> Xys { get; set; }
public DbSet<Xyz> Xyzs { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.EnableSensitiveDataLogging();
optionsBuilder.UseSqlite("Filename=./Xyz.sqlite", x => x.UseNetTopologySuite());
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Xy>().Property(c => c.XyPoint)
.ForSqliteHasSrid(4326);
modelBuilder.Entity<Xy>().Property(c => c.XyPoint)
.ForSqliteHasDimension(Ordinates.XY);
modelBuilder.Entity<Xyz>().Property(c => c.XyzPoint)
.ForSqliteHasSrid(4326);
modelBuilder.Entity<Xyz>().Property(c => c.XyzPoint)
.ForSqliteHasDimension(Ordinates.XYZ);
}
}
public class Xy
{
public int Id { get; set; }
public string Note { get; set; }
public IPoint XyPoint { get; set; }
}
public class Xyz
{
public int Id { get; set; }
public string Note { get; set; }
public IPoint XyzPoint { get; set; }
}
internal class Program
{
private static void Main(string[] args)
{
using (var context = new SpatialTestContext())
{
context.Database.EnsureDeleted();
context.Database.EnsureCreated();
Console.WriteLine($"Create New Xy Point");
var geometryFactory = NtsGeometryServices.Instance.CreateGeometryFactory(srid: 4326);
var xyLocation = geometryFactory.CreatePoint(new Coordinate(-122.121512, 47.6739882));
var newXyPoint = new Xy
{
Note = $"{DateTime.Now} New Xy Point",
XyPoint = xyLocation
};
context.Xys.Add(newXyPoint);
try
{
Console.WriteLine($"Saving New Xy Point");
context.SaveChanges(true);
}
catch (Exception e)
{
Console.WriteLine($"Error Saving New Xy Point {e}");
}
Console.WriteLine($"Create New Xyz Point");
var xyzPoint = geometryFactory.CreatePoint(new Coordinate(-122.121512, 47.6739882, 1000));
var newXyzPoint = new Xyz
{
Note = $"{DateTime.Now} New Xyz Point",
XyzPoint = xyzPoint
};
context.Xyzs.Add(newXyzPoint);
try
{
Console.WriteLine($"Saving New Xyz Point");
context.SaveChanges(true);
}
catch (Exception e)
{
Console.WriteLine($"Error Saving New Xyz Point {e}");
}
}
}
}
}Further technical details
EF Core version: netcoreapp3.0
Database Provider: Microsoft.EntityFrameworkCore.Sqlite.NetTopologySuite
Operating system: Win10 1809
IDE: VS 2019 Version 16.0.0 Preview 1.1