Skip to content

Commit

Permalink
SQLite Spatial: Support Z and M
Browse files Browse the repository at this point in the history
Removes `HasGeometricDimension`. Use `HasColumnType` instead.

    - x.Property(e => e.Geometry).HasGeometricDimension(Ordinates.XYZ)
    + x.Property(e => e.Geometry).HasColumnType("GEOMETRYZ")

Fixes #14257
  • Loading branch information
bricelam committed Jun 5, 2020
1 parent fb28b56 commit 49200d0
Show file tree
Hide file tree
Showing 25 changed files with 138 additions and 251 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -80,46 +80,5 @@ public static bool CanSetSrid(
SqliteAnnotationNames.Srid,
srid,
fromDataAnnotation);

/// <summary>
/// Configures the dimension of the column that the property maps to when targeting SQLite.
/// </summary>
/// <param name="propertyBuilder"> The builder for the property being configured. </param>
/// <param name="dimension"> The dimension. </param>
/// <param name="fromDataAnnotation"> Indicates whether the configuration was specified using a data annotation. </param>
/// <returns>
/// The same builder instance if the configuration was applied,
/// <see langword="null" /> otherwise.
/// </returns>
public static IConventionPropertyBuilder HasGeometricDimension(
[NotNull] this IConventionPropertyBuilder propertyBuilder,
[CanBeNull] string dimension,
bool fromDataAnnotation = false)
{
if (propertyBuilder.CanSetGeometricDimension(dimension, fromDataAnnotation))
{
propertyBuilder.Metadata.SetGeometricDimension(dimension, fromDataAnnotation);

return propertyBuilder;
}

return null;
}

/// <summary>
/// Returns a value indicating whether the given value can be set as the dimension for the column.
/// </summary>
/// <param name="propertyBuilder"> The builder for the property being configured. </param>
/// <param name="dimension"> The dimension. </param>
/// <param name="fromDataAnnotation"> Indicates whether the configuration was specified using a data annotation. </param>
/// <returns> <see langword="true" /> if the given value can be set as the dimension for the column. </returns>
public static bool CanSetGeometricDimension(
[NotNull] this IConventionPropertyBuilder propertyBuilder,
[CanBeNull] string dimension,
bool fromDataAnnotation = false)
=> Check.NotNull(propertyBuilder, nameof(propertyBuilder)).CanSetAnnotation(
SqliteAnnotationNames.Srid,
dimension,
fromDataAnnotation);
}
}
55 changes: 0 additions & 55 deletions src/EFCore.Sqlite.Core/Extensions/SqlitePropertyExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -69,60 +69,5 @@ public static void SetSrid([NotNull] this IConventionProperty property, int? val
/// <returns> The <see cref="ConfigurationSource" /> for the column SRID. </returns>
public static ConfigurationSource? GetSridConfigurationSource([NotNull] this IConventionProperty property)
=> property.FindAnnotation(SqliteAnnotationNames.Srid)?.GetConfigurationSource();

/// <summary>
/// Returns the dimension to use when creating a column for this property.
/// </summary>
/// <param name="property"> The property. </param>
/// <returns> The dimension to use when creating a column for this property. </returns>
public static string GetGeometricDimension([NotNull] this IProperty property)
=> (string)property[SqliteAnnotationNames.Dimension];

/// <summary>
/// Returns the dimension to use when creating a column for this property.
/// </summary>
/// <param name="property"> The property. </param>
/// <param name="tableName"> The table name. </param>
/// <param name="schema"> The schema. </param>
/// <returns> The dimension to use when creating a column for this property. </returns>
public static string GetGeometricDimension([NotNull] this IProperty property, [NotNull] string tableName, [CanBeNull] string schema)
{
var annotation = property.FindAnnotation(SqliteAnnotationNames.Dimension);
if (annotation != null)
{
return (string)annotation.Value;
}

var sharedTableRootProperty = property.FindSharedTableRootProperty(tableName, schema);
return sharedTableRootProperty != null
? sharedTableRootProperty.GetGeometricDimension(tableName, schema)
: null;
}

/// <summary>
/// Sets the dimension to use when creating a column for this property.
/// </summary>
/// <param name="property"> The property. </param>
/// <param name="value"> The dimension. </param>
public static void SetGeometricDimension([NotNull] this IMutableProperty property, [CanBeNull] string value)
=> property.SetOrRemoveAnnotation(SqliteAnnotationNames.Dimension, value);

/// <summary>
/// Sets the dimension to use when creating a column for this property.
/// </summary>
/// <param name="property"> The property. </param>
/// <param name="value"> The dimension. </param>
/// <param name="fromDataAnnotation"> Indicates whether the configuration was specified using a data annotation. </param>
public static void SetGeometricDimension(
[NotNull] this IConventionProperty property, [CanBeNull] string value, bool fromDataAnnotation = false)
=> property.SetOrRemoveAnnotation(SqliteAnnotationNames.Dimension, value, fromDataAnnotation);

/// <summary>
/// Gets the <see cref="ConfigurationSource" /> for the column dimension.
/// </summary>
/// <param name="property"> The property. </param>
/// <returns> The <see cref="ConfigurationSource" /> for the column dimension. </returns>
public static ConfigurationSource? GetGeometricDimensionConfigurationSource([NotNull] this IConventionProperty property)
=> property.FindAnnotation(SqliteAnnotationNames.Dimension)?.GetConfigurationSource();
}
}
14 changes: 0 additions & 14 deletions src/EFCore.Sqlite.Core/Internal/SqliteModelValidator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -112,20 +112,6 @@ protected override void ValidateCompatible(
columnName,
tableName));
}

