Skip to content

Commit

Permalink
Allow primitive collections of spatial types
Browse files Browse the repository at this point in the history
Blocked on dotnet#30677 for serializing spatial types as WKT in JSON

Closes dotnet#30630
  • Loading branch information
roji committed Apr 26, 2023
1 parent 90416e1 commit f2e1444
Show file tree
Hide file tree
Showing 6 changed files with 23 additions and 45 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,6 @@ namespace Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal;
/// </summary>
public class SqlServerNetTopologySuiteTypeMappingSourcePlugin : IRelationalTypeMappingSourcePlugin
{
private readonly HashSet<string> _spatialStoreTypes = new(StringComparer.OrdinalIgnoreCase) { "geometry", "geography" };

private readonly NtsGeometryServices _geometryServices;

/// <summary>
Expand All @@ -41,8 +39,7 @@ public SqlServerNetTopologySuiteTypeMappingSourcePlugin(NtsGeometryServices geom
var storeTypeName = mappingInfo.StoreTypeName;

return typeof(Geometry).IsAssignableFrom(clrType)
|| (storeTypeName != null
&& _spatialStoreTypes.Contains(storeTypeName))
|| storeTypeName is "geometry" or "geography"
? (RelationalTypeMapping)Activator.CreateInstance(
typeof(SqlServerGeometryTypeMapping<>).MakeGenericType(
clrType is null || clrType == typeof(SqlBytes) ? typeof(Geometry) : clrType),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -461,12 +461,6 @@ public SqlServerTypeMappingSource(
return null;
}

// Specifically exclude collections over Geometry, since there's a dedicated GeometryCollection type for that (see #30630)
if (elementClrType.Namespace == "NetTopologySuite.Geometries")
{
return null;
}

// TODO: This can be moved into a SQL Server implementation of ValueConverterSelector.. But it seems better for this method's logic
// to be in the type mapping source.
var stringMappingInfo = new RelationalTypeMappingInfo(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -201,12 +201,6 @@ public static bool IsSpatialiteType(string columnType)
return null;
}

// Specifically exclude collections over Geometry, since there's a dedicated GeometryCollection type for that (see #30630)
if (elementClrType.Namespace == "NetTopologySuite.Geometries")
{
return null;
}

stringTypeMapping = (SqliteStringTypeMapping)stringTypeMapping
.Clone(new CollectionToJsonStringConverter(mappingInfo.ClrType, elementTypeMapping));

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using NetTopologySuite.Geometries;

namespace Microsoft.EntityFrameworkCore.Query;

public abstract class NonSharedPrimitiveCollectionsQueryTestBase : NonSharedModelTestBase
Expand Down Expand Up @@ -77,11 +79,6 @@ public virtual Task Array_of_enum()

enum MyEnum { Label1, Label2 }

// This ensures that collections of Geometry (e.g. Geometry[]) aren't mapped; NTS has GeometryCollection for that.
// See SQL Server/SQLite for a sample implementation.
[ConditionalFact] // #30630
public abstract Task Array_of_geometry_is_not_supported();

[ConditionalFact]
public virtual async Task Array_of_array_is_not_supported()
{
Expand Down Expand Up @@ -215,12 +212,16 @@ public virtual async Task Inline_collection_in_query_filter()
protected async Task TestArray<TElement>(
TElement value1,
TElement value2,
Action<ModelBuilder> onModelCreating = null)
Action<ModelBuilder> onModelCreating = null,
Action<DbContextOptionsBuilder> onConfiguring = null,
Action<IServiceCollection> addServices = null)
{
var arrayClrType = typeof(TElement).MakeArrayType();

var contextFactory = await InitializeAsync<TestContext>(
onModelCreating: onModelCreating ?? (mb => mb.Entity<TestEntity>().Property(arrayClrType, "SomeArray")),
onModelCreating ?? (mb => mb.Entity<TestEntity>().Property(arrayClrType, "SomeArray")),
onConfiguring,
addServices,
seed: context =>
{
var instance1 = new TestEntity { Id = 1 };
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -208,17 +208,13 @@ WHERE CAST([s].[value] AS int) = 0) = 2
""");
}

[ConditionalFact] // #30630
public override async Task Array_of_geometry_is_not_supported()
{
var exception = await Assert.ThrowsAsync<InvalidOperationException>(
() => InitializeAsync<TestContext>(
onConfiguring: options => options.UseSqlServer(o => o.UseNetTopologySuite()),
addServices: s => s.AddEntityFrameworkSqlServerNetTopologySuite(),
onModelCreating: mb => mb.Entity<TestEntity>().Property<Point[]>("Points")));

Assert.Equal(CoreStrings.PropertyNotMapped("Point[]", "TestEntity", "Points"), exception.Message);
}
[ConditionalFact]
public virtual Task Array_of_geometry()
=> TestArray(
new Point(1.0, 2.0),
new Point(2.0, 3.0),
onConfiguring: options => options.UseSqlServer(o => o.UseNetTopologySuite()),
addServices: s => s.AddEntityFrameworkSqlServerNetTopologySuite());

#endregion Support for specific element types

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -200,17 +200,13 @@ LIMIT 2
""");
}

[ConditionalFact] // #30630
public override async Task Array_of_geometry_is_not_supported()
{
var exception = await Assert.ThrowsAsync<InvalidOperationException>(
() => InitializeAsync<TestContext>(
onConfiguring: options => options.UseSqlite(o => o.UseNetTopologySuite()),
addServices: s => s.AddEntityFrameworkSqliteNetTopologySuite(),
onModelCreating: mb => mb.Entity<TestEntity>().Property<Point[]>("Points")));

Assert.Equal(CoreStrings.PropertyNotMapped("Point[]", "TestEntity", "Points"), exception.Message);
}
[ConditionalFact]
public virtual Task Array_of_geometry()
=> TestArray(
new Point(1.0, 2.0),
new Point(2.0, 3.0),
onConfiguring: options => options.UseSqlite(o => o.UseNetTopologySuite()),
addServices: s => s.AddEntityFrameworkSqliteNetTopologySuite());

#endregion Support for specific element types

Expand Down

0 comments on commit f2e1444

Please # to comment.