From aebc18ad92b46a1deafc2336ecaf21f8eca64363 Mon Sep 17 00:00:00 2001 From: Shay Rojansky Date: Wed, 28 Apr 2021 13:17:43 +0300 Subject: [PATCH] Add translation for Postgis <-> (DistanceKnn) (#1828) Closes #1827 --- ...qlNetTopologySuiteDbFunctionsExtensions.cs | 27 ++++++++++-- ...TopologySuiteMethodCallTranslatorPlugin.cs | 19 +++++--- .../NpgsqlFullTextSearchMethodTranslator.cs | 1 + .../NpgsqlJsonDbFunctionsTranslator.cs | 1 + .../Internal/NpgsqlLTreeTranslator.cs | 1 + .../Internal/NpgsqlNetworkTranslator.cs | 1 + .../Internal/NpgsqlRangeTranslator.cs | 1 + .../Internal/PostgresBinaryExpression.cs | 2 + .../Internal/PostgresExpressionType.cs | 41 ------------------ .../Expressions/PostgresExpressionType.cs | 43 +++++++++++++++++++ .../Query/Internal/NpgsqlQuerySqlGenerator.cs | 6 ++- .../Internal/NpgsqlSqlNullabilityProcessor.cs | 3 ++ .../Query/NpgsqlSqlExpressionFactory.cs | 15 +++++++ .../Query/SpatialQueryNpgsqlGeographyTest.cs | 31 +++++++++++-- 14 files changed, 137 insertions(+), 55 deletions(-) delete mode 100644 src/EFCore.PG/Query/Expressions/Internal/PostgresExpressionType.cs create mode 100644 src/EFCore.PG/Query/Expressions/PostgresExpressionType.cs diff --git a/src/EFCore.PG.NTS/Extensions/NpgsqlNetTopologySuiteDbFunctionsExtensions.cs b/src/EFCore.PG.NTS/Extensions/NpgsqlNetTopologySuiteDbFunctionsExtensions.cs index de6b7f49..e3064d68 100644 --- a/src/EFCore.PG.NTS/Extensions/NpgsqlNetTopologySuiteDbFunctionsExtensions.cs +++ b/src/EFCore.PG.NTS/Extensions/NpgsqlNetTopologySuiteDbFunctionsExtensions.cs @@ -9,10 +9,9 @@ public static class NpgsqlNetTopologySuiteDbFunctionsExtensions { /// /// Returns a new geometry with its coordinates transformed to a different spatial reference system. + /// Translates to ST_Transform(geometry, srid). /// /// - /// The method call is translated to ST_Transform(geometry, srid). - /// /// See https://postgis.net/docs/ST_Transform.html. /// public static TGeometry Transform(this DbFunctions _, TGeometry geometry, int srid) @@ -21,24 +20,44 @@ public static TGeometry Transform(this DbFunctions _, TGeometry geome /// /// Tests whether the distance from the origin geometry to another is less than or equal to a specified value. + /// Translates to ST_DWithin. /// + /// + /// See https://postgis.net/docs/ST_DWithin.html. + /// /// The origin geometry. /// The geometry to check the distance to. /// The distance value to compare. /// Whether to use sphere or spheroid distance measurement. - /// True if the geometries are less than distance apart. + /// if the geometries are less than distance apart. public static bool IsWithinDistance(this DbFunctions _, Geometry geometry, Geometry anotherGeometry, double distance, bool useSpheroid) => throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(IsWithinDistance))); /// /// Returns the minimum distance between the origin geometry and another geometry g. + /// Translates to ST_Distance. /// + /// + /// See https://postgis.net/docs/ST_Distance.html. + /// /// The origin geometry. /// The geometry from which to compute the distance. /// Whether to use sphere or spheroid distance measurement. /// The distance between the geometries. - /// If g is null public static double Distance(this DbFunctions _, Geometry geometry, Geometry anotherGeometry, bool useSpheroid) => throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(Distance))); + + /// + /// Returns the 2D distance between two geometries. Used in the "ORDER BY" clause, provides index-assisted nearest-neighbor result + /// sets. Translates to <->. + /// + /// + /// See https://postgis.net/docs/ST_Distance.html. + /// + /// The origin geometry. + /// The geometry from which to compute the distance. + /// The 2D distance between the geometries. + public static double DistanceKnn(this DbFunctions _, Geometry geometry, Geometry anotherGeometry) + => throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(DistanceKnn))); } } diff --git a/src/EFCore.PG.NTS/Query/ExpressionTranslators/Internal/NpgsqlNetTopologySuiteMethodCallTranslatorPlugin.cs b/src/EFCore.PG.NTS/Query/ExpressionTranslators/Internal/NpgsqlNetTopologySuiteMethodCallTranslatorPlugin.cs index e1f975ac..31dbebe8 100644 --- a/src/EFCore.PG.NTS/Query/ExpressionTranslators/Internal/NpgsqlNetTopologySuiteMethodCallTranslatorPlugin.cs +++ b/src/EFCore.PG.NTS/Query/ExpressionTranslators/Internal/NpgsqlNetTopologySuiteMethodCallTranslatorPlugin.cs @@ -9,6 +9,7 @@ using Microsoft.EntityFrameworkCore.Query.SqlExpressions; using Microsoft.EntityFrameworkCore.Storage; using NetTopologySuite.Geometries; +using Npgsql.EntityFrameworkCore.PostgreSQL.Query.Expressions; // ReSharper disable once CheckNamespace namespace Npgsql.EntityFrameworkCore.PostgreSQL.Query.ExpressionTranslators.Internal @@ -18,10 +19,13 @@ public class NpgsqlNetTopologySuiteMethodCallTranslatorPlugin : IMethodCallTrans public NpgsqlNetTopologySuiteMethodCallTranslatorPlugin( IRelationalTypeMappingSource typeMappingSource, ISqlExpressionFactory sqlExpressionFactory) - => Translators = new IMethodCallTranslator[] + { + if (!(sqlExpressionFactory is NpgsqlSqlExpressionFactory npgsqlSqlExpressionFactory)) { - new NpgsqlGeometryMethodTranslator(sqlExpressionFactory, typeMappingSource), - }; + throw new ArgumentException($"Must be an {nameof(NpgsqlSqlExpressionFactory)}", nameof(sqlExpressionFactory)); + } + Translators = new IMethodCallTranslator[] { new NpgsqlGeometryMethodTranslator(npgsqlSqlExpressionFactory, typeMappingSource), }; + } public virtual IEnumerable Translators { get; } } @@ -33,7 +37,7 @@ public class NpgsqlGeometryMethodTranslator : IMethodCallTranslator { private static readonly MethodInfo _collectionItem = typeof(GeometryCollection).GetRuntimeProperty("Item")!.GetMethod!; - private readonly ISqlExpressionFactory _sqlExpressionFactory; + private readonly NpgsqlSqlExpressionFactory _sqlExpressionFactory; private readonly IRelationalTypeMappingSource _typeMappingSource; private static readonly bool[][] TrueArrays = @@ -46,7 +50,7 @@ public class NpgsqlGeometryMethodTranslator : IMethodCallTranslator }; public NpgsqlGeometryMethodTranslator( - ISqlExpressionFactory sqlExpressionFactory, + NpgsqlSqlExpressionFactory sqlExpressionFactory, IRelationalTypeMappingSource typeMappingSource) { _sqlExpressionFactory = sqlExpressionFactory; @@ -78,6 +82,11 @@ public class NpgsqlGeometryMethodTranslator : IMethodCallTranslator method.ReturnType, arguments[1].TypeMapping), + nameof(NpgsqlNetTopologySuiteDbFunctionsExtensions.DistanceKnn) => _sqlExpressionFactory.MakePostgresBinary( + PostgresExpressionType.PostgisDistanceKnn, + arguments[1], + arguments[2]), + nameof(NpgsqlNetTopologySuiteDbFunctionsExtensions.Distance) => TranslateGeometryMethod(arguments[1], method, new[] { arguments[2], arguments[3] }), nameof(NpgsqlNetTopologySuiteDbFunctionsExtensions.IsWithinDistance) => diff --git a/src/EFCore.PG/Query/ExpressionTranslators/Internal/NpgsqlFullTextSearchMethodTranslator.cs b/src/EFCore.PG/Query/ExpressionTranslators/Internal/NpgsqlFullTextSearchMethodTranslator.cs index bbc32fba..6eccf7bd 100644 --- a/src/EFCore.PG/Query/ExpressionTranslators/Internal/NpgsqlFullTextSearchMethodTranslator.cs +++ b/src/EFCore.PG/Query/ExpressionTranslators/Internal/NpgsqlFullTextSearchMethodTranslator.cs @@ -7,6 +7,7 @@ using Microsoft.EntityFrameworkCore.Query; using Microsoft.EntityFrameworkCore.Query.SqlExpressions; using Microsoft.EntityFrameworkCore.Storage; +using Npgsql.EntityFrameworkCore.PostgreSQL.Query.Expressions; using Npgsql.EntityFrameworkCore.PostgreSQL.Query.Expressions.Internal; using NpgsqlTypes; using static Npgsql.EntityFrameworkCore.PostgreSQL.Utilities.Statics; diff --git a/src/EFCore.PG/Query/ExpressionTranslators/Internal/NpgsqlJsonDbFunctionsTranslator.cs b/src/EFCore.PG/Query/ExpressionTranslators/Internal/NpgsqlJsonDbFunctionsTranslator.cs index cf0b7d34..2833d076 100644 --- a/src/EFCore.PG/Query/ExpressionTranslators/Internal/NpgsqlJsonDbFunctionsTranslator.cs +++ b/src/EFCore.PG/Query/ExpressionTranslators/Internal/NpgsqlJsonDbFunctionsTranslator.cs @@ -8,6 +8,7 @@ using Microsoft.EntityFrameworkCore.Query; using Microsoft.EntityFrameworkCore.Query.SqlExpressions; using Microsoft.EntityFrameworkCore.Storage; +using Npgsql.EntityFrameworkCore.PostgreSQL.Query.Expressions; using Npgsql.EntityFrameworkCore.PostgreSQL.Query.Expressions.Internal; using Npgsql.EntityFrameworkCore.PostgreSQL.Storage.Internal.Mapping; using static Npgsql.EntityFrameworkCore.PostgreSQL.Utilities.Statics; diff --git a/src/EFCore.PG/Query/ExpressionTranslators/Internal/NpgsqlLTreeTranslator.cs b/src/EFCore.PG/Query/ExpressionTranslators/Internal/NpgsqlLTreeTranslator.cs index 6352dc67..3e525235 100644 --- a/src/EFCore.PG/Query/ExpressionTranslators/Internal/NpgsqlLTreeTranslator.cs +++ b/src/EFCore.PG/Query/ExpressionTranslators/Internal/NpgsqlLTreeTranslator.cs @@ -12,6 +12,7 @@ using Npgsql.EntityFrameworkCore.PostgreSQL.Query.Expressions.Internal; using Npgsql.EntityFrameworkCore.PostgreSQL.Query.Internal; using Npgsql.EntityFrameworkCore.PostgreSQL.Internal; +using Npgsql.EntityFrameworkCore.PostgreSQL.Query.Expressions; using static Npgsql.EntityFrameworkCore.PostgreSQL.Utilities.Statics; namespace Npgsql.EntityFrameworkCore.PostgreSQL.Query.ExpressionTranslators.Internal diff --git a/src/EFCore.PG/Query/ExpressionTranslators/Internal/NpgsqlNetworkTranslator.cs b/src/EFCore.PG/Query/ExpressionTranslators/Internal/NpgsqlNetworkTranslator.cs index a7d53047..25a991f3 100644 --- a/src/EFCore.PG/Query/ExpressionTranslators/Internal/NpgsqlNetworkTranslator.cs +++ b/src/EFCore.PG/Query/ExpressionTranslators/Internal/NpgsqlNetworkTranslator.cs @@ -9,6 +9,7 @@ using Microsoft.EntityFrameworkCore.Query; using Microsoft.EntityFrameworkCore.Query.SqlExpressions; using Microsoft.EntityFrameworkCore.Storage; +using Npgsql.EntityFrameworkCore.PostgreSQL.Query.Expressions; using Npgsql.EntityFrameworkCore.PostgreSQL.Query.Expressions.Internal; using static Npgsql.EntityFrameworkCore.PostgreSQL.Utilities.Statics; diff --git a/src/EFCore.PG/Query/ExpressionTranslators/Internal/NpgsqlRangeTranslator.cs b/src/EFCore.PG/Query/ExpressionTranslators/Internal/NpgsqlRangeTranslator.cs index 50c86d22..41107a92 100644 --- a/src/EFCore.PG/Query/ExpressionTranslators/Internal/NpgsqlRangeTranslator.cs +++ b/src/EFCore.PG/Query/ExpressionTranslators/Internal/NpgsqlRangeTranslator.cs @@ -7,6 +7,7 @@ using Microsoft.EntityFrameworkCore.Query; using Microsoft.EntityFrameworkCore.Query.SqlExpressions; using Microsoft.EntityFrameworkCore.Storage; +using Npgsql.EntityFrameworkCore.PostgreSQL.Query.Expressions; using Npgsql.EntityFrameworkCore.PostgreSQL.Query.Expressions.Internal; using Npgsql.EntityFrameworkCore.PostgreSQL.Storage.Internal.Mapping; using NpgsqlTypes; diff --git a/src/EFCore.PG/Query/Expressions/Internal/PostgresBinaryExpression.cs b/src/EFCore.PG/Query/Expressions/Internal/PostgresBinaryExpression.cs index b3f49453..8afb3167 100644 --- a/src/EFCore.PG/Query/Expressions/Internal/PostgresBinaryExpression.cs +++ b/src/EFCore.PG/Query/Expressions/Internal/PostgresBinaryExpression.cs @@ -127,6 +127,8 @@ protected override void Print(ExpressionPrinter expressionPrinter) PostgresExpressionType.JsonExistsAny => "?|", PostgresExpressionType.JsonExistsAll => "?&", + PostgresExpressionType.PostgisDistanceKnn => "<->", + _ => throw new ArgumentOutOfRangeException($"Unhandled operator type: {OperatorType}") }) .Append(" "); diff --git a/src/EFCore.PG/Query/Expressions/Internal/PostgresExpressionType.cs b/src/EFCore.PG/Query/Expressions/Internal/PostgresExpressionType.cs deleted file mode 100644 index ee5e9c82..00000000 --- a/src/EFCore.PG/Query/Expressions/Internal/PostgresExpressionType.cs +++ /dev/null @@ -1,41 +0,0 @@ -namespace Npgsql.EntityFrameworkCore.PostgreSQL.Query.Expressions.Internal -{ - /// - /// PostgreSQL-specific expression node types. - /// - public enum PostgresExpressionType - { - Contains, - ContainedBy, - Overlaps, - - AtTimeZone, - - NetworkContainedByOrEqual, - NetworkContainsOrEqual, - NetworkContainsOrContainedBy, - - RangeIsStrictlyLeftOf, - RangeIsStrictlyRightOf, - RangeDoesNotExtendRightOf, - RangeDoesNotExtendLeftOf, - RangeIsAdjacentTo, - RangeUnion, - RangeIntersect, - RangeExcept, - - TextSearchMatch, - TextSearchAnd, - TextSearchOr, - - JsonExists, - JsonExistsAny, - JsonExistsAll, - - LTreeMatches, - LTreeMatchesAny, - LTreeFirstAncestor, - LTreeFirstDescendent, - LTreeFirstMatches - } -} diff --git a/src/EFCore.PG/Query/Expressions/PostgresExpressionType.cs b/src/EFCore.PG/Query/Expressions/PostgresExpressionType.cs new file mode 100644 index 00000000..3f568204 --- /dev/null +++ b/src/EFCore.PG/Query/Expressions/PostgresExpressionType.cs @@ -0,0 +1,43 @@ +namespace Npgsql.EntityFrameworkCore.PostgreSQL.Query.Expressions +{ + /// + /// PostgreSQL-specific expression node types. + /// + public enum PostgresExpressionType + { + Contains, // >> (inet/cidr), @> + ContainedBy, // << (inet/cidr), <@ + Overlaps, // && + + AtTimeZone, // AT TIME ZONE + + NetworkContainedByOrEqual, // <<= + NetworkContainsOrEqual, // >>= + NetworkContainsOrContainedBy, // && + + RangeIsStrictlyLeftOf, // << + RangeIsStrictlyRightOf, // >> + RangeDoesNotExtendRightOf, // &< + RangeDoesNotExtendLeftOf, // &> + RangeIsAdjacentTo, // -|- + RangeUnion, // + + RangeIntersect, // * + RangeExcept, // - + + TextSearchMatch, // @@ + TextSearchAnd, // && + TextSearchOr, // || + + JsonExists, // ? + JsonExistsAny, // ?@> + JsonExistsAll, // ?<@ + + LTreeMatches, // ~ or @ + LTreeMatchesAny, // ? + LTreeFirstAncestor, // ?@> + LTreeFirstDescendent, // ?<@ + LTreeFirstMatches, // ?~ or ?@ + + PostgisDistanceKnn // <-> + } +} diff --git a/src/EFCore.PG/Query/Internal/NpgsqlQuerySqlGenerator.cs b/src/EFCore.PG/Query/Internal/NpgsqlQuerySqlGenerator.cs index d65f3729..1ef4d8e7 100644 --- a/src/EFCore.PG/Query/Internal/NpgsqlQuerySqlGenerator.cs +++ b/src/EFCore.PG/Query/Internal/NpgsqlQuerySqlGenerator.cs @@ -9,6 +9,7 @@ using Microsoft.EntityFrameworkCore.Query.SqlExpressions; using Microsoft.EntityFrameworkCore.Storage; using Microsoft.EntityFrameworkCore.Utilities; +using Npgsql.EntityFrameworkCore.PostgreSQL.Query.Expressions; using Npgsql.EntityFrameworkCore.PostgreSQL.Query.Expressions.Internal; using Npgsql.EntityFrameworkCore.PostgreSQL.Storage.Internal.Mapping; using NpgsqlTypes; @@ -300,8 +301,9 @@ protected virtual Expression VisitPostgresBinary(PostgresBinaryExpression binary PostgresExpressionType.LTreeFirstMatches when binaryExpression.Right.TypeMapping.StoreType == "ltxtquery" => "?@", - _ => throw new ArgumentOutOfRangeException( - $"Unhandled operator type: {binaryExpression.OperatorType}") + PostgresExpressionType.PostgisDistanceKnn => "<->", + + _ => throw new ArgumentOutOfRangeException($"Unhandled operator type: {binaryExpression.OperatorType}") }) .Append(" "); diff --git a/src/EFCore.PG/Query/Internal/NpgsqlSqlNullabilityProcessor.cs b/src/EFCore.PG/Query/Internal/NpgsqlSqlNullabilityProcessor.cs index 71c923b5..04f1fa07 100644 --- a/src/EFCore.PG/Query/Internal/NpgsqlSqlNullabilityProcessor.cs +++ b/src/EFCore.PG/Query/Internal/NpgsqlSqlNullabilityProcessor.cs @@ -3,6 +3,7 @@ using Microsoft.EntityFrameworkCore.Query; using Microsoft.EntityFrameworkCore.Query.SqlExpressions; using Microsoft.EntityFrameworkCore.Utilities; +using Npgsql.EntityFrameworkCore.PostgreSQL.Query.Expressions; using Npgsql.EntityFrameworkCore.PostgreSQL.Query.Expressions.Internal; using Npgsql.EntityFrameworkCore.PostgreSQL.Storage.Internal.Mapping; using static Npgsql.EntityFrameworkCore.PostgreSQL.Utilities.Statics; @@ -197,9 +198,11 @@ PostgresUnknownBinaryExpression postgresUnknownBinaryExpression nullable = binaryExpression.OperatorType switch { + // The following LTree search methods return null for "not found" PostgresExpressionType.LTreeFirstAncestor => true, PostgresExpressionType.LTreeFirstDescendent => true, PostgresExpressionType.LTreeFirstMatches => true, + _ => leftNullable || rightNullable }; diff --git a/src/EFCore.PG/Query/NpgsqlSqlExpressionFactory.cs b/src/EFCore.PG/Query/NpgsqlSqlExpressionFactory.cs index 2e9c83bb..3c992607 100644 --- a/src/EFCore.PG/Query/NpgsqlSqlExpressionFactory.cs +++ b/src/EFCore.PG/Query/NpgsqlSqlExpressionFactory.cs @@ -9,6 +9,7 @@ using Microsoft.EntityFrameworkCore.Query.SqlExpressions; using Microsoft.EntityFrameworkCore.Storage; using Microsoft.EntityFrameworkCore.Utilities; +using Npgsql.EntityFrameworkCore.PostgreSQL.Query.Expressions; using Npgsql.EntityFrameworkCore.PostgreSQL.Query.Expressions.Internal; using Npgsql.EntityFrameworkCore.PostgreSQL.Storage.Internal.Mapping; @@ -18,6 +19,7 @@ public class NpgsqlSqlExpressionFactory : SqlExpressionFactory { private readonly IRelationalTypeMappingSource _typeMappingSource; private readonly RelationalTypeMapping _boolTypeMapping; + private readonly RelationalTypeMapping _doubleTypeMapping; private static Type? _nodaTimeDurationType; private static Type? _nodaTimePeriodType; @@ -27,6 +29,7 @@ public NpgsqlSqlExpressionFactory(SqlExpressionFactoryDependencies dependencies) { _typeMappingSource = dependencies.TypeMappingSource; _boolTypeMapping = _typeMappingSource.FindMapping(typeof(bool))!; + _doubleTypeMapping = _typeMappingSource.FindMapping(typeof(double))!; } #region Expression factory methods @@ -216,6 +219,10 @@ RelationalTypeMapping FlipTimestampTypeMapping(RelationalTypeMapping mapping) case PostgresExpressionType.JsonExistsAll: returnType = typeof(bool); break; + + case PostgresExpressionType.PostgisDistanceKnn: + returnType = typeof(double); + break; } return (PostgresBinaryExpression)ApplyTypeMapping( @@ -508,6 +515,14 @@ private SqlExpression ApplyTypeMappingOnILike(PostgresILikeExpression ilikeExpre break; } + case PostgresExpressionType.PostgisDistanceKnn: + { + inferredTypeMapping = typeMapping ?? ExpressionExtensions.InferTypeMapping(left, right); + resultType = typeof(double); + resultTypeMapping = _doubleTypeMapping; + break; + } + default: throw new InvalidOperationException($"Incorrect {nameof(operatorType)} for {nameof(postgresBinaryExpression)}"); } diff --git a/test/EFCore.PG.FunctionalTests/Query/SpatialQueryNpgsqlGeographyTest.cs b/test/EFCore.PG.FunctionalTests/Query/SpatialQueryNpgsqlGeographyTest.cs index 6abd416f..472033c1 100644 --- a/test/EFCore.PG.FunctionalTests/Query/SpatialQueryNpgsqlGeographyTest.cs +++ b/test/EFCore.PG.FunctionalTests/Query/SpatialQueryNpgsqlGeographyTest.cs @@ -22,7 +22,7 @@ public SpatialQueryNpgsqlGeographyTest(SpatialQueryNpgsqlGeographyFixture fixtur : base(fixture) { Fixture.TestSqlLoggerFactory.Clear(); - Fixture.TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper); + // Fixture.TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper); } protected override bool AssertDistances @@ -92,7 +92,7 @@ public override async Task Centroid(bool async) [ConditionalTheory] [MemberData(nameof(IsAsyncDataAndUseSpheroid))] - public async Task DistanceDbFunction(bool async, bool useSpheroid) + public async Task Distance_with_spheroid(bool async, bool useSpheroid) { var point = Fixture.GeometryFactory.CreatePoint(new Coordinate(0, 1)); @@ -116,6 +116,31 @@ public async Task DistanceDbFunction(bool async, bool useSpheroid) FROM ""PointEntity"" AS p"); } + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public async Task DistanceKnn(bool async) + { + var point = Fixture.GeometryFactory.CreatePoint(new Coordinate(0, 1)); + + await AssertQuery( + async, + ss => ss.Set().Select(e => new { e.Id, Distance = (double?)EF.Functions.DistanceKnn(e.Point, point) }), + ss => ss.Set() + .Select(e => new { e.Id, Distance = (e.Point == null ? (double?)null : e.Point.Distance(point)) }), + elementSorter: e => e.Id, + elementAsserter: (e, a) => + { + Assert.Equal(e.Id, a.Id); + Assert.Equal(e.Distance == null, a.Distance == null); + }); + + AssertSql( + @"@__point_1='POINT (0 1)' (DbType = Object) + +SELECT p.""Id"", p.""Point"" <-> @__point_1 AS ""Distance"" +FROM ""PointEntity"" AS p"); + } + public override async Task GeometryType(bool async) { // PostGIS returns "POINT", NTS returns "Point" @@ -168,7 +193,7 @@ public override async Task IsWithinDistance(bool async) [ConditionalTheory] [MemberData(nameof(IsAsyncDataAndUseSpheroid))] - public async Task IsWithinDistanceDbFunction(bool async, bool useSpheroid) + public async Task IsWithinDistance_with_spheroid(bool async, bool useSpheroid) { var point = Fixture.GeometryFactory.CreatePoint(new Coordinate(0, 1)); -- GitLab