var propertyDimension = property.GetGeometricDimension(tableName, schema);
var duplicatePropertyDimension = duplicateProperty.GetGeometricDimension(tableName, schema);
if (propertyDimension != duplicatePropertyDimension)
{
throw new InvalidOperationException(
SqliteStrings.DuplicateColumnNameDimensionMismatch(
duplicateProperty.DeclaringEntityType.DisplayName(),
duplicateProperty.Name,
property.DeclaringEntityType.DisplayName(),
property.Name,
columnName,
tableName));
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -66,13 +66,5 @@ public static class SqliteAnnotationNames
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public const string Srid = Prefix + "Srid";

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public const string Dimension = Prefix + "Dimension";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -75,12 +75,6 @@ public override IEnumerable<IAnnotation> For(IColumn column)
{
yield return new Annotation(SqliteAnnotationNames.Srid, srid);
}

var dimension = property.GetGeometricDimension();
if (dimension != null)
{
yield return new Annotation(SqliteAnnotationNames.Dimension, dimension);
}
}

private static bool HasConverter(IProperty property)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,6 @@ protected override void Generate(AddColumnOperation operation, IModel model, Mig
var longTypeMapping = Dependencies.TypeMappingSource.GetMapping(typeof(long));

var srid = operation[SqliteAnnotationNames.Srid] as int? ?? 0;
var dimension = operation[SqliteAnnotationNames.Dimension] as string;

var geometryType = operation.ColumnType
?? GetColumnType(
Expand All @@ -161,10 +160,6 @@ protected override void Generate(AddColumnOperation operation, IModel model, Mig
operation.Name,
operation,
model);
if (!string.IsNullOrEmpty(dimension))
{
geometryType += dimension;
}

builder
.Append("SELECT AddGeometryColumn(")
Expand Down
3 changes: 0 additions & 3 deletions src/EFCore.Sqlite.Core/Properties/SqliteStrings.resx
Original file line number Diff line number Diff line change
Expand Up @@ -181,9 +181,6 @@
<value>A connection of an unexpected type ({type}) is being used. The SQL functions prefixed with 'ef_' could not be created automatically. Manually define them if you encounter errors while querying.</value>
<comment>Warning SqliteEventId.UnexpectedConnectionTypeWarning string</comment>
</data>
<data name="DuplicateColumnNameDimensionMismatch" xml:space="preserve">
<value>'{entityType1}.{property1}' and '{entityType2}.{property2}' are both mapped to column '{columnName}' in '{table}' but are configured with different geometric dimensions.</value>
</data>
<data name="DuplicateColumnNameSridMismatch" xml:space="preserve">
<value>'{entityType1}.{property1}' and '{entityType2}.{property2}' are both mapped to column '{columnName}' in '{table}' but are configured with different SRIDs.</value>
</data>
Expand Down

This file was deleted.

8 changes: 8 additions & 0 deletions src/EFCore.Sqlite.NTS/Properties/SqliteNTSStrings.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions src/EFCore.Sqlite.NTS/Properties/SqliteNTSStrings.resx
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,9 @@
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="InvalidGeometryType" xml:space="preserve">
<value>Invalid geometry type: {storeType}</value>
</data>
<data name="NTSServicesMissing" xml:space="preserve">
<value>UseNetTopologySuite requires AddEntityFrameworkSqliteNetTopologySuite to be called on the internal service provider used.</value>
</data>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using System.Reflection;
using System.Text;
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore.Sqlite.Internal;
using Microsoft.EntityFrameworkCore.Sqlite.Storage.ValueConversion.Internal;
using Microsoft.EntityFrameworkCore.Storage;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
Expand Down Expand Up @@ -36,7 +37,7 @@ private static readonly MethodInfo _getBytes
/// </summary>
[UsedImplicitly]
public SqliteGeometryTypeMapping([NotNull] NtsGeometryServices geometryServices, [NotNull] string storeType)
: base(new GeometryValueConverter<TGeometry>(CreateReader(geometryServices), CreateWriter()), storeType)
: base(new GeometryValueConverter<TGeometry>(CreateReader(geometryServices), CreateWriter(storeType)), storeType)
{
}

Expand Down Expand Up @@ -131,7 +132,60 @@ private static GaiaGeoReader CreateReader(NtsGeometryServices geometryServices)
geometryServices.DefaultCoordinateSequenceFactory,
geometryServices.DefaultPrecisionModel);

private static GaiaGeoWriter CreateWriter()
=> new GaiaGeoWriter { HandleOrdinates = Ordinates.XY };
private static GaiaGeoWriter CreateWriter(string storeType)
{
Ordinates handleOrdinates;
switch (storeType.ToUpperInvariant())
{
case "POINT":
case "LINESTRING":
case "POLYGON":
case "MULTIPOINT":
case "MULTILINESTRING":
case "MULTIPOLYGON":
case "GEOMETRYCOLLECTION":
case "GEOMETRY":
handleOrdinates = Ordinates.XY;
break;

case "POINTZ":
case "LINESTRINGZ":
case "POLYGONZ":
case "MULTIPOINTZ":
case "MULTILINESTRINGZ":
case "MULTIPOLYGONZ":
case "GEOMETRYCOLLECTIONZ":
case "GEOMETRYZ":
handleOrdinates = Ordinates.XYZ;
break;

case "POINTM":
case "LINESTRINGM":
case "POLYGONM":
case "MULTIPOINTM":
case "MULTILINESTRINGM":
case "MULTIPOLYGONM":
case "GEOMETRYCOLLECTIONM":
case "GEOMETRYM":
handleOrdinates = Ordinates.XYM;
break;

case "POINTZM":
case "LINESTRINGZM":
case "POLYGONZM":
case "MULTIPOINTZM":
case "MULTILINESTRINGZM":
case "MULTIPOLYGONZM":
case "GEOMETRYCOLLECTIONZM":
case "GEOMETRYZM":
handleOrdinates = Ordinates.XYZM;
break;

default:
throw new ArgumentException(SqliteNTSStrings.InvalidGeometryType(storeType), nameof(storeType));
}

return new GaiaGeoWriter { HandleOrdinates = handleOrdinates };
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,37 @@ private static readonly Dictionary<string, Type> _storeTypeMappings
= new Dictionary<string, Type>(StringComparer.OrdinalIgnoreCase)
{
{ "GEOMETRY", typeof(Geometry) },
{ "GEOMETRYZ", typeof(Geometry) },
{ "GEOMETRYM", typeof(Geometry) },
{ "GEOMETRYZM", typeof(Geometry) },
{ "GEOMETRYCOLLECTION", typeof(GeometryCollection) },
{ "GEOMETRYCOLLECTIONZ", typeof(GeometryCollection) },
{ "GEOMETRYCOLLECTIONM", typeof(GeometryCollection) },
{ "GEOMETRYCOLLECTIONZM", typeof(GeometryCollection) },
{ "LINESTRING", typeof(LineString) },
{ "LINESTRINGZ", typeof(LineString) },
{ "LINESTRINGM", typeof(LineString) },
{ "LINESTRINGZM", typeof(LineString) },
{ "MULTILINESTRING", typeof(MultiLineString) },
{ "MULTILINESTRINGZ", typeof(MultiLineString) },
{ "MULTILINESTRINGM", typeof(MultiLineString) },
{ "MULTILINESTRINGZM", typeof(MultiLineString) },
{ "MULTIPOINT", typeof(MultiPoint) },
{ "MULTIPOINTZ", typeof(MultiPoint) },
{ "MULTIPOINTM", typeof(MultiPoint) },
{ "MULTIPOINTZM", typeof(MultiPoint) },
{ "MULTIPOLYGON", typeof(MultiPolygon) },
{ "MULTIPOLYGONZ", typeof(MultiPolygon) },
{ "MULTIPOLYGONM", typeof(MultiPolygon) },
{ "MULTIPOLYGONZM", typeof(MultiPolygon) },
{ "POINT", typeof(Point) },
{ "POLYGON", typeof(Polygon) }
{ "POINTZ", typeof(Point) },
{ "POINTM", typeof(Point) },
{ "POINTZM", typeof(Point) },
{ "POLYGON", typeof(Polygon) },
{ "POLYGONZ", typeof(Polygon) },
{ "POLYGONM", typeof(Polygon) },
{ "POLYGONZM", typeof(Polygon) }
};

private readonly NtsGeometryServices _geometryServices;
Expand Down
Loading

0 comments on commit 49200d0

Please sign in to comment.