Родитель
de44d56423
Коммит
dc0f1b01d3
|
@ -64,6 +64,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EFCore.Analyzers", "src\EFC
|
|||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EFCore.SqlServer.NTS", "src\EFCore.SqlServer.NTS\EFCore.SqlServer.NTS.csproj", "{F53EB45F-84D7-4520-B813-8916C0C756BB}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EFCore.Sqlite.NTS", "src\EFCore.Sqlite.NTS\EFCore.Sqlite.NTS.csproj", "{1747A095-A464-4445-9F39-A80531FB24FD}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
|
@ -158,6 +160,10 @@ Global
|
|||
{F53EB45F-84D7-4520-B813-8916C0C756BB}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{F53EB45F-84D7-4520-B813-8916C0C756BB}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{F53EB45F-84D7-4520-B813-8916C0C756BB}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{1747A095-A464-4445-9F39-A80531FB24FD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{1747A095-A464-4445-9F39-A80531FB24FD}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{1747A095-A464-4445-9F39-A80531FB24FD}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{1747A095-A464-4445-9F39-A80531FB24FD}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
@ -185,6 +191,7 @@ Global
|
|||
{313F46FE-9962-4A15-805F-FCBDF5A6181E} = {258D5057-81B9-40EC-A872-D21E27452749}
|
||||
{948D3EDA-ECF0-4367-B157-BF770F752A8B} = {CE6B50B2-34AE-44C9-940A-4E48C3E1B3BC}
|
||||
{F53EB45F-84D7-4520-B813-8916C0C756BB} = {CE6B50B2-34AE-44C9-940A-4E48C3E1B3BC}
|
||||
{1747A095-A464-4445-9F39-A80531FB24FD} = {CE6B50B2-34AE-44C9-940A-4E48C3E1B3BC}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {EC8BCF1F-A206-4420-A292-3E3F2A4CDC54}
|
||||
|
|
|
@ -99,6 +99,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EFCore.Cosmos.Sql.Tests", "
|
|||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EFCore.SqlServer.NTS", "src\EFCore.SqlServer.NTS\EFCore.SqlServer.NTS.csproj", "{F53EB45F-84D7-4520-B813-8916C0C756BB}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EFCore.Sqlite.NTS", "src\EFCore.Sqlite.NTS\EFCore.Sqlite.NTS.csproj", "{1747A095-A464-4445-9F39-A80531FB24FD}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
|
@ -245,6 +247,10 @@ Global
|
|||
{F53EB45F-84D7-4520-B813-8916C0C756BB}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{F53EB45F-84D7-4520-B813-8916C0C756BB}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{F53EB45F-84D7-4520-B813-8916C0C756BB}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{1747A095-A464-4445-9F39-A80531FB24FD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{1747A095-A464-4445-9F39-A80531FB24FD}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{1747A095-A464-4445-9F39-A80531FB24FD}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{1747A095-A464-4445-9F39-A80531FB24FD}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
@ -288,6 +294,7 @@ Global
|
|||
{76EB1FAD-00F9-4B92-B954-073AF288D3DD} = {258D5057-81B9-40EC-A872-D21E27452749}
|
||||
{B4E155E5-C0B8-4680-92A0-A0DE745486B1} = {258D5057-81B9-40EC-A872-D21E27452749}
|
||||
{F53EB45F-84D7-4520-B813-8916C0C756BB} = {CE6B50B2-34AE-44C9-940A-4E48C3E1B3BC}
|
||||
{1747A095-A464-4445-9F39-A80531FB24FD} = {CE6B50B2-34AE-44C9-940A-4E48C3E1B3BC}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {285A5EB4-BCF4-40EB-B9E1-DF6DBCB5E705}
|
||||
|
|
|
@ -39,6 +39,8 @@
|
|||
<XunitAssertPackageVersion>2.3.1</XunitAssertPackageVersion>
|
||||
<XunitCorePackageVersion>2.3.1</XunitCorePackageVersion>
|
||||
<XunitRunnerVisualStudioPackageVersion>2.4.0</XunitRunnerVisualStudioPackageVersion>
|
||||
<NetTopologySuiteIOSpatiaLitePackageVersion>1.15.0</NetTopologySuiteIOSpatiaLitePackageVersion>
|
||||
<mod_spatialitePackageVersion>4.3.0.1</mod_spatialitePackageVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(DotNetPackageVersionPropsPath)" Condition=" '$(DotNetPackageVersionPropsPath)' != '' " />
|
||||
<PropertyGroup Label="Package Versions: Pinned" />
|
||||
|
|
|
@ -6,6 +6,7 @@ using System.Threading.Tasks;
|
|||
using GeoAPI.Geometries;
|
||||
using Microsoft.EntityFrameworkCore.TestModels.SpatialModel;
|
||||
using Microsoft.EntityFrameworkCore.TestUtilities;
|
||||
using Microsoft.EntityFrameworkCore.TestUtilities.Xunit;
|
||||
using NetTopologySuite.Geometries;
|
||||
using Xunit;
|
||||
|
||||
|
@ -20,14 +21,14 @@ namespace Microsoft.EntityFrameworkCore.Query
|
|||
{
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[ConditionalTheory]
|
||||
[MemberData(nameof(IsAsyncData))]
|
||||
public virtual Task Area(bool isAsync)
|
||||
{
|
||||
return AssertQuery<PolygonEntity>(isAsync, es => es.Select(e => new { e.Id, e.Polygon.Area }));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[ConditionalTheory]
|
||||
[MemberData(nameof(IsAsyncData))]
|
||||
public virtual Task AsBinary(bool isAsync)
|
||||
{
|
||||
|
@ -41,7 +42,7 @@ namespace Microsoft.EntityFrameworkCore.Query
|
|||
});
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[ConditionalTheory]
|
||||
[MemberData(nameof(IsAsyncData))]
|
||||
public virtual Task AsText(bool isAsync)
|
||||
{
|
||||
|
@ -55,7 +56,7 @@ namespace Microsoft.EntityFrameworkCore.Query
|
|||
});
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[ConditionalTheory]
|
||||
[MemberData(nameof(IsAsyncData))]
|
||||
public virtual Task Boundary(bool isAsync)
|
||||
{
|
||||
|
@ -69,7 +70,7 @@ namespace Microsoft.EntityFrameworkCore.Query
|
|||
});
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[ConditionalTheory]
|
||||
[MemberData(nameof(IsAsyncData))]
|
||||
public virtual Task Buffer(bool isAsync)
|
||||
{
|
||||
|
@ -83,7 +84,7 @@ namespace Microsoft.EntityFrameworkCore.Query
|
|||
});
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[ConditionalTheory]
|
||||
[MemberData(nameof(IsAsyncData))]
|
||||
public virtual async Task Buffer_quadrantSegments(bool isAsync)
|
||||
{
|
||||
|
@ -97,7 +98,7 @@ namespace Microsoft.EntityFrameworkCore.Query
|
|||
});
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[ConditionalTheory]
|
||||
[MemberData(nameof(IsAsyncData))]
|
||||
public virtual Task Centroid(bool isAsync)
|
||||
{
|
||||
|
@ -111,7 +112,7 @@ namespace Microsoft.EntityFrameworkCore.Query
|
|||
});
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[ConditionalTheory]
|
||||
[MemberData(nameof(IsAsyncData))]
|
||||
public virtual Task Contains(bool isAsync)
|
||||
{
|
||||
|
@ -120,7 +121,7 @@ namespace Microsoft.EntityFrameworkCore.Query
|
|||
es => es.Select(e => new { e.Id, Contains = e.Polygon.Contains(new Point(0.5, 0.25)) }));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[ConditionalTheory]
|
||||
[MemberData(nameof(IsAsyncData))]
|
||||
public virtual Task ConvexHull(bool isAsync)
|
||||
{
|
||||
|
@ -134,7 +135,7 @@ namespace Microsoft.EntityFrameworkCore.Query
|
|||
});
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[ConditionalTheory]
|
||||
[MemberData(nameof(IsAsyncData))]
|
||||
public virtual Task IGeometryCollection_Count(bool isAsync)
|
||||
{
|
||||
|
@ -143,7 +144,7 @@ namespace Microsoft.EntityFrameworkCore.Query
|
|||
es => es.Select(e => new { e.Id, e.MultiLineString.Count }));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[ConditionalTheory]
|
||||
[MemberData(nameof(IsAsyncData))]
|
||||
public virtual Task LineString_Count(bool isAsync)
|
||||
{
|
||||
|
@ -152,7 +153,7 @@ namespace Microsoft.EntityFrameworkCore.Query
|
|||
es => es.Select(e => new { e.Id, ((LineString)e.LineString).Count }));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[ConditionalTheory]
|
||||
[MemberData(nameof(IsAsyncData))]
|
||||
public virtual async Task CoveredBy(bool isAsync)
|
||||
{
|
||||
|
@ -176,7 +177,7 @@ namespace Microsoft.EntityFrameworkCore.Query
|
|||
}));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[ConditionalTheory]
|
||||
[MemberData(nameof(IsAsyncData))]
|
||||
public virtual async Task Covers(bool isAsync)
|
||||
{
|
||||
|
@ -185,7 +186,7 @@ namespace Microsoft.EntityFrameworkCore.Query
|
|||
es => es.Select(e => new { e.Id, Covers = e.Polygon.Covers(new Point(0.5, 0.25)) }));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[ConditionalTheory]
|
||||
[MemberData(nameof(IsAsyncData))]
|
||||
public virtual Task Crosses(bool isAsync)
|
||||
{
|
||||
|
@ -205,7 +206,7 @@ namespace Microsoft.EntityFrameworkCore.Query
|
|||
}));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[ConditionalTheory]
|
||||
[MemberData(nameof(IsAsyncData))]
|
||||
public virtual Task Difference(bool isAsync)
|
||||
{
|
||||
|
@ -233,14 +234,14 @@ namespace Microsoft.EntityFrameworkCore.Query
|
|||
});
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[ConditionalTheory]
|
||||
[MemberData(nameof(IsAsyncData))]
|
||||
public virtual Task Dimension(bool isAsync)
|
||||
{
|
||||
return AssertQuery<PointEntity>(isAsync, es => es.Select(e => new { e.Id, e.Point.Dimension }));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[ConditionalTheory]
|
||||
[MemberData(nameof(IsAsyncData))]
|
||||
public virtual Task Disjoint(bool isAsync)
|
||||
{
|
||||
|
@ -249,7 +250,7 @@ namespace Microsoft.EntityFrameworkCore.Query
|
|||
es => es.Select(e => new { e.Id, Disjoint = e.Polygon.Disjoint(new Point(1, 0)) }));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[ConditionalTheory]
|
||||
[MemberData(nameof(IsAsyncData))]
|
||||
public virtual Task Distance(bool isAsync)
|
||||
{
|
||||
|
@ -258,14 +259,14 @@ namespace Microsoft.EntityFrameworkCore.Query
|
|||
es => es.Select(e => new { e.Id, Distance = e.Point.Distance(new Point(0, 1)) }));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[ConditionalTheory]
|
||||
[MemberData(nameof(IsAsyncData))]
|
||||
public virtual Task EndPoint(bool isAsync)
|
||||
{
|
||||
return AssertQuery<LineStringEntity>(isAsync, es => es.Select(e => new { e.Id, e.LineString.EndPoint }));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[ConditionalTheory]
|
||||
[MemberData(nameof(IsAsyncData))]
|
||||
public virtual Task Envelope(bool isAsync)
|
||||
{
|
||||
|
@ -279,7 +280,7 @@ namespace Microsoft.EntityFrameworkCore.Query
|
|||
});
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[ConditionalTheory]
|
||||
[MemberData(nameof(IsAsyncData))]
|
||||
public virtual Task EqualsTopologically(bool isAsync)
|
||||
{
|
||||
|
@ -288,21 +289,21 @@ namespace Microsoft.EntityFrameworkCore.Query
|
|||
es => es.Select(e => new { e.Id, EqualsTopologically = e.Point.EqualsTopologically(new Point(0, 0)) }));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[ConditionalTheory]
|
||||
[MemberData(nameof(IsAsyncData))]
|
||||
public virtual Task ExteriorRing(bool isAsync)
|
||||
{
|
||||
return AssertQuery<PolygonEntity>(isAsync, es => es.Select(e => new { e.Id, e.Polygon.ExteriorRing }));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[ConditionalTheory]
|
||||
[MemberData(nameof(IsAsyncData))]
|
||||
public virtual Task GeometryType(bool isAsync)
|
||||
{
|
||||
return AssertQuery<PointEntity>(isAsync, es => es.Select(e => new { e.Id, e.Point.GeometryType }));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[ConditionalTheory]
|
||||
[MemberData(nameof(IsAsyncData))]
|
||||
public virtual Task GetGeometryN(bool isAsync)
|
||||
{
|
||||
|
@ -311,7 +312,7 @@ namespace Microsoft.EntityFrameworkCore.Query
|
|||
es => es.Select(e => new { e.Id, Geometry0 = e.MultiLineString.GetGeometryN(0) }));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[ConditionalTheory]
|
||||
[MemberData(nameof(IsAsyncData))]
|
||||
public virtual Task GetInteriorRingN(bool isAsync)
|
||||
{
|
||||
|
@ -323,7 +324,7 @@ namespace Microsoft.EntityFrameworkCore.Query
|
|||
select new { e.Id, InteriorRing0 = e.Polygon.GetInteriorRingN(0) });
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[ConditionalTheory]
|
||||
[MemberData(nameof(IsAsyncData))]
|
||||
public virtual Task GetPointN(bool isAsync)
|
||||
{
|
||||
|
@ -332,7 +333,7 @@ namespace Microsoft.EntityFrameworkCore.Query
|
|||
es => es.Select(e => new { e.Id, Point0 = e.LineString.GetPointN(0) }));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[ConditionalTheory]
|
||||
[MemberData(nameof(IsAsyncData))]
|
||||
public virtual Task Intersection(bool isAsync)
|
||||
{
|
||||
|
@ -360,7 +361,7 @@ namespace Microsoft.EntityFrameworkCore.Query
|
|||
});
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[ConditionalTheory]
|
||||
[MemberData(nameof(IsAsyncData))]
|
||||
public virtual Task Intersects(bool isAsync)
|
||||
{
|
||||
|
@ -380,14 +381,14 @@ namespace Microsoft.EntityFrameworkCore.Query
|
|||
}));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[ConditionalTheory]
|
||||
[MemberData(nameof(IsAsyncData))]
|
||||
public virtual Task ICurve_IsClosed(bool isAsync)
|
||||
{
|
||||
return AssertQuery<LineStringEntity>(isAsync, es => es.Select(e => new { e.Id, e.LineString.IsClosed }));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[ConditionalTheory]
|
||||
[MemberData(nameof(IsAsyncData))]
|
||||
public virtual Task IMultiCurve_IsClosed(bool isAsync)
|
||||
{
|
||||
|
@ -396,7 +397,7 @@ namespace Microsoft.EntityFrameworkCore.Query
|
|||
es => es.Select(e => new { e.Id, e.MultiLineString.IsClosed }));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[ConditionalTheory]
|
||||
[MemberData(nameof(IsAsyncData))]
|
||||
public virtual Task IsEmpty(bool isAsync)
|
||||
{
|
||||
|
@ -405,28 +406,28 @@ namespace Microsoft.EntityFrameworkCore.Query
|
|||
es => es.Select(e => new { e.Id, e.MultiLineString.IsEmpty }));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[ConditionalTheory]
|
||||
[MemberData(nameof(IsAsyncData))]
|
||||
public virtual Task IsRing(bool isAsync)
|
||||
{
|
||||
return AssertQuery<LineStringEntity>(isAsync, es => es.Select(e => new { e.Id, e.LineString.IsRing }));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[ConditionalTheory]
|
||||
[MemberData(nameof(IsAsyncData))]
|
||||
public virtual Task IsSimple(bool isAsync)
|
||||
{
|
||||
return AssertQuery<LineStringEntity>(isAsync, es => es.Select(e => new { e.Id, e.LineString.IsSimple }));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[ConditionalTheory]
|
||||
[MemberData(nameof(IsAsyncData))]
|
||||
public virtual Task IsValid(bool isAsync)
|
||||
{
|
||||
return AssertQuery<PointEntity>(isAsync, es => es.Select(e => new { e.Id, e.Point.IsValid }));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[ConditionalTheory]
|
||||
[MemberData(nameof(IsAsyncData))]
|
||||
public virtual Task Item(bool isAsync)
|
||||
{
|
||||
|
@ -435,14 +436,14 @@ namespace Microsoft.EntityFrameworkCore.Query
|
|||
es => es.Select(e => new { e.Id, Item0 = e.MultiLineString[0] }));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[ConditionalTheory]
|
||||
[MemberData(nameof(IsAsyncData))]
|
||||
public virtual Task Length(bool isAsync)
|
||||
{
|
||||
return AssertQuery<LineStringEntity>(isAsync, es => es.Select(e => new { e.Id, e.LineString.Length }));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[ConditionalTheory]
|
||||
[MemberData(nameof(IsAsyncData))]
|
||||
public virtual Task M(bool isAsync)
|
||||
{
|
||||
|
@ -456,7 +457,7 @@ namespace Microsoft.EntityFrameworkCore.Query
|
|||
});
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[ConditionalTheory]
|
||||
[MemberData(nameof(IsAsyncData))]
|
||||
public virtual Task NumGeometries(bool isAsync)
|
||||
{
|
||||
|
@ -465,7 +466,7 @@ namespace Microsoft.EntityFrameworkCore.Query
|
|||
es => es.Select(e => new { e.Id, e.MultiLineString.NumGeometries }));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[ConditionalTheory]
|
||||
[MemberData(nameof(IsAsyncData))]
|
||||
public virtual Task NumInteriorRings(bool isAsync)
|
||||
{
|
||||
|
@ -474,14 +475,14 @@ namespace Microsoft.EntityFrameworkCore.Query
|
|||
es => es.Select(e => new { e.Id, e.Polygon.NumInteriorRings }));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[ConditionalTheory]
|
||||
[MemberData(nameof(IsAsyncData))]
|
||||
public virtual Task NumPoints(bool isAsync)
|
||||
{
|
||||
return AssertQuery<LineStringEntity>(isAsync, es => es.Select(e => new { e.Id, e.LineString.NumPoints }));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[ConditionalTheory]
|
||||
[MemberData(nameof(IsAsyncData))]
|
||||
public virtual Task Overlaps(bool isAsync)
|
||||
{
|
||||
|
@ -504,7 +505,7 @@ namespace Microsoft.EntityFrameworkCore.Query
|
|||
}));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[ConditionalTheory]
|
||||
[MemberData(nameof(IsAsyncData))]
|
||||
public virtual Task PointOnSurface(bool isAsync)
|
||||
{
|
||||
|
@ -518,7 +519,7 @@ namespace Microsoft.EntityFrameworkCore.Query
|
|||
});
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[ConditionalTheory]
|
||||
[MemberData(nameof(IsAsyncData))]
|
||||
public virtual Task Relate(bool isAsync)
|
||||
{
|
||||
|
@ -542,7 +543,7 @@ namespace Microsoft.EntityFrameworkCore.Query
|
|||
}));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[ConditionalTheory]
|
||||
[MemberData(nameof(IsAsyncData))]
|
||||
public virtual async Task Reverse(bool isAsync)
|
||||
{
|
||||
|
@ -551,21 +552,21 @@ namespace Microsoft.EntityFrameworkCore.Query
|
|||
es => es.Select(e => new { e.Id, Reverse = e.LineString.Reverse() }));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[ConditionalTheory]
|
||||
[MemberData(nameof(IsAsyncData))]
|
||||
public virtual Task SRID(bool isAsync)
|
||||
{
|
||||
return AssertQuery<PointEntity>(isAsync, es => es.Select(e => new { e.Id, e.Point.SRID }));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[ConditionalTheory]
|
||||
[MemberData(nameof(IsAsyncData))]
|
||||
public virtual Task StartPoint(bool isAsync)
|
||||
{
|
||||
return AssertQuery<LineStringEntity>(isAsync, es => es.Select(e => new { e.Id, e.LineString.StartPoint }));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[ConditionalTheory]
|
||||
[MemberData(nameof(IsAsyncData))]
|
||||
public virtual Task SymmetricDifference(bool isAsync)
|
||||
{
|
||||
|
@ -596,7 +597,7 @@ namespace Microsoft.EntityFrameworkCore.Query
|
|||
});
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[ConditionalTheory]
|
||||
[MemberData(nameof(IsAsyncData))]
|
||||
public virtual Task ToBinary(bool isAsync)
|
||||
{
|
||||
|
@ -610,7 +611,7 @@ namespace Microsoft.EntityFrameworkCore.Query
|
|||
});
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[ConditionalTheory]
|
||||
[MemberData(nameof(IsAsyncData))]
|
||||
public virtual Task ToText(bool isAsync)
|
||||
{
|
||||
|
@ -624,7 +625,7 @@ namespace Microsoft.EntityFrameworkCore.Query
|
|||
});
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[ConditionalTheory]
|
||||
[MemberData(nameof(IsAsyncData))]
|
||||
public virtual Task Touches(bool isAsync)
|
||||
{
|
||||
|
@ -647,7 +648,7 @@ namespace Microsoft.EntityFrameworkCore.Query
|
|||
}));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[ConditionalTheory]
|
||||
[MemberData(nameof(IsAsyncData))]
|
||||
public virtual Task Union(bool isAsync)
|
||||
{
|
||||
|
@ -675,7 +676,7 @@ namespace Microsoft.EntityFrameworkCore.Query
|
|||
});
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[ConditionalTheory]
|
||||
[MemberData(nameof(IsAsyncData))]
|
||||
public virtual Task Within(bool isAsync)
|
||||
{
|
||||
|
@ -699,21 +700,21 @@ namespace Microsoft.EntityFrameworkCore.Query
|
|||
}));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[ConditionalTheory]
|
||||
[MemberData(nameof(IsAsyncData))]
|
||||
public virtual Task X(bool isAsync)
|
||||
{
|
||||
return AssertQuery<PointEntity>(isAsync, es => es.Select(e => new { e.Id, e.Point.X }));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[ConditionalTheory]
|
||||
[MemberData(nameof(IsAsyncData))]
|
||||
public virtual Task Y(bool isAsync)
|
||||
{
|
||||
return AssertQuery<PointEntity>(isAsync, es => es.Select(e => new { e.Id, e.Point.Y }));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[ConditionalTheory]
|
||||
[MemberData(nameof(IsAsyncData))]
|
||||
public virtual Task Z(bool isAsync)
|
||||
{
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using Microsoft.EntityFrameworkCore.TestModels.SpatialModel;
|
||||
using Microsoft.EntityFrameworkCore.TestUtilities.Xunit;
|
||||
using NetTopologySuite.Geometries;
|
||||
using Xunit;
|
||||
|
||||
|
@ -16,7 +17,7 @@ namespace Microsoft.EntityFrameworkCore
|
|||
|
||||
protected virtual TFixture Fixture { get; }
|
||||
|
||||
[Fact]
|
||||
[ConditionalFact]
|
||||
public virtual void Values_are_copied_into_change_tracker()
|
||||
{
|
||||
using (var db = Fixture.CreateContext())
|
||||
|
@ -34,7 +35,7 @@ namespace Microsoft.EntityFrameworkCore
|
|||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[ConditionalFact]
|
||||
public virtual void Values_arent_compared_by_reference()
|
||||
{
|
||||
using (var db = Fixture.CreateContext())
|
||||
|
|
|
@ -61,7 +61,7 @@ namespace Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal
|
|||
/// </summary>
|
||||
protected override string GenerateNonNullSqlLiteral(object value)
|
||||
{
|
||||
// TODO: Can we avoid the conversion in the first place? Should we just inline the BLOB?
|
||||
// TODO: Avoid converting in the first place
|
||||
var geometry = _reader.Read(((SqlBytes)value).Value);
|
||||
var srid = geometry.SRID;
|
||||
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Data.Sqlite.Core" Version="$(MicrosoftDataSqliteCorePackageVersion)" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyModel" Version="$(MicrosoftExtensionsDependencyModelPackageVersion)" />
|
||||
<PackageReference Include="StyleCop.Analyzers" Version="$(StyleCopAnalyzersPackageVersion)" PrivateAssets="All" />
|
||||
</ItemGroup>
|
||||
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using JetBrains.Annotations;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Utilities;
|
||||
|
||||
namespace Microsoft.EntityFrameworkCore
|
||||
{
|
||||
/// <summary>
|
||||
/// SQLite specific extension methods for metadata.
|
||||
/// </summary>
|
||||
public static class SqliteMetadataExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the SQLite specific metadata for a property.
|
||||
/// </summary>
|
||||
/// <param name="property"> The property to get metadata for. </param>
|
||||
/// <returns> The SQLite specific metadata for the property. </returns>
|
||||
public static SqlitePropertyAnnotations Sqlite([NotNull] this IMutableProperty property)
|
||||
=> (SqlitePropertyAnnotations)Sqlite((IProperty)property);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the SQLite specific metadata for a property.
|
||||
/// </summary>
|
||||
/// <param name="property"> The property to get metadata for. </param>
|
||||
/// <returns> The SQLite specific metadata for the property. </returns>
|
||||
public static ISqlitePropertyAnnotations Sqlite([NotNull] this IProperty property)
|
||||
=> new SqlitePropertyAnnotations(Check.NotNull(property, nameof(property)));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using JetBrains.Annotations;
|
||||
using Microsoft.EntityFrameworkCore.Metadata.Builders;
|
||||
using Microsoft.EntityFrameworkCore.Utilities;
|
||||
|
||||
namespace Microsoft.EntityFrameworkCore
|
||||
{
|
||||
/// <summary>
|
||||
/// SQLite specific extension methods for <see cref="PropertyBuilder" />.
|
||||
/// </summary>
|
||||
public static class SqlitePropertyBuilderExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Configures the SRID 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="srid"> The SRID. </param>
|
||||
/// <returns> The same builder instance so that multiple calls can be chained. </returns>
|
||||
public static PropertyBuilder ForSqliteHasSrid([NotNull] this PropertyBuilder propertyBuilder, int srid)
|
||||
{
|
||||
Check.NotNull(propertyBuilder, nameof(propertyBuilder));
|
||||
|
||||
propertyBuilder.Metadata.Sqlite().Srid = srid;
|
||||
|
||||
return propertyBuilder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Configures the SRID 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="srid"> The SRID. </param>
|
||||
/// <returns> The same builder instance so that multiple calls can be chained. </returns>
|
||||
public static PropertyBuilder<TProperty> ForSqliteHasSrid<TProperty>(
|
||||
[NotNull] this PropertyBuilder<TProperty> propertyBuilder,
|
||||
int srid)
|
||||
=> (PropertyBuilder<TProperty>)ForSqliteHasSrid((PropertyBuilder)propertyBuilder, srid);
|
||||
}
|
||||
}
|
|
@ -17,6 +17,7 @@ namespace Microsoft.EntityFrameworkCore.Sqlite.Infrastructure.Internal
|
|||
public class SqliteOptionsExtension : RelationalOptionsExtension, IDbContextOptionsExtensionWithDebugInfo
|
||||
{
|
||||
private bool _enforceForeignKeys = true;
|
||||
private bool _loadSpatialite;
|
||||
private string _logFragment;
|
||||
|
||||
/// <summary>
|
||||
|
@ -37,6 +38,7 @@ namespace Microsoft.EntityFrameworkCore.Sqlite.Infrastructure.Internal
|
|||
: base(copyFrom)
|
||||
{
|
||||
_enforceForeignKeys = copyFrom._enforceForeignKeys;
|
||||
_loadSpatialite = copyFrom._loadSpatialite;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -52,6 +54,12 @@ namespace Microsoft.EntityFrameworkCore.Sqlite.Infrastructure.Internal
|
|||
/// </summary>
|
||||
public virtual bool EnforceForeignKeys => _enforceForeignKeys;
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public virtual bool LoadSpatialite => _loadSpatialite;
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
|
@ -65,6 +73,19 @@ namespace Microsoft.EntityFrameworkCore.Sqlite.Infrastructure.Internal
|
|||
return clone;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public virtual SqliteOptionsExtension WithLoadSpatialite(bool loadSpatialite)
|
||||
{
|
||||
var clone = (SqliteOptionsExtension)Clone();
|
||||
|
||||
clone._loadSpatialite = loadSpatialite;
|
||||
|
||||
return clone;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
|
@ -106,6 +127,11 @@ namespace Microsoft.EntityFrameworkCore.Sqlite.Infrastructure.Internal
|
|||
builder.Append("SuppressForeignKeyEnforcement ");
|
||||
}
|
||||
|
||||
if (_loadSpatialite)
|
||||
{
|
||||
builder.Append("LoadSpatialite ");
|
||||
}
|
||||
|
||||
_logFragment = builder.ToString();
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,165 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data.Common;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using JetBrains.Annotations;
|
||||
using Microsoft.Data.Sqlite;
|
||||
using Microsoft.EntityFrameworkCore.Utilities;
|
||||
using Microsoft.Extensions.DependencyModel;
|
||||
using RuntimeEnvironment = Microsoft.DotNet.PlatformAbstractions.RuntimeEnvironment;
|
||||
|
||||
namespace Microsoft.EntityFrameworkCore.Infrastructure
|
||||
{
|
||||
/// <summary>
|
||||
/// Finds and loads SpatiaLite.
|
||||
/// </summary>
|
||||
public static class SpatialiteLoader
|
||||
{
|
||||
private static readonly string _sharedLibraryExtension;
|
||||
private static readonly string _pathVariableName;
|
||||
|
||||
private static string _extension;
|
||||
|
||||
static SpatialiteLoader()
|
||||
{
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||
{
|
||||
_sharedLibraryExtension = ".dll";
|
||||
_pathVariableName = "PATH";
|
||||
}
|
||||
else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
|
||||
{
|
||||
_sharedLibraryExtension = ".dylib";
|
||||
_pathVariableName = "DYLD_LIBRARY_PATH";
|
||||
}
|
||||
else
|
||||
{
|
||||
_sharedLibraryExtension = ".so";
|
||||
_pathVariableName = "LD_LIBRARY_PATH";
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to load the mod_spatialite extension into the specified connection.
|
||||
/// </summary>
|
||||
/// <param name="connection"> The connection. </param>
|
||||
/// <returns> true if the extension was loaded; otherwise, false. </returns>
|
||||
public static bool TryLoad([NotNull] DbConnection connection)
|
||||
{
|
||||
try
|
||||
{
|
||||
Load(connection);
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (SqliteException ex) when (ex.SqliteErrorCode == 1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <para>
|
||||
/// Loads the mod_spatialite extension into the specified connection.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// The the extension will be loaded from native NuGet assets when available.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
/// <param name="connection"> The connection. </param>
|
||||
public static void Load([NotNull] DbConnection connection)
|
||||
{
|
||||
Check.NotNull(connection, nameof(connection));
|
||||
|
||||
var extension = FindExtension();
|
||||
|
||||
using (var command = connection.CreateCommand())
|
||||
{
|
||||
command.CommandText = "SELECT load_extension('" + extension + "');";
|
||||
command.ExecuteNonQuery();
|
||||
}
|
||||
}
|
||||
|
||||
private static string FindExtension()
|
||||
{
|
||||
if (_extension != null)
|
||||
{
|
||||
return _extension;
|
||||
}
|
||||
|
||||
var candidateAssets = new Dictionary<string, int>();
|
||||
var rid = RuntimeEnvironment.GetRuntimeIdentifier();
|
||||
var rids = DependencyContext.Default.RuntimeGraph.First(g => g.Runtime == rid).Fallbacks.ToList();
|
||||
rids.Insert(0, rid);
|
||||
|
||||
foreach (var library in DependencyContext.Default.RuntimeLibraries)
|
||||
{
|
||||
foreach (var group in library.NativeLibraryGroups)
|
||||
{
|
||||
foreach (var file in group.RuntimeFiles)
|
||||
{
|
||||
if (string.Equals(
|
||||
Path.GetFileName(file.Path),
|
||||
"mod_spatialite" + _sharedLibraryExtension,
|
||||
StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
var fallbacks = rids.IndexOf(group.Runtime);
|
||||
if (fallbacks != -1)
|
||||
{
|
||||
candidateAssets.Add(library.Path + "/" + file.Path, fallbacks);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var assetPath = candidateAssets.OrderBy(p => p.Value)
|
||||
.Select(p => p.Key.Replace('/', Path.DirectorySeparatorChar)).FirstOrDefault();
|
||||
if (assetPath != null)
|
||||
{
|
||||
string assetFullPath = null;
|
||||
var probingDirectories = ((string)AppDomain.CurrentDomain.GetData("PROBING_DIRECTORIES"))
|
||||
.Split(Path.PathSeparator);
|
||||
foreach (var directory in probingDirectories)
|
||||
{
|
||||
var candidateFullPath = Path.Combine(directory, assetPath);
|
||||
if (File.Exists(candidateFullPath))
|
||||
{
|
||||
assetFullPath = candidateFullPath;
|
||||
}
|
||||
}
|
||||
Debug.Assert(assetFullPath != null);
|
||||
|
||||
var assetDirectory = Path.GetDirectoryName(assetFullPath);
|
||||
|
||||
var currentPath = Environment.GetEnvironmentVariable(_pathVariableName);
|
||||
if (!currentPath.Split(Path.PathSeparator).Any(
|
||||
p => string.Equals(
|
||||
p.TrimEnd(Path.DirectorySeparatorChar),
|
||||
assetDirectory,
|
||||
StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
Environment.SetEnvironmentVariable(
|
||||
_pathVariableName,
|
||||
assetDirectory + Path.PathSeparator + currentPath);
|
||||
}
|
||||
}
|
||||
|
||||
var extension = "mod_spatialite";
|
||||
|
||||
// Workaround ericsink/SQLitePCL.raw#225
|
||||
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||
{
|
||||
extension += _sharedLibraryExtension;
|
||||
}
|
||||
|
||||
return _extension = extension;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
namespace Microsoft.EntityFrameworkCore.Metadata
|
||||
{
|
||||
/// <summary>
|
||||
/// API for SQLite-specific annotations accessed through
|
||||
/// <see cref="SqliteMetadataExtensions.Sqlite(IProperty)" />.
|
||||
/// </summary>
|
||||
public interface ISqlitePropertyAnnotations : IRelationalPropertyAnnotations
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the SRID to use when creating a column for this property.
|
||||
/// </summary>
|
||||
int? Srid { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the dimension to use when creating a column for this property.
|
||||
/// </summary>
|
||||
string Dimension { get; }
|
||||
}
|
||||
}
|
|
@ -38,5 +38,23 @@ namespace Microsoft.EntityFrameworkCore.Sqlite.Metadata.Internal
|
|||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public const string InlinePrimaryKeyName = Prefix + "InlinePrimaryKeyName";
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public const string InitSpatialMetaData = Prefix + "InitSpatialMetaData";
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public const string Srid = Prefix + "Srid";
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public const string Dimension = Prefix + "Dimension";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,10 +25,10 @@ namespace Microsoft.EntityFrameworkCore.Sqlite.Metadata.Internal
|
|||
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public static RelationalPropertyBuilderAnnotations Sqlite(
|
||||
public static SqlitePropertyBuilderAnnotations Sqlite(
|
||||
[NotNull] this InternalPropertyBuilder builder,
|
||||
ConfigurationSource configurationSource)
|
||||
=> new RelationalPropertyBuilderAnnotations(builder, configurationSource);
|
||||
=> new SqlitePropertyBuilderAnnotations(builder, configurationSource);
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
|
||||
|
|
|
@ -0,0 +1,104 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using JetBrains.Annotations;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Metadata.Internal;
|
||||
|
||||
namespace Microsoft.EntityFrameworkCore.Sqlite.Metadata.Internal
|
||||
{
|
||||
/// <summary>
|
||||
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public class SqlitePropertyBuilderAnnotations : SqlitePropertyAnnotations
|
||||
{
|
||||
/// <summary>
|
||||
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public SqlitePropertyBuilderAnnotations(
|
||||
[NotNull] InternalPropertyBuilder internalBuilder,
|
||||
ConfigurationSource configurationSource)
|
||||
: base(new RelationalAnnotationsBuilder(internalBuilder, configurationSource))
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
protected new virtual RelationalAnnotationsBuilder Annotations => (RelationalAnnotationsBuilder)base.Annotations;
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
protected override bool ShouldThrowOnConflict => false;
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
protected override bool ShouldThrowOnInvalidConfiguration => Annotations.ConfigurationSource == ConfigurationSource.Explicit;
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public virtual bool HasColumnName([CanBeNull] string value) => SetColumnName(value);
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public virtual bool CanSetColumnName([CanBeNull] string value)
|
||||
=> Annotations.CanSetAnnotation(RelationalAnnotationNames.ColumnName, value);
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public virtual bool HasColumnType([CanBeNull] string value) => SetColumnType(value);
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public virtual bool HasDefaultValueSql([CanBeNull] string value)
|
||||
=> SetDefaultValueSql(value);
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public virtual bool HasComputedColumnSql([CanBeNull] string value)
|
||||
=> SetComputedColumnSql(value);
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public virtual bool HasDefaultValue([CanBeNull] object value)
|
||||
=> SetDefaultValue(value);
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public new virtual bool IsFixedLength(bool fixedLength)
|
||||
=> SetFixedLength(fixedLength);
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public virtual bool HasSrid(int? value) => SetSrid(value);
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public virtual bool HasDimension(string value) => SetDimension(value);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using JetBrains.Annotations;
|
||||
using Microsoft.EntityFrameworkCore.Sqlite.Metadata.Internal;
|
||||
|
||||
namespace Microsoft.EntityFrameworkCore.Metadata
|
||||
{
|
||||
/// <summary>
|
||||
/// Properties for SQLite-specific annotations accessed through
|
||||
/// <see cref="SqliteMetadataExtensions.Sqlite(IMutableProperty)" />.
|
||||
/// </summary>
|
||||
public class SqlitePropertyAnnotations : RelationalPropertyAnnotations, ISqlitePropertyAnnotations
|
||||
{
|
||||
/// <summary>
|
||||
/// Constructs an instance for annotations of the given <see cref="IProperty" />.
|
||||
/// </summary>
|
||||
/// <param name="property"> The <see cref="IProperty" /> to use. </param>
|
||||
public SqlitePropertyAnnotations([NotNull] IProperty property)
|
||||
: base(property)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructs an instance for annotations of the <see cref="IProperty" />
|
||||
/// represented by the given annotation helper.
|
||||
/// </summary>
|
||||
/// <param name="annotations">
|
||||
/// The <see cref="RelationalAnnotations" /> helper representing the <see cref="IProperty" /> to annotate.
|
||||
/// </param>
|
||||
protected SqlitePropertyAnnotations([NotNull] RelationalAnnotations annotations)
|
||||
: base(annotations)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the SRID to use when creating a column for this property.
|
||||
/// </summary>
|
||||
public virtual int? Srid
|
||||
{
|
||||
get => (int?)Annotations.Metadata[SqliteAnnotationNames.Srid];
|
||||
set => SetSrid(value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the SRID to use when creating a column for this property.
|
||||
/// </summary>
|
||||
/// <param name="value"> The SRID. </param>
|
||||
/// <returns> true if the annotation was set; otherwise, false. </returns>
|
||||
protected virtual bool SetSrid(int? value)
|
||||
=> Annotations.SetAnnotation(SqliteAnnotationNames.Srid, value);
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the dimension to use when creating a column for this property.
|
||||
/// </summary>
|
||||
public virtual string Dimension
|
||||
{
|
||||
get => (string)Annotations.Metadata[SqliteAnnotationNames.Dimension];
|
||||
set => SetDimension(value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the dimension to use when creating a column for this property.
|
||||
/// </summary>
|
||||
/// <param name="value"> The dimension. </param>
|
||||
/// <returns> true if the annotation was set; otherwise, false. </returns>
|
||||
protected virtual bool SetDimension(string value)
|
||||
=> Annotations.SetAnnotation(SqliteAnnotationNames.Dimension, value);
|
||||
}
|
||||
}
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using JetBrains.Annotations;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
|
@ -27,6 +28,19 @@ namespace Microsoft.EntityFrameworkCore.Sqlite.Migrations.Internal
|
|||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public override IEnumerable<IAnnotation> For(IModel model)
|
||||
{
|
||||
if (model.GetEntityTypes().SelectMany(t => t.GetProperties()).Any(
|
||||
p => SqliteMigrationsSqlGenerator.IsSpatialiteType(p.Relational().ColumnType)))
|
||||
{
|
||||
yield return new Annotation(SqliteAnnotationNames.InitSpatialMetaData, true);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
|
@ -39,6 +53,18 @@ namespace Microsoft.EntityFrameworkCore.Sqlite.Migrations.Internal
|
|||
{
|
||||
yield return new Annotation(SqliteAnnotationNames.Autoincrement, true);
|
||||
}
|
||||
|
||||
var srid = property.Sqlite().Srid;
|
||||
if (srid != null)
|
||||
{
|
||||
yield return new Annotation(SqliteAnnotationNames.Srid, srid);
|
||||
}
|
||||
|
||||
var dimension = property.Sqlite().Dimension;
|
||||
if (dimension != null)
|
||||
{
|
||||
yield return new Annotation(SqliteAnnotationNames.Dimension, dimension);
|
||||
}
|
||||
}
|
||||
|
||||
private static bool HasConverter(IProperty property)
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using JetBrains.Annotations;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
|
@ -12,6 +13,7 @@ using Microsoft.EntityFrameworkCore.Metadata.Internal;
|
|||
using Microsoft.EntityFrameworkCore.Migrations.Operations;
|
||||
using Microsoft.EntityFrameworkCore.Sqlite.Internal;
|
||||
using Microsoft.EntityFrameworkCore.Sqlite.Metadata.Internal;
|
||||
using Microsoft.EntityFrameworkCore.Storage;
|
||||
using Microsoft.EntityFrameworkCore.Utilities;
|
||||
|
||||
namespace Microsoft.EntityFrameworkCore.Migrations
|
||||
|
@ -21,6 +23,19 @@ namespace Microsoft.EntityFrameworkCore.Migrations
|
|||
/// </summary>
|
||||
public class SqliteMigrationsSqlGenerator : MigrationsSqlGenerator
|
||||
{
|
||||
private static readonly HashSet<string> _spatialiteTypes
|
||||
= new HashSet<string>(StringComparer.OrdinalIgnoreCase)
|
||||
{
|
||||
"GEOMETRY",
|
||||
"GEOMETRYCOLLECTION",
|
||||
"LINESTRING",
|
||||
"MULTILINESTRING",
|
||||
"MULTIPOINT",
|
||||
"MULTIPOLYGON",
|
||||
"POINT",
|
||||
"POLYGON"
|
||||
};
|
||||
|
||||
private readonly IMigrationsAnnotationProvider _migrationsAnnotations;
|
||||
|
||||
/// <summary>
|
||||
|
@ -42,9 +57,33 @@ namespace Microsoft.EntityFrameworkCore.Migrations
|
|||
/// <param name="model"> The target model which may be <c>null</c> if the operations exist without a model. </param>
|
||||
/// <returns> The list of commands to be executed or scripted. </returns>
|
||||
public override IReadOnlyList<MigrationCommand> Generate(IReadOnlyList<MigrationOperation> operations, IModel model = null)
|
||||
=> base.Generate(LiftForeignKeyOperations(operations), model);
|
||||
=> base.Generate(RewriteOperations(operations, model), model);
|
||||
|
||||
private static IReadOnlyList<MigrationOperation> LiftForeignKeyOperations(IReadOnlyList<MigrationOperation> migrationOperations)
|
||||
/// <summary>
|
||||
/// Checks whether a column type is one of the SpatiaLite column types.
|
||||
/// </summary>
|
||||
/// <param name="columnType"> The column type to check. </param>
|
||||
/// <returns> true if it's a SpatiaLite type; otherwise, false. </returns>
|
||||
public static bool IsSpatialiteType(string columnType)
|
||||
=> _spatialiteTypes.Contains(columnType);
|
||||
|
||||
private bool IsSpatialiteColumn(AddColumnOperation operation, IModel model)
|
||||
=> IsSpatialiteType(
|
||||
operation.ColumnType
|
||||
?? GetColumnType(
|
||||
operation.Schema,
|
||||
operation.Table,
|
||||
operation.Name,
|
||||
operation.ClrType,
|
||||
operation.IsUnicode,
|
||||
operation.MaxLength,
|
||||
operation.IsFixedLength,
|
||||
operation.IsRowVersion,
|
||||
model));
|
||||
|
||||
private IReadOnlyList<MigrationOperation> RewriteOperations(
|
||||
IReadOnlyList<MigrationOperation> migrationOperations,
|
||||
IModel model)
|
||||
{
|
||||
var operations = new List<MigrationOperation>();
|
||||
foreach (var operation in migrationOperations)
|
||||
|
@ -58,15 +97,120 @@ namespace Microsoft.EntityFrameworkCore.Migrations
|
|||
if (table != null)
|
||||
{
|
||||
table.ForeignKeys.Add(foreignKeyOperation);
|
||||
//do not add to fk operation migration
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
operations.Add(operation);
|
||||
}
|
||||
}
|
||||
else if (operation is CreateTableOperation createTableOperation)
|
||||
{
|
||||
var spatialiteColumns = new Stack<AddColumnOperation>();
|
||||
for (var i = createTableOperation.Columns.Count - 1; i >= 0; i--)
|
||||
{
|
||||
var addColumnOperation = createTableOperation.Columns[i];
|
||||
|
||||
operations.Add(operation);
|
||||
if (IsSpatialiteColumn(addColumnOperation, model))
|
||||
{
|
||||
spatialiteColumns.Push(addColumnOperation);
|
||||
createTableOperation.Columns.RemoveAt(i);
|
||||
}
|
||||
}
|
||||
|
||||
operations.Add(operation);
|
||||
operations.AddRange(spatialiteColumns);
|
||||
}
|
||||
else
|
||||
{
|
||||
operations.Add(operation);
|
||||
}
|
||||
}
|
||||
|
||||
return operations.AsReadOnly();
|
||||
return operations;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Builds commands for the given <see cref="AlterDatabaseOperation" /> by making calls on the given
|
||||
/// <see cref="MigrationCommandListBuilder" />.
|
||||
/// </summary>
|
||||
/// <param name="operation"> The operation. </param>
|
||||
/// <param name="model"> The target model which may be <c>null</c> if the operations exist without a model. </param>
|
||||
/// <param name="builder"> The command builder to use to build the commands. </param>
|
||||
protected override void Generate(AlterDatabaseOperation operation, IModel model, MigrationCommandListBuilder builder)
|
||||
{
|
||||
if (operation[SqliteAnnotationNames.InitSpatialMetaData] as bool? != true
|
||||
|| operation.OldDatabase[SqliteAnnotationNames.InitSpatialMetaData] as bool? == true)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
builder
|
||||
.Append("SELECT InitSpatialMetaData()")
|
||||
.AppendLine(Dependencies.SqlGenerationHelper.StatementTerminator);
|
||||
EndStatement(builder);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Builds commands for the given <see cref="AddColumnOperation" /> by making calls on the given
|
||||
/// <see cref="MigrationCommandListBuilder" />.
|
||||
/// </summary>
|
||||
/// <param name="operation"> The operation. </param>
|
||||
/// <param name="model"> The target model which may be <c>null</c> if the operations exist without a model. </param>
|
||||
/// <param name="builder"> The command builder to use to build the commands. </param>
|
||||
/// <param name="terminate"> Indicates whether or not to terminate the command after generating SQL for the operation. </param>
|
||||
protected override void Generate(AddColumnOperation operation, IModel model, MigrationCommandListBuilder builder, bool terminate)
|
||||
{
|
||||
if (!IsSpatialiteColumn(operation, model))
|
||||
{
|
||||
base.Generate(operation, model, builder, terminate);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
var stringTypeMapping = Dependencies.TypeMappingSource.GetMapping(typeof(string));
|
||||
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(
|
||||
operation.Schema,
|
||||
operation.Table,
|
||||
operation.Name,
|
||||
operation.ClrType,
|
||||
operation.IsUnicode,
|
||||
operation.MaxLength,
|
||||
operation.IsFixedLength,
|
||||
operation.IsRowVersion,
|
||||
model);
|
||||
if (!string.IsNullOrEmpty(dimension))
|
||||
{
|
||||
geometryType += dimension;
|
||||
}
|
||||
|
||||
builder
|
||||
.Append("SELECT AddGeometryColumn(")
|
||||
.Append(stringTypeMapping.GenerateSqlLiteral(operation.Table))
|
||||
.Append(", ")
|
||||
.Append(stringTypeMapping.GenerateSqlLiteral(operation.Name))
|
||||
.Append(", ")
|
||||
.Append(longTypeMapping.GenerateSqlLiteral(srid))
|
||||
.Append(", ")
|
||||
.Append(stringTypeMapping.GenerateSqlLiteral(geometryType))
|
||||
.Append(", -1, ")
|
||||
.Append(operation.IsNullable ? "0" : "1")
|
||||
.Append(")");
|
||||
|
||||
if (terminate)
|
||||
{
|
||||
builder.AppendLine(Dependencies.SqlGenerationHelper.StatementTerminator);
|
||||
EndStatement(builder);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Fail("I have a bad feeling about this. Geometry columns don't compose well.");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -12,6 +12,7 @@ using System.Text;
|
|||
using JetBrains.Annotations;
|
||||
using Microsoft.Data.Sqlite;
|
||||
using Microsoft.EntityFrameworkCore.Diagnostics;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Internal;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Scaffolding;
|
||||
|
@ -82,6 +83,9 @@ namespace Microsoft.EntityFrameworkCore.Sqlite.Scaffolding.Internal
|
|||
if (!connectionStartedOpen)
|
||||
{
|
||||
connection.Open();
|
||||
|
||||
((SqliteConnection)connection).EnableExtensions();
|
||||
SpatialiteLoader.TryLoad(connection);
|
||||
}
|
||||
|
||||
try
|
||||
|
@ -146,9 +150,9 @@ namespace Microsoft.EntityFrameworkCore.Sqlite.Scaffolding.Internal
|
|||
command.CommandText = new StringBuilder()
|
||||
.AppendLine("SELECT \"name\"")
|
||||
.AppendLine("FROM \"sqlite_master\"")
|
||||
.Append("WHERE \"type\" = 'table' AND instr(\"name\", 'sqlite_') <> 1 AND \"name\" <> '")
|
||||
.Append("WHERE \"type\" = 'table' AND instr(\"name\", 'sqlite_') <> 1 AND \"name\" NOT IN ('")
|
||||
.Append(HistoryRepository.DefaultTableName)
|
||||
.AppendLine("';")
|
||||
.AppendLine("', 'geometry_columns', 'spatial_ref_sys', 'spatialite_history');")
|
||||
.ToString();
|
||||
|
||||
using (var reader = command.ExecuteReader())
|
||||
|
|
|
@ -7,6 +7,7 @@ using System.Threading;
|
|||
using System.Threading.Tasks;
|
||||
using JetBrains.Annotations;
|
||||
using Microsoft.Data.Sqlite;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Sqlite.Infrastructure.Internal;
|
||||
using Microsoft.EntityFrameworkCore.Storage;
|
||||
using Microsoft.EntityFrameworkCore.Utilities;
|
||||
|
@ -20,6 +21,7 @@ namespace Microsoft.EntityFrameworkCore.Sqlite.Storage.Internal
|
|||
public class SqliteRelationalConnection : RelationalConnection, ISqliteRelationalConnection
|
||||
{
|
||||
private readonly IRawSqlCommandBuilder _rawSqlCommandBuilder;
|
||||
private readonly bool _loadSpatialite;
|
||||
private readonly bool _enforceForeignKeys = true;
|
||||
|
||||
/// <summary>
|
||||
|
@ -38,6 +40,7 @@ namespace Microsoft.EntityFrameworkCore.Sqlite.Storage.Internal
|
|||
var optionsExtension = dependencies.ContextOptions.Extensions.OfType<SqliteOptionsExtension>().FirstOrDefault();
|
||||
if (optionsExtension != null)
|
||||
{
|
||||
_loadSpatialite = optionsExtension.LoadSpatialite;
|
||||
_enforceForeignKeys = optionsExtension.EnforceForeignKeys;
|
||||
}
|
||||
}
|
||||
|
@ -62,6 +65,7 @@ namespace Microsoft.EntityFrameworkCore.Sqlite.Storage.Internal
|
|||
{
|
||||
if (base.Open(errorsExpected))
|
||||
{
|
||||
LoadSpatialite();
|
||||
EnableForeignKeys();
|
||||
return true;
|
||||
}
|
||||
|
@ -77,6 +81,7 @@ namespace Microsoft.EntityFrameworkCore.Sqlite.Storage.Internal
|
|||
{
|
||||
if (await base.OpenAsync(cancellationToken, errorsExpected))
|
||||
{
|
||||
LoadSpatialite();
|
||||
EnableForeignKeys();
|
||||
return true;
|
||||
}
|
||||
|
@ -84,6 +89,19 @@ namespace Microsoft.EntityFrameworkCore.Sqlite.Storage.Internal
|
|||
return false;
|
||||
}
|
||||
|
||||
private void LoadSpatialite()
|
||||
{
|
||||
if (!_loadSpatialite)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var connection = (SqliteConnection)DbConnection;
|
||||
connection.EnableExtensions();
|
||||
SpatialiteLoader.Load(DbConnection);
|
||||
connection.EnableExtensions(false);
|
||||
}
|
||||
|
||||
private void EnableForeignKeys()
|
||||
{
|
||||
if (_enforceForeignKeys)
|
||||
|
|
|
@ -49,6 +49,15 @@ namespace Microsoft.EntityFrameworkCore.Sqlite.Storage.Internal
|
|||
{ typeof(Guid), new SqliteGuidTypeMapping(BlobTypeName) }
|
||||
};
|
||||
|
||||
private readonly Dictionary<string, RelationalTypeMapping> _storeTypeMappings
|
||||
= new Dictionary<string, RelationalTypeMapping>(StringComparer.OrdinalIgnoreCase)
|
||||
{
|
||||
{ IntegerTypeName, _integer },
|
||||
{ RealTypeName, _real },
|
||||
{ BlobTypeName, _blob },
|
||||
{ TextTypeName, _text },
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
|
@ -74,14 +83,21 @@ namespace Microsoft.EntityFrameworkCore.Sqlite.Storage.Internal
|
|||
}
|
||||
|
||||
var storeTypeName = mappingInfo.StoreTypeName;
|
||||
if (storeTypeName == null)
|
||||
if (storeTypeName != null
|
||||
&& _storeTypeMappings.TryGetValue(storeTypeName, out mapping))
|
||||
{
|
||||
return null;
|
||||
return mapping;
|
||||
}
|
||||
|
||||
return storeTypeName.Length != 0
|
||||
? _typeRules.Select(r => r(storeTypeName)).FirstOrDefault(r => r != null) ?? _text
|
||||
: _text; // This may seem odd, but it's okay because we are matching SQLite's loose typing.
|
||||
mapping = base.FindMapping(mappingInfo);
|
||||
|
||||
return mapping != null
|
||||
? mapping
|
||||
: storeTypeName != null
|
||||
? storeTypeName.Length != 0
|
||||
? _typeRules.Select(r => r(storeTypeName)).FirstOrDefault(r => r != null) ?? _text
|
||||
: _text // This may seem odd, but it's okay because we are matching SQLite's loose typing.
|
||||
: null;
|
||||
}
|
||||
|
||||
private readonly Func<string, RelationalTypeMapping>[] _typeRules =
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<AssemblyName>Microsoft.EntityFrameworkCore.Sqlite.NetTopologySuite</AssemblyName>
|
||||
<PackageId>Microsoft.EntityFrameworkCore.Sqlite.NetTopologySuite</PackageId>
|
||||
<RootNamespace>Microsoft.EntityFrameworkCore.Sqlite</RootNamespace>
|
||||
<Description>NetTopologySuite support for the SQLite database provider for Entity Framework Core.</Description>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<MinClientVersion>3.6</MinClientVersion>
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
<PackageTags>$(PackageTags);SQLite;GIS;NTS;OGC;SpatiaLite</PackageTags>
|
||||
<CodeAnalysisRuleSet>..\..\EFCore.ruleset</CodeAnalysisRuleSet>
|
||||
<EnableApiCheck>false</EnableApiCheck>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Include="..\Shared\*.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\EFCore.Sqlite.Core\EFCore.Sqlite.Core.csproj" PrivateAssets="contentfiles;build" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="mod_spatialite" Version="$(mod_spatialitePackageVersion)" />
|
||||
<PackageReference Include="NetTopologySuite.Core" Version="$(NetTopologySuiteCorePackageVersion)" />
|
||||
<PackageReference Include="NetTopologySuite.IO.SpatiaLite" Version="$(NetTopologySuiteIOSpatiaLitePackageVersion)" />
|
||||
<PackageReference Include="StyleCop.Analyzers" Version="$(StyleCopAnalyzersPackageVersion)" PrivateAssets="All" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Update="Properties\SqliteNTSStrings.Designer.tt">
|
||||
<Generator>TextTemplatingFileGenerator</Generator>
|
||||
<LastGenOutput>SqliteNTSStrings.Designer.cs</LastGenOutput>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Service Include="{508349b6-6b84-4df5-91f0-309beebad82d}" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Update="Properties\SqliteNTSStrings.Designer.cs">
|
||||
<DesignTime>True</DesignTime>
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>SqliteNTSStrings.Designer.tt</DependentUpon>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Update="Properties\SqliteNTSStrings.resx">
|
||||
<CustomToolNamespace>Microsoft.EntityFrameworkCore.Sqlite.Internal</CustomToolNamespace>
|
||||
</EmbeddedResource>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
|
@ -0,0 +1,39 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using JetBrains.Annotations;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Sqlite.Infrastructure.Internal;
|
||||
using Microsoft.EntityFrameworkCore.Utilities;
|
||||
|
||||
namespace Microsoft.EntityFrameworkCore
|
||||
{
|
||||
/// <summary>
|
||||
/// NetTopologySuite specific extension methods for <see cref="SqliteDbContextOptionsBuilder"/>.
|
||||
/// </summary>
|
||||
public static class SqliteNTSDbContextOptionsBuilderExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Use NetTopologySuite to access SpatiaLite data.
|
||||
/// </summary>
|
||||
/// <param name="optionsBuilder"> The build being used to configure SQLite. </param>
|
||||
/// <returns> The options builder so that further configuration can be chained. </returns>
|
||||
public static SqliteDbContextOptionsBuilder UseNetTopologySuite(
|
||||
[NotNull] this SqliteDbContextOptionsBuilder optionsBuilder)
|
||||
{
|
||||
Check.NotNull(optionsBuilder, nameof(optionsBuilder));
|
||||
|
||||
var coreOptionsBuilder = ((IRelationalDbContextOptionsBuilderInfrastructure)optionsBuilder).OptionsBuilder;
|
||||
var infrastructure = (IDbContextOptionsBuilderInfrastructure)coreOptionsBuilder;
|
||||
var sqliteExtension = coreOptionsBuilder.Options.FindExtension<SqliteOptionsExtension>()
|
||||
?? new SqliteOptionsExtension();
|
||||
var ntsExtension = coreOptionsBuilder.Options.FindExtension<SqliteNTSOptionsExtension>()
|
||||
?? new SqliteNTSOptionsExtension();
|
||||
|
||||
infrastructure.AddOrUpdateExtension(sqliteExtension.WithLoadSpatialite(true));
|
||||
infrastructure.AddOrUpdateExtension(ntsExtension);
|
||||
|
||||
return optionsBuilder;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using GeoAPI.Geometries;
|
||||
using JetBrains.Annotations;
|
||||
using Microsoft.EntityFrameworkCore.Metadata.Builders;
|
||||
using Microsoft.EntityFrameworkCore.Utilities;
|
||||
|
||||
namespace Microsoft.EntityFrameworkCore
|
||||
{
|
||||
/// <summary>
|
||||
/// SQLite and NetTopologySuite specific extension methods for <see cref="PropertyBuilder" />.
|
||||
/// </summary>
|
||||
public static class SqliteNTSPropertyBuilderExtensions
|
||||
{
|
||||
/// <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="ordinates"> The dimension ordinates. </param>
|
||||
/// <returns> The same builder instance so that multiple calls can be chained. </returns>
|
||||
public static PropertyBuilder ForSqliteHasDimension(
|
||||
[NotNull] this PropertyBuilder propertyBuilder,
|
||||
Ordinates ordinates)
|
||||
{
|
||||
Check.NotNull(propertyBuilder, nameof(propertyBuilder));
|
||||
|
||||
string dimension = null;
|
||||
if (ordinates.HasFlag(Ordinates.Z))
|
||||
{
|
||||
dimension += "Z";
|
||||
}
|
||||
if (ordinates.HasFlag(Ordinates.M))
|
||||
{
|
||||
dimension += "M";
|
||||
}
|
||||
|
||||
propertyBuilder.Metadata.Sqlite().Dimension = dimension;
|
||||
|
||||
return propertyBuilder;
|
||||
}
|
||||
|
||||
/// <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="ordinates"> The dimension ordinates. </param>
|
||||
/// <returns> The same builder instance so that multiple calls can be chained. </returns>
|
||||
public static PropertyBuilder<TProperty> ForSqliteHasDimension<TProperty>(
|
||||
[NotNull] this PropertyBuilder<TProperty> propertyBuilder,
|
||||
Ordinates ordinates)
|
||||
=> (PropertyBuilder<TProperty>)ForSqliteHasDimension((PropertyBuilder)propertyBuilder, ordinates);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using JetBrains.Annotations;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Query.ExpressionTranslators;
|
||||
using Microsoft.EntityFrameworkCore.Sqlite.Query.ExpressionTranslators.Internal;
|
||||
using Microsoft.EntityFrameworkCore.Sqlite.Storage.Internal;
|
||||
using Microsoft.EntityFrameworkCore.Storage;
|
||||
using Microsoft.EntityFrameworkCore.Utilities;
|
||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||
using NetTopologySuite;
|
||||
|
||||
namespace Microsoft.Extensions.DependencyInjection
|
||||
{
|
||||
/// <summary>
|
||||
/// EntityFrameworkCore.Sqlite.NetTopologySuite extension methods for <see cref="IServiceCollection" />.
|
||||
/// </summary>
|
||||
public static class SqliteNTSServiceCollectionExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Adds the services required for NetTopologySuite support in the SQLite provider for Entity Framework.
|
||||
/// </summary>
|
||||
/// <param name="serviceCollection"> The <see cref="IServiceCollection" /> to add services to. </param>
|
||||
/// <returns> The same service collection so that multiple calls can be chained. </returns>
|
||||
public static IServiceCollection AddEntityFrameworkSqliteNetTopologySuite(
|
||||
[NotNull] this IServiceCollection serviceCollection)
|
||||
{
|
||||
Check.NotNull(serviceCollection, nameof(serviceCollection));
|
||||
|
||||
serviceCollection.TryAddSingleton(NtsGeometryServices.Instance);
|
||||
|
||||
new EntityFrameworkRelationalServicesBuilder(serviceCollection)
|
||||
.TryAddProviderSpecificServices(
|
||||
x => x.TryAddSingletonEnumerable<IRelationalTypeMappingSourcePlugin, SqliteNTSTypeMappingSourcePlugin>()
|
||||
.TryAddSingletonEnumerable<IMethodCallTranslatorPlugin, SqliteNTSMethodCallTranslatorPlugin>()
|
||||
.TryAddSingletonEnumerable<IMemberTranslatorPlugin, SqliteNTSMemberTranslatorPlugin>());
|
||||
|
||||
return serviceCollection;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Sqlite.Internal;
|
||||
using Microsoft.EntityFrameworkCore.Sqlite.Storage.Internal;
|
||||
using Microsoft.EntityFrameworkCore.Storage;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Microsoft.EntityFrameworkCore.Sqlite.Infrastructure.Internal
|
||||
{
|
||||
/// <summary>
|
||||
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public class SqliteNTSOptionsExtension : IDbContextOptionsExtension
|
||||
{
|
||||
/// <summary>
|
||||
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public virtual string LogFragment => "using NetTopologySuite ";
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public virtual bool ApplyServices(IServiceCollection services)
|
||||
{
|
||||
services.AddEntityFrameworkSqliteNetTopologySuite();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public virtual long GetServiceProviderHashCode() => 0;
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public virtual void Validate(IDbContextOptions options)
|
||||
{
|
||||
var internalServiceProvider = options.FindExtension<CoreOptionsExtension>()?.InternalServiceProvider;
|
||||
if (internalServiceProvider != null)
|
||||
{
|
||||
using (var scope = internalServiceProvider.CreateScope())
|
||||
{
|
||||
if (scope.ServiceProvider.GetService<IEnumerable<IRelationalTypeMappingSourcePlugin>>()
|
||||
?.Any(s => s is SqliteNTSTypeMappingSourcePlugin) != true)
|
||||
{
|
||||
throw new InvalidOperationException(SqliteNTSStrings.NTSServicesMissing);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
// <auto-generated />
|
||||
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using System.Resources;
|
||||
using JetBrains.Annotations;
|
||||
using Microsoft.EntityFrameworkCore.Diagnostics;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Microsoft.EntityFrameworkCore.Sqlite.Internal
|
||||
{
|
||||
/// <summary>
|
||||
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public static class SqliteNTSStrings
|
||||
{
|
||||
private static readonly ResourceManager _resourceManager
|
||||
= new ResourceManager("Microsoft.EntityFrameworkCore.Sqlite.Properties.SqliteNTSStrings", typeof(SqliteNTSStrings).GetTypeInfo().Assembly);
|
||||
|
||||
/// <summary>
|
||||
/// UseNetTopologySuite requires AddEntityFrameworkSqliteNetTopologySuite to be called on the internal service provider used.
|
||||
/// </summary>
|
||||
public static string NTSServicesMissing
|
||||
=> GetString("NTSServicesMissing");
|
||||
|
||||
private static string GetString(string name, params string[] formatterNames)
|
||||
{
|
||||
var value = _resourceManager.GetString(name);
|
||||
for (var i = 0; i < formatterNames.Length; i++)
|
||||
{
|
||||
value = value.Replace("{" + formatterNames[i] + "}", "{" + i + "}");
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
<#
|
||||
Session["ResourceFile"] = "SqliteNTSStrings.resx";
|
||||
#>
|
||||
<#@ include file="..\..\..\tools\Resources.tt" #>
|
|
@ -0,0 +1,123 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="NTSServicesMissing" xml:space="preserve">
|
||||
<value>UseNetTopologySuite requires AddEntityFrameworkSqliteNetTopologySuite to be called on the internal service provider used.</value>
|
||||
</data>
|
||||
</root>
|
|
@ -0,0 +1,45 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
using GeoAPI.Geometries;
|
||||
using Microsoft.EntityFrameworkCore.Query.Expressions;
|
||||
using Microsoft.EntityFrameworkCore.Query.ExpressionTranslators;
|
||||
|
||||
namespace Microsoft.EntityFrameworkCore.Sqlite.Query.ExpressionTranslators.Internal
|
||||
{
|
||||
/// <summary>
|
||||
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public class SqliteCurveMemberTranslator : IMemberTranslator
|
||||
{
|
||||
private static readonly IDictionary<MemberInfo, string> _memberToFunctionName = new Dictionary<MemberInfo, string>
|
||||
{
|
||||
{ typeof(ICurve).GetRuntimeProperty(nameof(ICurve.EndPoint)), "EndPoint" },
|
||||
{ typeof(ICurve).GetRuntimeProperty(nameof(ICurve.IsClosed)), "IsClosed" },
|
||||
{ typeof(ICurve).GetRuntimeProperty(nameof(ICurve.IsRing)), "IsRing" },
|
||||
{ typeof(ICurve).GetRuntimeProperty(nameof(ICurve.StartPoint)), "StartPoint" }
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public Expression Translate(MemberExpression memberExpression)
|
||||
{
|
||||
var member = memberExpression.Member.OnInterface(typeof(ICurve));
|
||||
if (_memberToFunctionName.TryGetValue(member, out var functionName))
|
||||
{
|
||||
return new SqlFunctionExpression(
|
||||
functionName,
|
||||
memberExpression.Type,
|
||||
new[] { memberExpression.Expression });
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
using GeoAPI.Geometries;
|
||||
using Microsoft.EntityFrameworkCore.Query.Expressions;
|
||||
using Microsoft.EntityFrameworkCore.Query.ExpressionTranslators;
|
||||
|
||||
namespace Microsoft.EntityFrameworkCore.Sqlite.Query.ExpressionTranslators.Internal
|
||||
{
|
||||
/// <summary>
|
||||
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public class SqliteGeometryCollectionMemberTranslator : IMemberTranslator
|
||||
{
|
||||
private static readonly MemberInfo _count = typeof(IGeometryCollection).GetRuntimeProperty(nameof(IGeometryCollection.Count));
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public virtual Expression Translate(MemberExpression memberExpression)
|
||||
{
|
||||
var member = memberExpression.Member.OnInterface(typeof(IGeometryCollection));
|
||||
if (Equals(member, _count))
|
||||
{
|
||||
return new SqlFunctionExpression(
|
||||
"NumGeometries",
|
||||
memberExpression.Type,
|
||||
new[] { memberExpression.Expression });
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
using GeoAPI.Geometries;
|
||||
using Microsoft.EntityFrameworkCore.Query.Expressions;
|
||||
using Microsoft.EntityFrameworkCore.Query.ExpressionTranslators;
|
||||
|
||||
namespace Microsoft.EntityFrameworkCore.Sqlite.Query.ExpressionTranslators.Internal
|
||||
{
|
||||
/// <summary>
|
||||
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public class SqliteGeometryCollectionMethodTranslator : IMethodCallTranslator
|
||||
{
|
||||
private static readonly MethodInfo _item = typeof(IGeometryCollection).GetRuntimeProperty("Item").GetMethod;
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public virtual Expression Translate(MethodCallExpression methodCallExpression)
|
||||
{
|
||||
var method = methodCallExpression.Method.OnInterface(typeof(IGeometryCollection));
|
||||
if (Equals(method, _item))
|
||||
{
|
||||
return new SqlFunctionExpression(
|
||||
"GeometryN",
|
||||
methodCallExpression.Type,
|
||||
new[]
|
||||
{
|
||||
methodCallExpression.Object,
|
||||
Expression.Add(methodCallExpression.Arguments[0], Expression.Constant(1))
|
||||
});
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
using GeoAPI.Geometries;
|
||||
using Microsoft.EntityFrameworkCore.Query.Expressions;
|
||||
using Microsoft.EntityFrameworkCore.Query.ExpressionTranslators;
|
||||
|
||||
namespace Microsoft.EntityFrameworkCore.Sqlite.Query.ExpressionTranslators.Internal
|
||||
{
|
||||
/// <summary>
|
||||
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public class SqliteGeometryMemberTranslator : IMemberTranslator
|
||||
{
|
||||
private static readonly IDictionary<MemberInfo, string> _memberToFunctionName = new Dictionary<MemberInfo, string>
|
||||
{
|
||||
{ typeof(IGeometry).GetRuntimeProperty(nameof(IGeometry.Area)), "Area" },
|
||||
{ typeof(IGeometry).GetRuntimeProperty(nameof(IGeometry.Boundary)), "Boundary" },
|
||||
{ typeof(IGeometry).GetRuntimeProperty(nameof(IGeometry.Centroid)), "Centroid" },
|
||||
{ typeof(IGeometry).GetRuntimeProperty(nameof(IGeometry.Dimension)), "Dimension" },
|
||||
{ typeof(IGeometry).GetRuntimeProperty(nameof(IGeometry.Envelope)), "Envelope" },
|
||||
{ typeof(IGeometry).GetRuntimeProperty(nameof(IGeometry.IsEmpty)), "IsEmpty" },
|
||||
{ typeof(IGeometry).GetRuntimeProperty(nameof(IGeometry.IsSimple)), "IsSimple" },
|
||||
{ typeof(IGeometry).GetRuntimeProperty(nameof(IGeometry.IsValid)), "IsValid" },
|
||||
{ typeof(IGeometry).GetRuntimeProperty(nameof(IGeometry.Length)), "GLength" },
|
||||
{ typeof(IGeometry).GetRuntimeProperty(nameof(IGeometry.NumGeometries)), "NumGeometries" },
|
||||
{ typeof(IGeometry).GetRuntimeProperty(nameof(IGeometry.NumPoints)), "NumPoints" },
|
||||
{ typeof(IGeometry).GetRuntimeProperty(nameof(IGeometry.PointOnSurface)), "PointOnSurface" },
|
||||
{ typeof(IGeometry).GetRuntimeProperty(nameof(IGeometry.SRID)), "SRID" }
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public Expression Translate(MemberExpression memberExpression)
|
||||
{
|
||||
var member = memberExpression.Member.OnInterface(typeof(IGeometry));
|
||||
if (_memberToFunctionName.TryGetValue(member, out var functionName))
|
||||
{
|
||||
return new SqlFunctionExpression(
|
||||
functionName,
|
||||
memberExpression.Type,
|
||||
new[] { memberExpression.Expression });
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,81 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
using GeoAPI.Geometries;
|
||||
using Microsoft.EntityFrameworkCore.Query.Expressions;
|
||||
using Microsoft.EntityFrameworkCore.Query.ExpressionTranslators;
|
||||
using NetTopologySuite.Geometries;
|
||||
|
||||
namespace Microsoft.EntityFrameworkCore.Sqlite.Query.ExpressionTranslators.Internal
|
||||
{
|
||||
/// <summary>
|
||||
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public class SqliteGeometryMethodTranslator : IMethodCallTranslator
|
||||
{
|
||||
private static readonly IDictionary<MethodInfo, string> _methodToFunctionName = new Dictionary<MethodInfo, string>
|
||||
{
|
||||
{ typeof(IGeometry).GetRuntimeMethod(nameof(IGeometry.AsBinary), Type.EmptyTypes), "AsBinary" },
|
||||
{ typeof(IGeometry).GetRuntimeMethod(nameof(IGeometry.AsText), Type.EmptyTypes), "AsText" },
|
||||
{ typeof(IGeometry).GetRuntimeMethod(nameof(IGeometry.Buffer), new[] { typeof(double) }), "Buffer" },
|
||||
{ typeof(IGeometry).GetRuntimeMethod(nameof(IGeometry.Buffer), new[] { typeof(double), typeof(int) }), "Buffer" },
|
||||
{ typeof(IGeometry).GetRuntimeMethod(nameof(IGeometry.Contains), new[] { typeof(IGeometry) }), "Contains" },
|
||||
{ typeof(IGeometry).GetRuntimeMethod(nameof(IGeometry.ConvexHull), Type.EmptyTypes), "ConvexHull" },
|
||||
{ typeof(IGeometry).GetRuntimeMethod(nameof(IGeometry.Crosses), new[] { typeof(IGeometry) }), "Crosses" },
|
||||
{ typeof(IGeometry).GetRuntimeMethod(nameof(IGeometry.CoveredBy), new[] { typeof(IGeometry) }), "CoveredBy" },
|
||||
{ typeof(IGeometry).GetRuntimeMethod(nameof(IGeometry.Covers), new[] { typeof(IGeometry) }), "Covers" },
|
||||
{ typeof(IGeometry).GetRuntimeMethod(nameof(IGeometry.Difference), new[] { typeof(IGeometry) }), "Difference" },
|
||||
{ typeof(IGeometry).GetRuntimeMethod(nameof(IGeometry.Disjoint), new[] { typeof(IGeometry) }), "Disjoint" },
|
||||
{ typeof(IGeometry).GetRuntimeMethod(nameof(IGeometry.Distance), new[] { typeof(IGeometry) }), "Distance" },
|
||||
{ typeof(IGeometry).GetRuntimeMethod(nameof(IGeometry.EqualsTopologically), new[] { typeof(IGeometry) }), "Equals" },
|
||||
{ typeof(IGeometry).GetRuntimeMethod(nameof(IGeometry.Intersection), new[] { typeof(IGeometry) }), "Intersection" },
|
||||
{ typeof(IGeometry).GetRuntimeMethod(nameof(IGeometry.Intersects), new[] { typeof(IGeometry) }), "Intersects" },
|
||||
{ typeof(IGeometry).GetRuntimeMethod(nameof(IGeometry.Overlaps), new[] { typeof(IGeometry) }), "Overlaps" },
|
||||
{ typeof(IGeometry).GetRuntimeMethod(nameof(IGeometry.Relate), new[] { typeof(IGeometry), typeof(string) }), "Relate" },
|
||||
{ typeof(IGeometry).GetRuntimeMethod(nameof(IGeometry.Reverse), Type.EmptyTypes), "ST_Reverse" },
|
||||
{ typeof(IGeometry).GetRuntimeMethod(nameof(IGeometry.SymmetricDifference), new[] { typeof(IGeometry) }), "SymDifference" },
|
||||
{ typeof(Geometry).GetRuntimeMethod(nameof(Geometry.ToBinary), Type.EmptyTypes), "AsBinary" },
|
||||
{ typeof(Geometry).GetRuntimeMethod(nameof(Geometry.ToText), Type.EmptyTypes), "AsText" },
|
||||
{ typeof(IGeometry).GetRuntimeMethod(nameof(IGeometry.Touches), new[] { typeof(IGeometry) }), "Touches" },
|
||||
{ typeof(IGeometry).GetRuntimeMethod(nameof(IGeometry.Union), new[] { typeof(IGeometry) }), "GUnion" },
|
||||
{ typeof(IGeometry).GetRuntimeMethod(nameof(IGeometry.Within), new[] { typeof(IGeometry) }), "Within" }
|
||||
};
|
||||
|
||||
private static readonly MethodInfo _getGeometryN = typeof(IGeometry).GetRuntimeMethod(nameof(IGeometry.GetGeometryN), new[] { typeof(int) });
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public Expression Translate(MethodCallExpression methodCallExpression)
|
||||
{
|
||||
var method = methodCallExpression.Method.OnInterface(typeof(IGeometry));
|
||||
if (_methodToFunctionName.TryGetValue(method, out var functionName))
|
||||
{
|
||||
return new SqlFunctionExpression(
|
||||
functionName,
|
||||
methodCallExpression.Type,
|
||||
new[] { methodCallExpression.Object }.Concat(methodCallExpression.Arguments));
|
||||
}
|
||||
if (Equals(method, _getGeometryN))
|
||||
{
|
||||
return new SqlFunctionExpression(
|
||||
"GeometryN",
|
||||
methodCallExpression.Type,
|
||||
new[]
|
||||
{
|
||||
methodCallExpression.Object,
|
||||
Expression.Add(methodCallExpression.Arguments[0], Expression.Constant(1))
|
||||
});
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
using Microsoft.EntityFrameworkCore.Query.Expressions;
|
||||
using Microsoft.EntityFrameworkCore.Query.ExpressionTranslators;
|
||||
using NetTopologySuite.Geometries;
|
||||
|
||||
namespace Microsoft.EntityFrameworkCore.Sqlite.Query.ExpressionTranslators.Internal
|
||||
{
|
||||
/// <summary>
|
||||
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public class SqliteLineStringMemberTranslator : IMemberTranslator
|
||||
{
|
||||
private static readonly MemberInfo _count = typeof(LineString).GetRuntimeProperty(nameof(LineString.Count));
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public Expression Translate(MemberExpression memberExpression)
|
||||
{
|
||||
if (Equals(memberExpression.Member, _count))
|
||||
{
|
||||
return new SqlFunctionExpression(
|
||||
"NumPoints",
|
||||
memberExpression.Type,
|
||||
new[] { memberExpression.Expression });
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
using GeoAPI.Geometries;
|
||||
using Microsoft.EntityFrameworkCore.Query.Expressions;
|
||||
using Microsoft.EntityFrameworkCore.Query.ExpressionTranslators;
|
||||
|
||||
namespace Microsoft.EntityFrameworkCore.Sqlite.Query.ExpressionTranslators.Internal
|
||||
{
|
||||
/// <summary>
|
||||
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public class SqliteLineStringMethodTranslator : IMethodCallTranslator
|
||||
{
|
||||
private static readonly MethodInfo _getPointN = typeof(ILineString).GetRuntimeMethod(nameof(ILineString.GetPointN), new[] { typeof(int) });
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public Expression Translate(MethodCallExpression methodCallExpression)
|
||||
{
|
||||
var method = methodCallExpression.Method.OnInterface(typeof(ILineString));
|
||||
if (Equals(method, _getPointN))
|
||||
{
|
||||
return new SqlFunctionExpression(
|
||||
"PointN",
|
||||
methodCallExpression.Type,
|
||||
new[] {
|
||||
methodCallExpression.Object,
|
||||
Expression.Add(methodCallExpression.Arguments[0], Expression.Constant(1))
|
||||
});
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
using GeoAPI.Geometries;
|
||||
using Microsoft.EntityFrameworkCore.Query.Expressions;
|
||||
using Microsoft.EntityFrameworkCore.Query.ExpressionTranslators;
|
||||
|
||||
namespace Microsoft.EntityFrameworkCore.Sqlite.Query.ExpressionTranslators.Internal
|
||||
{
|
||||
/// <summary>
|
||||
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public class SqliteMultiCurveMemberTranslator : IMemberTranslator
|
||||
{
|
||||
private static readonly MemberInfo _isClosed = typeof(IMultiCurve).GetRuntimeProperty(nameof(IMultiCurve.IsClosed));
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public Expression Translate(MemberExpression memberExpression)
|
||||
{
|
||||
var member = memberExpression.Member.OnInterface(typeof(IMultiCurve));
|
||||
if (Equals(member, _isClosed))
|
||||
{
|
||||
return new SqlFunctionExpression(
|
||||
"IsClosed",
|
||||
memberExpression.Type,
|
||||
new[] { memberExpression.Expression });
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.EntityFrameworkCore.Query.ExpressionTranslators;
|
||||
|
||||
namespace Microsoft.EntityFrameworkCore.Sqlite.Query.ExpressionTranslators.Internal
|
||||
{
|
||||
/// <summary>
|
||||
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public class SqliteNTSMemberTranslatorPlugin : IMemberTranslatorPlugin
|
||||
{
|
||||
/// <summary>
|
||||
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public virtual IEnumerable<IMemberTranslator> Translators { get; }
|
||||
= new IMemberTranslator[]
|
||||
{
|
||||
new SqliteCurveMemberTranslator(),
|
||||
new SqliteGeometryMemberTranslator(),
|
||||
new SqliteGeometryCollectionMemberTranslator(),
|
||||
new SqliteLineStringMemberTranslator(),
|
||||
new SqliteMultiCurveMemberTranslator(),
|
||||
new SqlitePointMemberTranslator(),
|
||||
new SqlitePolygonMemberTranslator()
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.EntityFrameworkCore.Query.ExpressionTranslators;
|
||||
|
||||
namespace Microsoft.EntityFrameworkCore.Sqlite.Query.ExpressionTranslators.Internal
|
||||
{
|
||||
/// <summary>
|
||||
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public class SqliteNTSMethodCallTranslatorPlugin : IMethodCallTranslatorPlugin
|
||||
{
|
||||
/// <summary>
|
||||
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public virtual IEnumerable<IMethodCallTranslator> Translators { get; } = new IMethodCallTranslator[]
|
||||
{
|
||||
new SqliteGeometryMethodTranslator(),
|
||||
new SqliteGeometryCollectionMethodTranslator(),
|
||||
new SqliteLineStringMethodTranslator(),
|
||||
new SqlitePolygonMethodTranslator()
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
using GeoAPI.Geometries;
|
||||
using Microsoft.EntityFrameworkCore.Query.Expressions;
|
||||
using Microsoft.EntityFrameworkCore.Query.ExpressionTranslators;
|
||||
|
||||
namespace Microsoft.EntityFrameworkCore.Sqlite.Query.ExpressionTranslators.Internal
|
||||
{
|
||||
/// <summary>
|
||||
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public class SqlitePointMemberTranslator : IMemberTranslator
|
||||
{
|
||||
private static readonly IDictionary<MemberInfo, string> _memberToFunctionName = new Dictionary<MemberInfo, string>
|
||||
{
|
||||
{ typeof(IPoint).GetRuntimeProperty(nameof(IPoint.M)), "M" },
|
||||
{ typeof(IPoint).GetRuntimeProperty(nameof(IPoint.X)), "X" },
|
||||
{ typeof(IPoint).GetRuntimeProperty(nameof(IPoint.Y)), "Y" },
|
||||
{ typeof(IPoint).GetRuntimeProperty(nameof(IPoint.Z)), "Z" }
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public Expression Translate(MemberExpression memberExpression)
|
||||
{
|
||||
var member = memberExpression.Member.OnInterface(typeof(IPoint));
|
||||
if (_memberToFunctionName.TryGetValue(member, out var functionName))
|
||||
{
|
||||
return new SqlFunctionExpression(
|
||||
functionName,
|
||||
memberExpression.Type,
|
||||
new[] { memberExpression.Expression });
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
using GeoAPI.Geometries;
|
||||
using Microsoft.EntityFrameworkCore.Query.Expressions;
|
||||
using Microsoft.EntityFrameworkCore.Query.ExpressionTranslators;
|
||||
|
||||
namespace Microsoft.EntityFrameworkCore.Sqlite.Query.ExpressionTranslators.Internal
|
||||
{
|
||||
/// <summary>
|
||||
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public class SqlitePolygonMemberTranslator : IMemberTranslator
|
||||
{
|
||||
private static readonly IDictionary<MemberInfo, string> _memberToFunctionName = new Dictionary<MemberInfo, string>
|
||||
{
|
||||
{ typeof(IPolygon).GetRuntimeProperty(nameof(IPolygon.ExteriorRing)), "ExteriorRing" },
|
||||
{ typeof(IPolygon).GetRuntimeProperty(nameof(IPolygon.NumInteriorRings)), "NumInteriorRing" }
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public Expression Translate(MemberExpression memberExpression)
|
||||
{
|
||||
var member = memberExpression.Member.OnInterface(typeof(IPolygon));
|
||||
if (_memberToFunctionName.TryGetValue(member, out var functionName))
|
||||
{
|
||||
return new SqlFunctionExpression(
|
||||
functionName,
|
||||
memberExpression.Type,
|
||||
new[] { memberExpression.Expression });
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
using GeoAPI.Geometries;
|
||||
using Microsoft.EntityFrameworkCore.Query.Expressions;
|
||||
using Microsoft.EntityFrameworkCore.Query.ExpressionTranslators;
|
||||
|
||||
namespace Microsoft.EntityFrameworkCore.Sqlite.Query.ExpressionTranslators.Internal
|
||||
{
|
||||
/// <summary>
|
||||
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public class SqlitePolygonMethodTranslator : IMethodCallTranslator
|
||||
{
|
||||
private static readonly MethodInfo _getInteriorRingN = typeof(IPolygon).GetRuntimeMethod(nameof(IPolygon.GetInteriorRingN), new[] { typeof(int) });
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public Expression Translate(MethodCallExpression methodCallExpression)
|
||||
{
|
||||
var method = methodCallExpression.Method.OnInterface(typeof(IPolygon));
|
||||
if (Equals(method, _getInteriorRingN))
|
||||
{
|
||||
return new SqlFunctionExpression(
|
||||
"InteriorRingN",
|
||||
methodCallExpression.Type,
|
||||
new[]
|
||||
{
|
||||
methodCallExpression.Object,
|
||||
Expression.Add(methodCallExpression.Arguments[0], Expression.Constant(1))
|
||||
});
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.ChangeTracking;
|
||||
using Microsoft.EntityFrameworkCore.Sqlite.Storage.ValueConversion.Internal;
|
||||
using Microsoft.EntityFrameworkCore.Storage;
|
||||
using NetTopologySuite.IO;
|
||||
|
||||
namespace Microsoft.EntityFrameworkCore.Sqlite.Storage.Internal
|
||||
{
|
||||
/// <summary>
|
||||
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public class SqliteGeometryTypeMapping : RelationalTypeMapping
|
||||
{
|
||||
private readonly GaiaGeoReader _reader;
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public SqliteGeometryTypeMapping(Type clrType, GaiaGeoReader reader, string storeType)
|
||||
: base(
|
||||
new RelationalTypeMappingParameters(
|
||||
new CoreTypeMappingParameters(
|
||||
clrType,
|
||||
new GeometryValueConverter(clrType, reader),
|
||||
new GeometryValueComparer(clrType)),
|
||||
storeType))
|
||||
{
|
||||
_reader = reader;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
protected SqliteGeometryTypeMapping(RelationalTypeMappingParameters parameters)
|
||||
: base(parameters)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
protected override RelationalTypeMapping Clone(RelationalTypeMappingParameters parameters)
|
||||
=> new SqliteGeometryTypeMapping(parameters);
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
protected override string GenerateNonNullSqlLiteral(object value)
|
||||
{
|
||||
// TODO: Avoid converting in the first place
|
||||
var geometry = _reader.Read((byte[])value);
|
||||
var srid = geometry.SRID;
|
||||
|
||||
// TODO: This won't emit M (see NetTopologySuite/NetTopologySuite#156)
|
||||
var text = "'" + geometry.AsText() + "'";
|
||||
|
||||
return srid > 0
|
||||
? $"GeomFromText({text}, {srid})"
|
||||
: $"GeomFromText({text})";
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,111 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using GeoAPI;
|
||||
using GeoAPI.Geometries;
|
||||
using JetBrains.Annotations;
|
||||
using Microsoft.EntityFrameworkCore.Storage;
|
||||
using Microsoft.EntityFrameworkCore.Utilities;
|
||||
using NetTopologySuite.IO;
|
||||
|
||||
namespace Microsoft.EntityFrameworkCore.Sqlite.Storage.Internal
|
||||
{
|
||||
/// <summary>
|
||||
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public class SqliteNTSTypeMappingSourcePlugin : IRelationalTypeMappingSourcePlugin
|
||||
{
|
||||
private static readonly Dictionary<string, Type> _storeTypeMappings
|
||||
= new Dictionary<string, Type>(StringComparer.OrdinalIgnoreCase)
|
||||
{
|
||||
{ "GEOMETRY", typeof(IGeometry) },
|
||||
{ "GEOMETRYCOLLECTION", typeof(IGeometryCollection) },
|
||||
{ "LINESTRING", typeof(ILineString) },
|
||||
{ "MULTILINESTRING", typeof(IMultiLineString) },
|
||||
{ "MULTIPOINT", typeof(IMultiPoint) },
|
||||
{ "MULTIPOLYGON", typeof(IMultiPolygon) },
|
||||
{ "POINT", typeof(IPoint) },
|
||||
{ "POLYGON", typeof(IPolygon) }
|
||||
};
|
||||
|
||||
private readonly GaiaGeoReader _reader;
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public SqliteNTSTypeMappingSourcePlugin([NotNull] IGeometryServices geometryServices)
|
||||
{
|
||||
Check.NotNull(geometryServices, nameof(geometryServices));
|
||||
|
||||
_reader = new GaiaGeoReader(
|
||||
geometryServices.DefaultCoordinateSequenceFactory,
|
||||
geometryServices.DefaultPrecisionModel);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public virtual RelationalTypeMapping FindMapping(in RelationalTypeMappingInfo mappingInfo)
|
||||
{
|
||||
var clrType = mappingInfo.ClrType;
|
||||
var storeTypeName = mappingInfo.StoreTypeName;
|
||||
string defaultStoreType = null;
|
||||
Type defaultClrType = null;
|
||||
|
||||
return (clrType != null && TryGetDefaultStoreType(clrType, out defaultStoreType)
|
||||
|| storeTypeName != null && _storeTypeMappings.TryGetValue(storeTypeName, out defaultClrType))
|
||||
? new SqliteGeometryTypeMapping(
|
||||
clrType ?? defaultClrType ?? typeof(IGeometry),
|
||||
_reader,
|
||||
storeTypeName ?? defaultStoreType ?? "GEOMETRY")
|
||||
: null;
|
||||
}
|
||||
|
||||
private static bool TryGetDefaultStoreType(Type clrType, out string defaultStoreType)
|
||||
{
|
||||
if (typeof(ILineString).IsAssignableFrom(clrType))
|
||||
{
|
||||
defaultStoreType = "LINESTRING";
|
||||
}
|
||||
else if (typeof(IMultiLineString).IsAssignableFrom(clrType))
|
||||
{
|
||||
defaultStoreType = "MULTILINESTRING";
|
||||
}
|
||||
else if (typeof(IMultiPoint).IsAssignableFrom(clrType))
|
||||
{
|
||||
defaultStoreType = "MULTIPOINT";
|
||||
}
|
||||
else if (typeof(IMultiPolygon).IsAssignableFrom(clrType))
|
||||
{
|
||||
defaultStoreType = "MULTIPOLYGON";
|
||||
}
|
||||
else if (typeof(IPoint).IsAssignableFrom(clrType))
|
||||
{
|
||||
defaultStoreType = "POINT";
|
||||
}
|
||||
else if (typeof(IPolygon).IsAssignableFrom(clrType))
|
||||
{
|
||||
defaultStoreType = "POLYGON";
|
||||
}
|
||||
else if (typeof(IGeometryCollection).IsAssignableFrom(clrType))
|
||||
{
|
||||
defaultStoreType = "GEOMETRYCOLLECTION";
|
||||
}
|
||||
else if (typeof(IGeometry).IsAssignableFrom(clrType))
|
||||
{
|
||||
defaultStoreType = "GEOMETRY";
|
||||
}
|
||||
else
|
||||
{
|
||||
defaultStoreType = null;
|
||||
}
|
||||
|
||||
return defaultStoreType != null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,95 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
using GeoAPI.Geometries;
|
||||
using Microsoft.EntityFrameworkCore.Internal;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
using NetTopologySuite.IO;
|
||||
|
||||
namespace Microsoft.EntityFrameworkCore.Sqlite.Storage.ValueConversion.Internal
|
||||
{
|
||||
/// <summary>
|
||||
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public class GeometryValueConverter : ValueConverter
|
||||
{
|
||||
private static readonly GaiaGeoWriter _writer = new GaiaGeoWriter();
|
||||
|
||||
private Func<object, object> _convertToProvider;
|
||||
private Func<object, object> _convertFromProvider;
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public GeometryValueConverter(Type type, GaiaGeoReader reader)
|
||||
: base(
|
||||
(Expression<Func<IGeometry, byte[]>>)(g => _writer.Write(g)),
|
||||
GetConvertFromProviderExpression(type, reader))
|
||||
{
|
||||
ModelClrType = type;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public override Func<object, object> ConvertToProvider
|
||||
=> NonCapturingLazyInitializer.EnsureInitialized(
|
||||
ref _convertToProvider,
|
||||
this,
|
||||
x => Compile(x.ConvertToProviderExpression));
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public override Func<object, object> ConvertFromProvider
|
||||
=> NonCapturingLazyInitializer.EnsureInitialized(
|
||||
ref _convertFromProvider,
|
||||
this,
|
||||
x => Compile(x.ConvertFromProviderExpression));
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public override Type ModelClrType { get; }
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public override Type ProviderClrType
|
||||
=> typeof(byte[]);
|
||||
|
||||
private static LambdaExpression GetConvertFromProviderExpression(Type type, GaiaGeoReader reader)
|
||||
{
|
||||
var bytes = Expression.Parameter(typeof(byte[]), "blob");
|
||||
|
||||
Expression body = Expression.Call(
|
||||
Expression.Constant(reader),
|
||||
typeof(GaiaGeoReader).GetRuntimeMethod(nameof(GaiaGeoReader.Read), new[] { typeof(byte[]) }),
|
||||
bytes);
|
||||
if (!type.IsAssignableFrom(typeof(IGeometry)))
|
||||
{
|
||||
body = Expression.Convert(body, type);
|
||||
}
|
||||
|
||||
return Expression.Lambda(body, bytes);
|
||||
}
|
||||
|
||||
private static Func<object, object> Compile(LambdaExpression convertExpression)
|
||||
{
|
||||
var compiled = convertExpression.Compile();
|
||||
|
||||
return x => x != null
|
||||
? compiled.DynamicInvoke(x)
|
||||
: null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.TestUtilities;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
|
@ -12,10 +13,17 @@ namespace Microsoft.EntityFrameworkCore.Query
|
|||
protected override ITestStoreFactory TestStoreFactory
|
||||
=> SqlServerTestStoreFactory.Instance;
|
||||
|
||||
// TODO: Use UseNetTopologySuite instead?
|
||||
protected override IServiceCollection AddServices(IServiceCollection serviceCollection)
|
||||
=> base.AddServices(serviceCollection)
|
||||
.AddEntityFrameworkSqlServerNetTopologySuite();
|
||||
|
||||
public override DbContextOptionsBuilder AddOptions(DbContextOptionsBuilder builder)
|
||||
{
|
||||
var optionsBuilder = base.AddOptions(builder);
|
||||
new SqlServerDbContextOptionsBuilder(optionsBuilder).UseNetTopologySuite();
|
||||
|
||||
return optionsBuilder;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.TestUtilities;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
|
@ -12,10 +13,17 @@ namespace Microsoft.EntityFrameworkCore
|
|||
protected override ITestStoreFactory TestStoreFactory
|
||||
=> SqlServerTestStoreFactory.Instance;
|
||||
|
||||
// TODO: Use UseNetTopologySuite instead?
|
||||
protected override IServiceCollection AddServices(IServiceCollection serviceCollection)
|
||||
=> base.AddServices(serviceCollection)
|
||||
.AddEntityFrameworkSqlServerNetTopologySuite();
|
||||
|
||||
public override DbContextOptionsBuilder AddOptions(DbContextOptionsBuilder builder)
|
||||
{
|
||||
var optionsBuilder = base.AddOptions(builder);
|
||||
new SqlServerDbContextOptionsBuilder(optionsBuilder).UseNetTopologySuite();
|
||||
|
||||
return optionsBuilder;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
</PropertyGroup>
|
||||
|
||||
<ItemGroup Condition="'$(FunctionalTests_PackageVersion)' == '0.0.0'">
|
||||
<ProjectReference Include="..\..\src\EFCore.Sqlite.Core\EFCore.Sqlite.Core.csproj" />
|
||||
<ProjectReference Include="..\..\src\EFCore.Sqlite.NTS\EFCore.Sqlite.NTS.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition="'$(FunctionalTests_PackageVersion)' != '0.0.0'">
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.TestUtilities;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Microsoft.EntityFrameworkCore.Query
|
||||
{
|
||||
#if !Test21
|
||||
public class SpatialQuerySqliteFixture : SpatialQueryRelationalFixture
|
||||
{
|
||||
protected override ITestStoreFactory TestStoreFactory
|
||||
=> SqliteTestStoreFactory.Instance;
|
||||
|
||||
protected override IServiceCollection AddServices(IServiceCollection serviceCollection)
|
||||
=> base.AddServices(serviceCollection)
|
||||
.AddEntityFrameworkSqliteNetTopologySuite();
|
||||
|
||||
public override DbContextOptionsBuilder AddOptions(DbContextOptionsBuilder builder)
|
||||
{
|
||||
var optionsBuilder = base.AddOptions(builder);
|
||||
new SqliteDbContextOptionsBuilder(optionsBuilder).UseNetTopologySuite();
|
||||
|
||||
return optionsBuilder;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
|
@ -0,0 +1,512 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.EntityFrameworkCore.TestUtilities;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace Microsoft.EntityFrameworkCore.Query
|
||||
{
|
||||
#if !Test21
|
||||
[SpatialiteRequired]
|
||||
public class SpatialQuerySqliteTest : SpatialQueryTestBase<SpatialQuerySqliteFixture>
|
||||
{
|
||||
public SpatialQuerySqliteTest(SpatialQuerySqliteFixture fixture, ITestOutputHelper testOutputHelper)
|
||||
: base(fixture)
|
||||
{
|
||||
Fixture.TestSqlLoggerFactory.Clear();
|
||||
//Fixture.TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper);
|
||||
}
|
||||
|
||||
public override async Task Area(bool isAsync)
|
||||
{
|
||||
await base.Area(isAsync);
|
||||
|
||||
AssertSql(
|
||||
@"SELECT ""e"".""Id"", Area(""e"".""Polygon"") AS ""Area""
|
||||
FROM ""PolygonEntity"" AS ""e""");
|
||||
}
|
||||
|
||||
public override async Task AsBinary(bool isAsync)
|
||||
{
|
||||
await base.AsBinary(isAsync);
|
||||
|
||||
AssertSql(
|
||||
@"SELECT ""e"".""Id"", AsBinary(""e"".""Point"") AS ""Binary""
|
||||
FROM ""PointEntity"" AS ""e""");
|
||||
}
|
||||
|
||||
public override async Task AsText(bool isAsync)
|
||||
{
|
||||
await base.AsText(isAsync);
|
||||
|
||||
AssertSql(
|
||||
@"SELECT ""e"".""Id"", AsText(""e"".""Point"") AS ""Text""
|
||||
FROM ""PointEntity"" AS ""e""");
|
||||
}
|
||||
|
||||
public override async Task Boundary(bool isAsync)
|
||||
{
|
||||
await base.Boundary(isAsync);
|
||||
|
||||
AssertSql(
|
||||
@"SELECT ""e"".""Id"", Boundary(""e"".""Polygon"") AS ""Boundary""
|
||||
FROM ""PolygonEntity"" AS ""e""");
|
||||
}
|
||||
|
||||
public override async Task Buffer(bool isAsync)
|
||||
{
|
||||
await base.Buffer(isAsync);
|
||||
|
||||
AssertSql(
|
||||
@"SELECT ""e"".""Id"", Buffer(""e"".""Polygon"", 1.0) AS ""Buffer""
|
||||
FROM ""PolygonEntity"" AS ""e""");
|
||||
}
|
||||
|
||||
public override async Task Buffer_quadrantSegments(bool isAsync)
|
||||
{
|
||||
await base.Buffer_quadrantSegments(isAsync);
|
||||
|
||||
AssertSql(
|
||||
@"SELECT ""e"".""Id"", Buffer(""e"".""Polygon"", 1.0, 8) AS ""Buffer""
|
||||
FROM ""PolygonEntity"" AS ""e""");
|
||||
}
|
||||
|
||||
public override async Task Centroid(bool isAsync)
|
||||
{
|
||||
await base.Centroid(isAsync);
|
||||
|
||||
AssertSql(
|
||||
@"SELECT ""e"".""Id"", Centroid(""e"".""Polygon"") AS ""Centroid""
|
||||
FROM ""PolygonEntity"" AS ""e""");
|
||||
}
|
||||
|
||||
public override async Task Contains(bool isAsync)
|
||||
{
|
||||
await base.Contains(isAsync);
|
||||
|
||||
AssertSql(
|
||||
@"SELECT ""e"".""Id"", Contains(""e"".""Polygon"", GeomFromText('POINT (0.5 0.25)')) AS ""Contains""
|
||||
FROM ""PolygonEntity"" AS ""e""");
|
||||
}
|
||||
|
||||
public override async Task ConvexHull(bool isAsync)
|
||||
{
|
||||
await base.ConvexHull(isAsync);
|
||||
|
||||
AssertSql(
|
||||
@"SELECT ""e"".""Id"", ConvexHull(""e"".""Polygon"") AS ""ConvexHull""
|
||||
FROM ""PolygonEntity"" AS ""e""");
|
||||
}
|
||||
|
||||
public override async Task IGeometryCollection_Count(bool isAsync)
|
||||
{
|
||||
await base.IGeometryCollection_Count(isAsync);
|
||||
|
||||
AssertSql(
|
||||
@"SELECT ""e"".""Id"", NumGeometries(""e"".""MultiLineString"") AS ""Count""
|
||||
FROM ""MultiLineStringEntity"" AS ""e""");
|
||||
}
|
||||
|
||||
public override async Task LineString_Count(bool isAsync)
|
||||
{
|
||||
await base.LineString_Count(isAsync);
|
||||
|
||||
AssertSql(
|
||||
@"SELECT ""e"".""Id"", NumPoints(""e"".""LineString"") AS ""Count""
|
||||
FROM ""LineStringEntity"" AS ""e""");
|
||||
}
|
||||
|
||||
public override async Task CoveredBy(bool isAsync)
|
||||
{
|
||||
await base.CoveredBy(isAsync);
|
||||
|
||||
AssertSql(
|
||||
@"SELECT ""e"".""Id"", CoveredBy(""e"".""Point"", GeomFromText('POLYGON ((-1 -1, -1 2, 2 2, 2 -1, -1 -1))')) AS ""CoveredBy""
|
||||
FROM ""PointEntity"" AS ""e""");
|
||||
}
|
||||
|
||||
public override async Task Covers(bool isAsync)
|
||||
{
|
||||
await base.Covers(isAsync);
|
||||
|
||||
AssertSql(
|
||||
@"SELECT ""e"".""Id"", Covers(""e"".""Polygon"", GeomFromText('POINT (0.5 0.25)')) AS ""Covers""
|
||||
FROM ""PolygonEntity"" AS ""e""");
|
||||
}
|
||||
|
||||
public override async Task Crosses(bool isAsync)
|
||||
{
|
||||
await base.Crosses(isAsync);
|
||||
|
||||
AssertSql(
|
||||
@"SELECT ""e"".""Id"", Crosses(""e"".""LineString"", GeomFromText('LINESTRING (0.5 -0.5, 0.5 0.5)')) AS ""Crosses""
|
||||
FROM ""LineStringEntity"" AS ""e""");
|
||||
}
|
||||
|
||||
public override async Task Difference(bool isAsync)
|
||||
{
|
||||
await base.Difference(isAsync);
|
||||
|
||||
AssertSql(
|
||||
@"SELECT ""e"".""Id"", Difference(""e"".""Polygon"", GeomFromText('POLYGON ((0 0, 1 0, 1 1, 0 0))')) AS ""Difference""
|
||||
FROM ""PolygonEntity"" AS ""e""");
|
||||
}
|
||||
|
||||
public override async Task Dimension(bool isAsync)
|
||||
{
|
||||
await base.Dimension(isAsync);
|
||||
|
||||
AssertSql(
|
||||
@"SELECT ""e"".""Id"", Dimension(""e"".""Point"") AS ""Dimension""
|
||||
FROM ""PointEntity"" AS ""e""");
|
||||
}
|
||||
|
||||
public override async Task Disjoint(bool isAsync)
|
||||
{
|
||||
await base.Disjoint(isAsync);
|
||||
|
||||
AssertSql(
|
||||
@"SELECT ""e"".""Id"", Disjoint(""e"".""Polygon"", GeomFromText('POINT (1 0)')) AS ""Disjoint""
|
||||
FROM ""PolygonEntity"" AS ""e""");
|
||||
}
|
||||
|
||||
public override async Task Distance(bool isAsync)
|
||||
{
|
||||
await base.Distance(isAsync);
|
||||
|
||||
AssertSql(
|
||||
@"SELECT ""e"".""Id"", Distance(""e"".""Point"", GeomFromText('POINT (0 1)')) AS ""Distance""
|
||||
FROM ""PointEntity"" AS ""e""");
|
||||
}
|
||||
|
||||
public override async Task EndPoint(bool isAsync)
|
||||
{
|
||||
await base.EndPoint(isAsync);
|
||||
|
||||
AssertSql(
|
||||
@"SELECT ""e"".""Id"", EndPoint(""e"".""LineString"") AS ""EndPoint""
|
||||
FROM ""LineStringEntity"" AS ""e""");
|
||||
}
|
||||
|
||||
public override async Task Envelope(bool isAsync)
|
||||
{
|
||||
await base.Envelope(isAsync);
|
||||
|
||||
AssertSql(
|
||||
@"SELECT ""e"".""Id"", Envelope(""e"".""Polygon"") AS ""Envelope""
|
||||
FROM ""PolygonEntity"" AS ""e""");
|
||||
}
|
||||
|
||||
public override async Task EqualsTopologically(bool isAsync)
|
||||
{
|
||||
await base.EqualsTopologically(isAsync);
|
||||
|
||||
AssertSql(
|
||||
@"SELECT ""e"".""Id"", Equals(""e"".""Point"", GeomFromText('POINT (0 0)')) AS ""EqualsTopologically""
|
||||
FROM ""PointEntity"" AS ""e""");
|
||||
}
|
||||
|
||||
public override async Task ExteriorRing(bool isAsync)
|
||||
{
|
||||
await base.ExteriorRing(isAsync);
|
||||
|
||||
AssertSql(
|
||||
@"SELECT ""e"".""Id"", ExteriorRing(""e"".""Polygon"") AS ""ExteriorRing""
|
||||
FROM ""PolygonEntity"" AS ""e""");
|
||||
}
|
||||
|
||||
public override async Task GetGeometryN(bool isAsync)
|
||||
{
|
||||
await base.GetGeometryN(isAsync);
|
||||
|
||||
AssertSql(
|
||||
@"SELECT ""e"".""Id"", GeometryN(""e"".""MultiLineString"", 0 + 1) AS ""Geometry0""
|
||||
FROM ""MultiLineStringEntity"" AS ""e""");
|
||||
}
|
||||
|
||||
public override async Task GetInteriorRingN(bool isAsync)
|
||||
{
|
||||
await base.GetInteriorRingN(isAsync);
|
||||
|
||||
AssertSql(
|
||||
@"SELECT ""e"".""Id"", InteriorRingN(""e"".""Polygon"", 0 + 1) AS ""InteriorRing0""
|
||||
FROM ""PolygonEntity"" AS ""e""
|
||||
WHERE NumInteriorRing(""e"".""Polygon"") > 0");
|
||||
}
|
||||
|
||||
public override async Task GetPointN(bool isAsync)
|
||||
{
|
||||
await base.GetPointN(isAsync);
|
||||
|
||||
AssertSql(
|
||||
@"SELECT ""e"".""Id"", PointN(""e"".""LineString"", 0 + 1) AS ""Point0""
|
||||
FROM ""LineStringEntity"" AS ""e""");
|
||||
}
|
||||
|
||||
public override async Task Intersection(bool isAsync)
|
||||
{
|
||||
await base.Intersection(isAsync);
|
||||
|
||||
AssertSql(
|
||||
@"SELECT ""e"".""Id"", Intersection(""e"".""Polygon"", GeomFromText('POLYGON ((0 0, 1 0, 1 1, 0 0))')) AS ""Intersection""
|
||||
FROM ""PolygonEntity"" AS ""e""");
|
||||
}
|
||||
|
||||
public override async Task Intersects(bool isAsync)
|
||||
{
|
||||
await base.Intersects(isAsync);
|
||||
|
||||
AssertSql(
|
||||
@"SELECT ""e"".""Id"", Intersects(""e"".""LineString"", GeomFromText('LINESTRING (0.5 -0.5, 0.5 0.5)')) AS ""Intersects""
|
||||
FROM ""LineStringEntity"" AS ""e""");
|
||||
}
|
||||
|
||||
public override async Task ICurve_IsClosed(bool isAsync)
|
||||
{
|
||||
await base.ICurve_IsClosed(isAsync);
|
||||
|
||||
AssertSql(
|
||||
@"SELECT ""e"".""Id"", IsClosed(""e"".""LineString"") AS ""IsClosed""
|
||||
FROM ""LineStringEntity"" AS ""e""");
|
||||
}
|
||||
|
||||
public override async Task IMultiCurve_IsClosed(bool isAsync)
|
||||
{
|
||||
await base.IMultiCurve_IsClosed(isAsync);
|
||||
|
||||
AssertSql(
|
||||
@"SELECT ""e"".""Id"", IsClosed(""e"".""MultiLineString"") AS ""IsClosed""
|
||||
FROM ""MultiLineStringEntity"" AS ""e""");
|
||||
}
|
||||
|
||||
public override async Task IsEmpty(bool isAsync)
|
||||
{
|
||||
await base.IsEmpty(isAsync);
|
||||
|
||||
AssertSql(
|
||||
@"SELECT ""e"".""Id"", IsEmpty(""e"".""MultiLineString"") AS ""IsEmpty""
|
||||
FROM ""MultiLineStringEntity"" AS ""e""");
|
||||
}
|
||||
|
||||
public override async Task IsRing(bool isAsync)
|
||||
{
|
||||
await base.IsRing(isAsync);
|
||||
|
||||
AssertSql(
|
||||
@"SELECT ""e"".""Id"", IsRing(""e"".""LineString"") AS ""IsRing""
|
||||
FROM ""LineStringEntity"" AS ""e""");
|
||||
}
|
||||
|
||||
public override async Task IsSimple(bool isAsync)
|
||||
{
|
||||
await base.IsSimple(isAsync);
|
||||
|
||||
AssertSql(
|
||||
@"SELECT ""e"".""Id"", IsSimple(""e"".""LineString"") AS ""IsSimple""
|
||||
FROM ""LineStringEntity"" AS ""e""");
|
||||
}
|
||||
|
||||
public override async Task IsValid(bool isAsync)
|
||||
{
|
||||
await base.IsValid(isAsync);
|
||||
|
||||
AssertSql(
|
||||
@"SELECT ""e"".""Id"", IsValid(""e"".""Point"") AS ""IsValid""
|
||||
FROM ""PointEntity"" AS ""e""");
|
||||
}
|
||||
|
||||
public override async Task Item(bool isAsync)
|
||||
{
|
||||
await base.Item(isAsync);
|
||||
|
||||
AssertSql(
|
||||
@"SELECT ""e"".""Id"", GeometryN(""e"".""MultiLineString"", 0 + 1) AS ""Item0""
|
||||
FROM ""MultiLineStringEntity"" AS ""e""");
|
||||
}
|
||||
|
||||
public override async Task Length(bool isAsync)
|
||||
{
|
||||
await base.Length(isAsync);
|
||||
|
||||
AssertSql(
|
||||
@"SELECT ""e"".""Id"", GLength(""e"".""LineString"") AS ""Length""
|
||||
FROM ""LineStringEntity"" AS ""e""");
|
||||
}
|
||||
|
||||
public override async Task M(bool isAsync)
|
||||
{
|
||||
await base.M(isAsync);
|
||||
|
||||
AssertSql(
|
||||
@"SELECT ""e"".""Id"", M(""e"".""Point"") AS ""M""
|
||||
FROM ""PointEntity"" AS ""e""");
|
||||
}
|
||||
|
||||
public override async Task NumGeometries(bool isAsync)
|
||||
{
|
||||
await base.NumGeometries(isAsync);
|
||||
|
||||
AssertSql(
|
||||
@"SELECT ""e"".""Id"", NumGeometries(""e"".""MultiLineString"") AS ""NumGeometries""
|
||||
FROM ""MultiLineStringEntity"" AS ""e""");
|
||||
}
|
||||
|
||||
public override async Task NumInteriorRings(bool isAsync)
|
||||
{
|
||||
await base.NumInteriorRings(isAsync);
|
||||
|
||||
AssertSql(
|
||||
@"SELECT ""e"".""Id"", NumInteriorRing(""e"".""Polygon"") AS ""NumInteriorRings""
|
||||
FROM ""PolygonEntity"" AS ""e""");
|
||||
}
|
||||
|
||||
public override async Task NumPoints(bool isAsync)
|
||||
{
|
||||
await base.NumPoints(isAsync);
|
||||
|
||||
AssertSql(
|
||||
@"SELECT ""e"".""Id"", NumPoints(""e"".""LineString"") AS ""NumPoints""
|
||||
FROM ""LineStringEntity"" AS ""e""");
|
||||
}
|
||||
|
||||
public override async Task Overlaps(bool isAsync)
|
||||
{
|
||||
await base.Overlaps(isAsync);
|
||||
|
||||
AssertSql(
|
||||
@"SELECT ""e"".""Id"", Overlaps(""e"".""Polygon"", GeomFromText('POLYGON ((0 0, 1 0, 1 1, 0 0))')) AS ""Overlaps""
|
||||
FROM ""PolygonEntity"" AS ""e""");
|
||||
}
|
||||
|
||||
public override async Task PointOnSurface(bool isAsync)
|
||||
{
|
||||
await base.PointOnSurface(isAsync);
|
||||
|
||||
AssertSql(
|
||||
@"SELECT ""e"".""Id"", PointOnSurface(""e"".""Polygon"") AS ""PointOnSurface"", ""e"".""Polygon""
|
||||
FROM ""PolygonEntity"" AS ""e""");
|
||||
}
|
||||
|
||||
public override async Task Relate(bool isAsync)
|
||||
{
|
||||
await base.Relate(isAsync);
|
||||
|
||||
AssertSql(
|
||||
@"SELECT ""e"".""Id"", Relate(""e"".""Polygon"", GeomFromText('POLYGON ((0 0, 1 0, 1 1, 0 0))'), '212111212') AS ""Relate""
|
||||
FROM ""PolygonEntity"" AS ""e""");
|
||||
}
|
||||
|
||||
public override async Task Reverse(bool isAsync)
|
||||
{
|
||||
await base.Reverse(isAsync);
|
||||
|
||||
AssertSql(
|
||||
@"SELECT ""e"".""Id"", ST_Reverse(""e"".""LineString"") AS ""Reverse""
|
||||
FROM ""LineStringEntity"" AS ""e""");
|
||||
}
|
||||
|
||||
public override async Task SRID(bool isAsync)
|
||||
{
|
||||
await base.SRID(isAsync);
|
||||
|
||||
AssertSql(
|
||||
@"SELECT ""e"".""Id"", SRID(""e"".""Point"") AS ""SRID""
|
||||
FROM ""PointEntity"" AS ""e""");
|
||||
}
|
||||
|
||||
public override async Task StartPoint(bool isAsync)
|
||||
{
|
||||
await base.StartPoint(isAsync);
|
||||
|
||||
AssertSql(
|
||||
@"SELECT ""e"".""Id"", StartPoint(""e"".""LineString"") AS ""StartPoint""
|
||||
FROM ""LineStringEntity"" AS ""e""");
|
||||
}
|
||||
|
||||
public override async Task SymmetricDifference(bool isAsync)
|
||||
{
|
||||
await base.SymmetricDifference(isAsync);
|
||||
|
||||
AssertSql(
|
||||
@"SELECT ""e"".""Id"", SymDifference(""e"".""Polygon"", GeomFromText('POLYGON ((0 0, 1 0, 1 1, 0 0))')) AS ""SymmetricDifference""
|
||||
FROM ""PolygonEntity"" AS ""e""");
|
||||
}
|
||||
|
||||
public override async Task ToBinary(bool isAsync)
|
||||
{
|
||||
await base.ToBinary(isAsync);
|
||||
|
||||
AssertSql(
|
||||
@"SELECT ""e"".""Id"", AsBinary(""e"".""Point"") AS ""Binary""
|
||||
FROM ""PointEntity"" AS ""e""");
|
||||
}
|
||||
|
||||
public override async Task ToText(bool isAsync)
|
||||
{
|
||||
await base.ToText(isAsync);
|
||||
|
||||
AssertSql(
|
||||
@"SELECT ""e"".""Id"", AsText(""e"".""Point"") AS ""Text""
|
||||
FROM ""PointEntity"" AS ""e""");
|
||||
}
|
||||
|
||||
public override async Task Touches(bool isAsync)
|
||||
{
|
||||
await base.Touches(isAsync);
|
||||
|
||||
AssertSql(
|
||||
@"SELECT ""e"".""Id"", Touches(""e"".""Polygon"", GeomFromText('POLYGON ((0 1, 1 1, 1 0, 0 1))')) AS ""Touches""
|
||||
FROM ""PolygonEntity"" AS ""e""");
|
||||
}
|
||||
|
||||
public override async Task Union(bool isAsync)
|
||||
{
|
||||
await base.Union(isAsync);
|
||||
|
||||
AssertSql(
|
||||
@"SELECT ""e"".""Id"", GUnion(""e"".""Polygon"", GeomFromText('POLYGON ((0 0, 1 0, 1 1, 0 0))')) AS ""Union""
|
||||
FROM ""PolygonEntity"" AS ""e""");
|
||||
}
|
||||
|
||||
public override async Task Within(bool isAsync)
|
||||
{
|
||||
await base.Within(isAsync);
|
||||
|
||||
AssertSql(
|
||||
@"SELECT ""e"".""Id"", Within(""e"".""Point"", GeomFromText('POLYGON ((-1 -1, -1 2, 2 2, 2 -1, -1 -1))')) AS ""Within""
|
||||
FROM ""PointEntity"" AS ""e""");
|
||||
}
|
||||
|
||||
public override async Task X(bool isAsync)
|
||||
{
|
||||
await base.X(isAsync);
|
||||
|
||||
AssertSql(
|
||||
@"SELECT ""e"".""Id"", X(""e"".""Point"") AS ""X""
|
||||
FROM ""PointEntity"" AS ""e""");
|
||||
}
|
||||
|
||||
public override async Task Y(bool isAsync)
|
||||
{
|
||||
await base.Y(isAsync);
|
||||
|
||||
AssertSql(
|
||||
@"SELECT ""e"".""Id"", Y(""e"".""Point"") AS ""Y""
|
||||
FROM ""PointEntity"" AS ""e""");
|
||||
}
|
||||
|
||||
public override async Task Z(bool isAsync)
|
||||
{
|
||||
await base.Z(isAsync);
|
||||
|
||||
AssertSql(
|
||||
@"SELECT ""e"".""Id"", Z(""e"".""Point"") AS ""Z""
|
||||
FROM ""PointEntity"" AS ""e""");
|
||||
}
|
||||
|
||||
private void AssertSql(params string[] expected)
|
||||
=> Fixture.TestSqlLoggerFactory.AssertBaseline(expected);
|
||||
}
|
||||
#endif
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.TestUtilities;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Microsoft.EntityFrameworkCore
|
||||
{
|
||||
#if !Test21
|
||||
public class SpatialSqliteFixture : SpatialFixtureBase
|
||||
{
|
||||
protected override ITestStoreFactory TestStoreFactory
|
||||
=> SqliteTestStoreFactory.Instance;
|
||||
|
||||
protected override IServiceCollection AddServices(IServiceCollection serviceCollection)
|
||||
=> base.AddServices(serviceCollection)
|
||||
.AddEntityFrameworkSqliteNetTopologySuite();
|
||||
|
||||
public override DbContextOptionsBuilder AddOptions(DbContextOptionsBuilder builder)
|
||||
{
|
||||
var optionsBuilder = base.AddOptions(builder);
|
||||
new SqliteDbContextOptionsBuilder(optionsBuilder).UseNetTopologySuite();
|
||||
|
||||
return optionsBuilder;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using Microsoft.EntityFrameworkCore.TestUtilities;
|
||||
|
||||
namespace Microsoft.EntityFrameworkCore
|
||||
{
|
||||
#if !Test21
|
||||
[SpatialiteRequired]
|
||||
public class SpatialSqliteTest : SpatialTestBase<SpatialSqliteFixture>
|
||||
{
|
||||
public SpatialSqliteTest(SpatialSqliteFixture fixture)
|
||||
: base(fixture)
|
||||
{
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
|
@ -15,11 +15,7 @@ namespace Microsoft.EntityFrameworkCore
|
|||
typeof(AsyncFromSqlSprocQueryTestBase<>),
|
||||
typeof(FromSqlSprocQueryTestBase<>),
|
||||
typeof(SqlExecutorTestBase<>),
|
||||
typeof(UdfDbFunctionTestBase<>),
|
||||
#if !Test21
|
||||
typeof(SpatialQueryTestBase<>),
|
||||
typeof(SpatialTestBase<>)
|
||||
#endif
|
||||
typeof(UdfDbFunctionTestBase<>)
|
||||
};
|
||||
|
||||
protected override Assembly TargetAssembly { get; } = typeof(SqliteComplianceTest).Assembly;
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
using System;
|
||||
using Microsoft.Data.Sqlite;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.TestUtilities.Xunit;
|
||||
|
||||
namespace Microsoft.EntityFrameworkCore.TestUtilities
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class)]
|
||||
public sealed class SpatialiteRequiredAttribute : Attribute, ITestCondition
|
||||
{
|
||||
private static readonly Lazy<bool> _loaded
|
||||
= new Lazy<bool>(
|
||||
() =>
|
||||
{
|
||||
using (var connection = new SqliteConnection("Data Source=:memory:"))
|
||||
{
|
||||
connection.Open();
|
||||
connection.EnableExtensions();
|
||||
|
||||
return SpatialiteLoader.TryLoad(connection);
|
||||
}
|
||||
});
|
||||
|
||||
public bool IsMet
|
||||
=> _loaded.Value;
|
||||
|
||||
public string SkipReason
|
||||
=> "mod_spatialite not found. Install it to run this test.";
|
||||
}
|
||||
}
|
|
@ -4,6 +4,7 @@
|
|||
using System;
|
||||
using System.Data.Common;
|
||||
using Microsoft.Data.Sqlite;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
|
||||
namespace Microsoft.EntityFrameworkCore.TestUtilities
|
||||
{
|
||||
|
@ -76,6 +77,9 @@ namespace Microsoft.EntityFrameworkCore.TestUtilities
|
|||
{
|
||||
Connection.Open();
|
||||
|
||||
((SqliteConnection)Connection).EnableExtensions();
|
||||
SpatialiteLoader.TryLoad(Connection);
|
||||
|
||||
using (var command = Connection.CreateCommand())
|
||||
{
|
||||
command.CommandText = "PRAGMA foreign_keys=ON;";
|
||||
|
|
Загрузка…
Ссылка в новой задаче