diff --git a/EntityFrameworkCore.ClickHouse.FunctionalTests/EntityFrameworkCore.ClickHouse.FunctionalTests.csproj b/EntityFrameworkCore.ClickHouse.FunctionalTests/EntityFrameworkCore.ClickHouse.FunctionalTests.csproj
index 2dee2fa..fde71bc 100644
--- a/EntityFrameworkCore.ClickHouse.FunctionalTests/EntityFrameworkCore.ClickHouse.FunctionalTests.csproj
+++ b/EntityFrameworkCore.ClickHouse.FunctionalTests/EntityFrameworkCore.ClickHouse.FunctionalTests.csproj
@@ -7,6 +7,7 @@
+
diff --git a/EntityFrameworkCore.ClickHouse.FunctionalTests/Migrations/ClickHouseMigrationsSqlGeneratorTest.cs b/EntityFrameworkCore.ClickHouse.FunctionalTests/Migrations/ClickHouseMigrationsSqlGeneratorTest.cs
index 02d5e8b..a18c917 100644
--- a/EntityFrameworkCore.ClickHouse.FunctionalTests/Migrations/ClickHouseMigrationsSqlGeneratorTest.cs
+++ b/EntityFrameworkCore.ClickHouse.FunctionalTests/Migrations/ClickHouseMigrationsSqlGeneratorTest.cs
@@ -15,7 +15,7 @@ public class ClickHouseMigrationsSqlGeneratorTest : MigrationsSqlGeneratorTestBa
public ClickHouseMigrationsSqlGeneratorTest()
: base(
ClickHouseTestHelpers.Instance,
- new ServiceCollection()/*.AddEntityFrameworkClickHouseNetTopologySuite()*/,
+ new ServiceCollection().AddEntityFrameworkClickHouseNetTopologySuite(),
ClickHouseTestHelpers.Instance.AddProviderOptions(
((IRelationalDbContextOptionsBuilderInfrastructure)
new ClickHouseDbContextOptionsBuilder(new DbContextOptionsBuilder())/*.UseNetTopologySuite()*/)
diff --git a/EntityFrameworkCore.ClickHouse.FunctionalTests/Query/SpatialQueryClickHouseFixture.cs b/EntityFrameworkCore.ClickHouse.FunctionalTests/Query/SpatialQueryClickHouseFixture.cs
new file mode 100644
index 0000000..3ca58b0
--- /dev/null
+++ b/EntityFrameworkCore.ClickHouse.FunctionalTests/Query/SpatialQueryClickHouseFixture.cs
@@ -0,0 +1,47 @@
+using ClickHouse.EntityFrameworkCore.Infrastructure;
+using EntityFrameworkCore.ClickHouse.FunctionalTests.TestUtilities;
+using EntityFrameworkCore.ClickHouse.NTS.Extensions;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Query;
+using Microsoft.EntityFrameworkCore.TestModels.SpatialModel;
+using Microsoft.EntityFrameworkCore.TestUtilities;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace EntityFrameworkCore.ClickHouse.FunctionalTests.Query;
+
+public class SpatialQueryClickHouseFixture : SpatialQueryRelationalFixture
+{
+ protected override ITestStoreFactory TestStoreFactory
+ => ClickHouseTestStoreFactory.Instance;
+
+ protected override IServiceCollection AddServices(IServiceCollection serviceCollection)
+ => base.AddServices(serviceCollection)
+ .AddEntityFrameworkClickHouseNetTopologySuite();
+
+ public override DbContextOptionsBuilder AddOptions(DbContextOptionsBuilder builder)
+ {
+ var optionsBuilder = base.AddOptions(builder);
+ new ClickHouseDbContextOptionsBuilder(optionsBuilder).UseNetTopologySuite();
+
+ return optionsBuilder;
+ }
+
+ protected override void OnModelCreating(ModelBuilder modelBuilder, DbContext context)
+ {
+ base.OnModelCreating(modelBuilder, context);
+
+ modelBuilder.Entity().Property(e => e.LineString).HasColumnType("Geometry");
+ modelBuilder.Entity().Property(e => e.MultiLineString).HasColumnType("Geometry");
+ modelBuilder.Entity(
+ x =>
+ {
+ x.Ignore(e => e.Geometry);
+ x.Property(e => e.Point).HasColumnType("Point").IsRequired();
+ x.Property(e => e.PointZ).HasColumnType("Point").IsRequired();
+ x.Property(e => e.PointM).HasColumnType("Point").IsRequired();
+ x.Property(e => e.PointZM).HasColumnType("Point").IsRequired();
+ });
+ modelBuilder.Entity().Property(e => e.Polygon).HasColumnType("Geometry");
+ modelBuilder.Entity().Property(e => e.Location).HasColumnType("Point");
+ }
+}
diff --git a/EntityFrameworkCore.ClickHouse.FunctionalTests/Query/SpatialQueryClickHouseTest.cs b/EntityFrameworkCore.ClickHouse.FunctionalTests/Query/SpatialQueryClickHouseTest.cs
new file mode 100644
index 0000000..aabc986
--- /dev/null
+++ b/EntityFrameworkCore.ClickHouse.FunctionalTests/Query/SpatialQueryClickHouseTest.cs
@@ -0,0 +1,14 @@
+using Microsoft.EntityFrameworkCore.Query;
+using Xunit.Abstractions;
+
+namespace EntityFrameworkCore.ClickHouse.FunctionalTests.Query;
+
+public class SpatialQueryClickHouseTest : SpatialQueryRelationalTestBase
+{
+ public SpatialQueryClickHouseTest(SpatialQueryClickHouseFixture fixture, ITestOutputHelper testOutputHelper)
+ : base(fixture)
+ {
+ Fixture.TestSqlLoggerFactory.Clear();
+ Fixture.TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper);
+ }
+}
diff --git a/EntityFrameworkCore.ClickHouse.NTS/Design/Internal/ClickHouseNetTopologySuiteDesignTimeServices.cs b/EntityFrameworkCore.ClickHouse.NTS/Design/Internal/ClickHouseNetTopologySuiteDesignTimeServices.cs
new file mode 100644
index 0000000..4a10610
--- /dev/null
+++ b/EntityFrameworkCore.ClickHouse.NTS/Design/Internal/ClickHouseNetTopologySuiteDesignTimeServices.cs
@@ -0,0 +1,19 @@
+using EntityFrameworkCore.ClickHouse.NTS.Scaffolding.Internal;
+using EntityFrameworkCore.ClickHouse.NTS.Storage.Internal;
+using Microsoft.EntityFrameworkCore.Design;
+using Microsoft.EntityFrameworkCore.Scaffolding;
+using Microsoft.EntityFrameworkCore.Storage;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.DependencyInjection.Extensions;
+using NetTopologySuite;
+
+namespace EntityFrameworkCore.ClickHouse.NTS.Design.Internal;
+
+public class ClickHouseNetTopologySuiteDesignTimeServices : IDesignTimeServices
+{
+ public virtual void ConfigureDesignTimeServices(IServiceCollection serviceCollection)
+ => serviceCollection
+ .AddSingleton()
+ .AddSingleton()
+ .TryAddSingleton(NtsGeometryServices.Instance);
+}
diff --git a/EntityFrameworkCore.ClickHouse.NTS/EntityFrameworkCore.ClickHouse.NTS.csproj b/EntityFrameworkCore.ClickHouse.NTS/EntityFrameworkCore.ClickHouse.NTS.csproj
new file mode 100644
index 0000000..973c29b
--- /dev/null
+++ b/EntityFrameworkCore.ClickHouse.NTS/EntityFrameworkCore.ClickHouse.NTS.csproj
@@ -0,0 +1,26 @@
+
+
+
+ net10.0
+ enable
+ enable
+
+
+
+
+
+
+
+
+
+
+
+
+ Extensions\ClickHouseDbParameterExtensions.cs
+
+
+ Extensions\StringExtensions.cs
+
+
+
+
diff --git a/EntityFrameworkCore.ClickHouse.NTS/Extensions/ClickHouseNetTopologySuiteDbContextOptionsBuilderExtensions.cs b/EntityFrameworkCore.ClickHouse.NTS/Extensions/ClickHouseNetTopologySuiteDbContextOptionsBuilderExtensions.cs
new file mode 100644
index 0000000..2d68270
--- /dev/null
+++ b/EntityFrameworkCore.ClickHouse.NTS/Extensions/ClickHouseNetTopologySuiteDbContextOptionsBuilderExtensions.cs
@@ -0,0 +1,21 @@
+using ClickHouse.EntityFrameworkCore.Infrastructure;
+using EntityFrameworkCore.ClickHouse.NTS.Infrastructure.Internal;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+
+namespace EntityFrameworkCore.ClickHouse.NTS.Extensions;
+
+public static class ClickHouseNetTopologySuiteDbContextOptionsBuilderExtensions
+{
+ public static ClickHouseDbContextOptionsBuilder UseNetTopologySuite(
+ this ClickHouseDbContextOptionsBuilder optionsBuilder)
+ {
+ var coreOptionsBuilder = ((IRelationalDbContextOptionsBuilderInfrastructure)optionsBuilder).OptionsBuilder;
+
+ var extension = coreOptionsBuilder.Options.FindExtension()
+ ?? new ClickHouseNetTopologySuiteOptionsExtension();
+
+ ((IDbContextOptionsBuilderInfrastructure)coreOptionsBuilder).AddOrUpdateExtension(extension);
+
+ return optionsBuilder;
+ }
+}
diff --git a/EntityFrameworkCore.ClickHouse.NTS/Extensions/ClickHouseNetTopologySuiteServiceCollectionExtensions.cs b/EntityFrameworkCore.ClickHouse.NTS/Extensions/ClickHouseNetTopologySuiteServiceCollectionExtensions.cs
new file mode 100644
index 0000000..208bf01
--- /dev/null
+++ b/EntityFrameworkCore.ClickHouse.NTS/Extensions/ClickHouseNetTopologySuiteServiceCollectionExtensions.cs
@@ -0,0 +1,28 @@
+using EntityFrameworkCore.ClickHouse.NTS.Query.Internal;
+using EntityFrameworkCore.ClickHouse.NTS.Storage.Internal;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Query;
+using Microsoft.EntityFrameworkCore.Storage;
+using Microsoft.Extensions.DependencyInjection.Extensions;
+using NetTopologySuite;
+
+// ReSharper disable once CheckNamespace
+namespace Microsoft.Extensions.DependencyInjection;
+
+public static class ClickHouseNetTopologySuiteServiceCollectionExtensions
+{
+ public static IServiceCollection AddEntityFrameworkClickHouseNetTopologySuite(
+ this IServiceCollection serviceCollection)
+ {
+ serviceCollection.TryAddSingleton(NtsGeometryServices.Instance);
+
+ new EntityFrameworkRelationalServicesBuilder(serviceCollection)
+ .TryAdd()
+ .TryAdd()
+ .TryAdd()
+ .TryAdd()
+ /*.TryAdd()*/;
+
+ return serviceCollection;
+ }
+}
diff --git a/EntityFrameworkCore.ClickHouse.NTS/Extensions/GeometryExtensions.cs b/EntityFrameworkCore.ClickHouse.NTS/Extensions/GeometryExtensions.cs
new file mode 100644
index 0000000..3808ff8
--- /dev/null
+++ b/EntityFrameworkCore.ClickHouse.NTS/Extensions/GeometryExtensions.cs
@@ -0,0 +1,42 @@
+using NetTopologySuite.Geometries;
+using System.Runtime.InteropServices.Swift;
+
+namespace EntityFrameworkCore.ClickHouse.NTS.Extensions;
+
+internal static class GeometryExtensions
+{
+ public static object ToClickHouse(this Geometry geometry)
+ {
+ return geometry.OgcGeometryType switch
+ {
+ OgcGeometryType.Point => ToClickHouse((Point)geometry),
+ OgcGeometryType.LineString => ToClickHouse((LineString)geometry),
+ OgcGeometryType.Polygon => throw new NotImplementedException(),
+ OgcGeometryType.MultiPoint => throw new NotImplementedException(),
+ OgcGeometryType.MultiLineString => ToClickHouse((MultiLineString)geometry),
+ OgcGeometryType.MultiPolygon => throw new NotImplementedException(),
+ OgcGeometryType.GeometryCollection => throw new NotImplementedException(),
+ OgcGeometryType.CircularString => throw new NotImplementedException(),
+ OgcGeometryType.CompoundCurve => throw new NotImplementedException(),
+ OgcGeometryType.CurvePolygon => throw new NotImplementedException(),
+ OgcGeometryType.MultiCurve => throw new NotImplementedException(),
+ OgcGeometryType.MultiSurface => throw new NotImplementedException(),
+ OgcGeometryType.Curve => throw new NotImplementedException(),
+ OgcGeometryType.Surface => throw new NotImplementedException(),
+ OgcGeometryType.PolyhedralSurface => throw new NotImplementedException(),
+ OgcGeometryType.TIN => throw new NotImplementedException(),
+ _ => throw new ArgumentOutOfRangeException()
+ };
+ }
+
+ public static Tuple ToClickHouse(Coordinate coordinate) => new(coordinate.X, coordinate.Y);
+
+ public static Tuple ToClickHouse(Point point) => new(point.X, point.Y);
+
+ public static Tuple[] ToClickHouse(LineString lineString) => lineString.Coordinates.Select(ToClickHouse).ToArray();
+
+ public static Tuple[][] ToClickHouse(MultiLineString multiLineString)
+ {
+ throw new NotImplementedException();
+ }
+}
diff --git a/EntityFrameworkCore.ClickHouse.NTS/Infrastructure/Internal/ClickHouseNetTopologySuiteOptionsExtension.cs b/EntityFrameworkCore.ClickHouse.NTS/Infrastructure/Internal/ClickHouseNetTopologySuiteOptionsExtension.cs
new file mode 100644
index 0000000..64634d7
--- /dev/null
+++ b/EntityFrameworkCore.ClickHouse.NTS/Infrastructure/Internal/ClickHouseNetTopologySuiteOptionsExtension.cs
@@ -0,0 +1,58 @@
+using EntityFrameworkCore.ClickHouse.NTS.Extensions;
+using EntityFrameworkCore.ClickHouse.NTS.Storage.Internal;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Storage;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace EntityFrameworkCore.ClickHouse.NTS.Infrastructure.Internal;
+
+public class ClickHouseNetTopologySuiteOptionsExtension : IDbContextOptionsExtension
+{
+ private DbContextOptionsExtensionInfo? _info;
+
+ public virtual void ApplyServices(IServiceCollection services)
+ => services.AddEntityFrameworkClickHouseNetTopologySuite();
+
+ public void Validate(IDbContextOptions options)
+ {
+ var internalServiceProvider = options.FindExtension()?.InternalServiceProvider;
+
+ if (internalServiceProvider != null)
+ {
+ using var scope = internalServiceProvider.CreateScope();
+ var plugins = scope.ServiceProvider.GetService>();
+ if (plugins?.Any(s => s is ClickHouseNetTopologySuiteTypeMappingSourcePlugin) != true)
+ {
+ throw new InvalidOperationException("UseNetTopologySuite requires AddEntityFrameworkClickHouseNetTopologySuite to be called on the internal service provider used.");
+ }
+ }
+ }
+
+ public DbContextOptionsExtensionInfo Info => _info ??= new ExtensionInfo(this);
+
+ private sealed class ExtensionInfo : DbContextOptionsExtensionInfo
+ {
+ public ExtensionInfo(IDbContextOptionsExtension extension)
+ : base(extension)
+ {
+ }
+
+ private new ClickHouseNetTopologySuiteOptionsExtension Extension
+ => (ClickHouseNetTopologySuiteOptionsExtension)base.Extension;
+
+ public override bool IsDatabaseProvider
+ => false;
+
+ public override int GetServiceProviderHashCode()
+ => 0;
+
+ public override bool ShouldUseSameServiceProvider(DbContextOptionsExtensionInfo other)
+ => other is ExtensionInfo;
+
+ public override void PopulateDebugInfo(IDictionary debugInfo)
+ => debugInfo["ClickHouse:" + nameof(ClickHouseNetTopologySuiteDbContextOptionsBuilderExtensions.UseNetTopologySuite)] = "1";
+
+ public override string LogFragment
+ => "using NetTopologySuite ";
+ }
+}
diff --git a/EntityFrameworkCore.ClickHouse.NTS/Query/Internal/ClickHouseGeometryCollectionMemberTranslator.cs b/EntityFrameworkCore.ClickHouse.NTS/Query/Internal/ClickHouseGeometryCollectionMemberTranslator.cs
new file mode 100644
index 0000000..b556bd7
--- /dev/null
+++ b/EntityFrameworkCore.ClickHouse.NTS/Query/Internal/ClickHouseGeometryCollectionMemberTranslator.cs
@@ -0,0 +1,15 @@
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Diagnostics;
+using Microsoft.EntityFrameworkCore.Query;
+using Microsoft.EntityFrameworkCore.Query.SqlExpressions;
+using System.Reflection;
+
+namespace EntityFrameworkCore.ClickHouse.NTS.Query.Internal;
+
+public class ClickHouseGeometryCollectionMemberTranslator : IMemberTranslator
+{
+ public SqlExpression? Translate(SqlExpression? instance, MemberInfo member, Type returnType, IDiagnosticsLogger logger)
+ {
+ return null;
+ }
+}
diff --git a/EntityFrameworkCore.ClickHouse.NTS/Query/Internal/ClickHouseGeometryCollectionMethodTranslator.cs b/EntityFrameworkCore.ClickHouse.NTS/Query/Internal/ClickHouseGeometryCollectionMethodTranslator.cs
new file mode 100644
index 0000000..0bb6d41
--- /dev/null
+++ b/EntityFrameworkCore.ClickHouse.NTS/Query/Internal/ClickHouseGeometryCollectionMethodTranslator.cs
@@ -0,0 +1,19 @@
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Diagnostics;
+using Microsoft.EntityFrameworkCore.Query;
+using Microsoft.EntityFrameworkCore.Query.SqlExpressions;
+using System.Reflection;
+
+namespace EntityFrameworkCore.ClickHouse.NTS.Query.Internal;
+
+public class ClickHouseGeometryCollectionMethodTranslator : IMethodCallTranslator
+{
+ public SqlExpression? Translate(
+ SqlExpression? instance,
+ MethodInfo method,
+ IReadOnlyList arguments,
+ IDiagnosticsLogger logger)
+ {
+ return null;
+ }
+}
diff --git a/EntityFrameworkCore.ClickHouse.NTS/Query/Internal/ClickHouseGeometryMemberTranslator.cs b/EntityFrameworkCore.ClickHouse.NTS/Query/Internal/ClickHouseGeometryMemberTranslator.cs
new file mode 100644
index 0000000..7d0fcbb
--- /dev/null
+++ b/EntityFrameworkCore.ClickHouse.NTS/Query/Internal/ClickHouseGeometryMemberTranslator.cs
@@ -0,0 +1,15 @@
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Diagnostics;
+using Microsoft.EntityFrameworkCore.Query;
+using Microsoft.EntityFrameworkCore.Query.SqlExpressions;
+using System.Reflection;
+
+namespace EntityFrameworkCore.ClickHouse.NTS.Query.Internal;
+
+public class ClickHouseGeometryMemberTranslator : IMemberTranslator
+{
+ public SqlExpression? Translate(SqlExpression? instance, MemberInfo member, Type returnType, IDiagnosticsLogger logger)
+ {
+ return null;
+ }
+}
diff --git a/EntityFrameworkCore.ClickHouse.NTS/Query/Internal/ClickHouseGeometryMethodTranslator.cs b/EntityFrameworkCore.ClickHouse.NTS/Query/Internal/ClickHouseGeometryMethodTranslator.cs
new file mode 100644
index 0000000..c03de54
--- /dev/null
+++ b/EntityFrameworkCore.ClickHouse.NTS/Query/Internal/ClickHouseGeometryMethodTranslator.cs
@@ -0,0 +1,19 @@
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Diagnostics;
+using Microsoft.EntityFrameworkCore.Query;
+using Microsoft.EntityFrameworkCore.Query.SqlExpressions;
+using System.Reflection;
+
+namespace EntityFrameworkCore.ClickHouse.NTS.Query.Internal;
+
+public class ClickHouseGeometryMethodTranslator : IMethodCallTranslator
+{
+ public SqlExpression? Translate(
+ SqlExpression? instance,
+ MethodInfo method,
+ IReadOnlyList arguments,
+ IDiagnosticsLogger logger)
+ {
+ return null;
+ }
+}
diff --git a/EntityFrameworkCore.ClickHouse.NTS/Query/Internal/ClickHouseLineStringMemberTranslator.cs b/EntityFrameworkCore.ClickHouse.NTS/Query/Internal/ClickHouseLineStringMemberTranslator.cs
new file mode 100644
index 0000000..dde2cfe
--- /dev/null
+++ b/EntityFrameworkCore.ClickHouse.NTS/Query/Internal/ClickHouseLineStringMemberTranslator.cs
@@ -0,0 +1,19 @@
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Diagnostics;
+using Microsoft.EntityFrameworkCore.Query;
+using Microsoft.EntityFrameworkCore.Query.SqlExpressions;
+using System.Reflection;
+
+namespace EntityFrameworkCore.ClickHouse.NTS.Query.Internal;
+
+public class ClickHouseLineStringMemberTranslator : IMemberTranslator
+{
+ public SqlExpression? Translate(
+ SqlExpression? instance,
+ MemberInfo member,
+ Type returnType,
+ IDiagnosticsLogger logger)
+ {
+ return null;
+ }
+}
diff --git a/EntityFrameworkCore.ClickHouse.NTS/Query/Internal/ClickHouseLineStringMethodTranslator.cs b/EntityFrameworkCore.ClickHouse.NTS/Query/Internal/ClickHouseLineStringMethodTranslator.cs
new file mode 100644
index 0000000..d3b796b
--- /dev/null
+++ b/EntityFrameworkCore.ClickHouse.NTS/Query/Internal/ClickHouseLineStringMethodTranslator.cs
@@ -0,0 +1,19 @@
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Diagnostics;
+using Microsoft.EntityFrameworkCore.Query;
+using Microsoft.EntityFrameworkCore.Query.SqlExpressions;
+using System.Reflection;
+
+namespace EntityFrameworkCore.ClickHouse.NTS.Query.Internal;
+
+public class ClickHouseLineStringMethodTranslator : IMethodCallTranslator
+{
+ public SqlExpression? Translate(
+ SqlExpression? instance,
+ MethodInfo method,
+ IReadOnlyList arguments,
+ IDiagnosticsLogger logger)
+ {
+ return null;
+ }
+}
diff --git a/EntityFrameworkCore.ClickHouse.NTS/Query/Internal/ClickHouseMultiLineStringMemberTranslator.cs b/EntityFrameworkCore.ClickHouse.NTS/Query/Internal/ClickHouseMultiLineStringMemberTranslator.cs
new file mode 100644
index 0000000..fb19c80
--- /dev/null
+++ b/EntityFrameworkCore.ClickHouse.NTS/Query/Internal/ClickHouseMultiLineStringMemberTranslator.cs
@@ -0,0 +1,19 @@
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Diagnostics;
+using Microsoft.EntityFrameworkCore.Query;
+using Microsoft.EntityFrameworkCore.Query.SqlExpressions;
+using System.Reflection;
+
+namespace EntityFrameworkCore.ClickHouse.NTS.Query.Internal;
+
+public class ClickHouseMultiLineStringMemberTranslator : IMemberTranslator
+{
+ public SqlExpression? Translate(
+ SqlExpression? instance,
+ MemberInfo member,
+ Type returnType,
+ IDiagnosticsLogger logger)
+ {
+ return null;
+ }
+}
diff --git a/EntityFrameworkCore.ClickHouse.NTS/Query/Internal/ClickHouseNetTopologySuiteAggregateMethodCallTranslatorPlugin.cs b/EntityFrameworkCore.ClickHouse.NTS/Query/Internal/ClickHouseNetTopologySuiteAggregateMethodCallTranslatorPlugin.cs
new file mode 100644
index 0000000..e93cb9b
--- /dev/null
+++ b/EntityFrameworkCore.ClickHouse.NTS/Query/Internal/ClickHouseNetTopologySuiteAggregateMethodCallTranslatorPlugin.cs
@@ -0,0 +1,16 @@
+using Microsoft.EntityFrameworkCore.Query;
+
+namespace EntityFrameworkCore.ClickHouse.NTS.Query.Internal;
+
+public class ClickHouseNetTopologySuiteAggregateMethodCallTranslatorPlugin : IAggregateMethodCallTranslatorPlugin
+{
+ public ClickHouseNetTopologySuiteAggregateMethodCallTranslatorPlugin()
+ {
+ Translators =
+ [
+ new ClickHouseNetTopologySuiteAggregateMethodTranslator()
+ ];
+ }
+
+ public IEnumerable Translators { get; }
+}
diff --git a/EntityFrameworkCore.ClickHouse.NTS/Query/Internal/ClickHouseNetTopologySuiteAggregateMethodTranslator.cs b/EntityFrameworkCore.ClickHouse.NTS/Query/Internal/ClickHouseNetTopologySuiteAggregateMethodTranslator.cs
new file mode 100644
index 0000000..71c79ad
--- /dev/null
+++ b/EntityFrameworkCore.ClickHouse.NTS/Query/Internal/ClickHouseNetTopologySuiteAggregateMethodTranslator.cs
@@ -0,0 +1,19 @@
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Diagnostics;
+using Microsoft.EntityFrameworkCore.Query;
+using Microsoft.EntityFrameworkCore.Query.SqlExpressions;
+using System.Reflection;
+
+namespace EntityFrameworkCore.ClickHouse.NTS.Query.Internal;
+
+public class ClickHouseNetTopologySuiteAggregateMethodTranslator : IAggregateMethodCallTranslator
+{
+ public SqlExpression? Translate(
+ MethodInfo method,
+ EnumerableExpression source,
+ IReadOnlyList arguments,
+ IDiagnosticsLogger logger)
+ {
+ return null;
+ }
+}
diff --git a/EntityFrameworkCore.ClickHouse.NTS/Query/Internal/ClickHouseNetTopologySuiteDbFunctionsMethodCallTranslator.cs b/EntityFrameworkCore.ClickHouse.NTS/Query/Internal/ClickHouseNetTopologySuiteDbFunctionsMethodCallTranslator.cs
new file mode 100644
index 0000000..b3a8290
--- /dev/null
+++ b/EntityFrameworkCore.ClickHouse.NTS/Query/Internal/ClickHouseNetTopologySuiteDbFunctionsMethodCallTranslator.cs
@@ -0,0 +1,19 @@
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Diagnostics;
+using Microsoft.EntityFrameworkCore.Query;
+using Microsoft.EntityFrameworkCore.Query.SqlExpressions;
+using System.Reflection;
+
+namespace EntityFrameworkCore.ClickHouse.NTS.Query.Internal;
+
+public class ClickHouseNetTopologySuiteDbFunctionsMethodCallTranslator : IMethodCallTranslator
+{
+ public SqlExpression? Translate(
+ SqlExpression? instance,
+ MethodInfo method,
+ IReadOnlyList arguments,
+ IDiagnosticsLogger logger)
+ {
+ return null;
+ }
+}
diff --git a/EntityFrameworkCore.ClickHouse.NTS/Query/Internal/ClickHouseNetTopologySuiteEvaluatableExpressionFilterPlugin.cs b/EntityFrameworkCore.ClickHouse.NTS/Query/Internal/ClickHouseNetTopologySuiteEvaluatableExpressionFilterPlugin.cs
new file mode 100644
index 0000000..fa5cc94
--- /dev/null
+++ b/EntityFrameworkCore.ClickHouse.NTS/Query/Internal/ClickHouseNetTopologySuiteEvaluatableExpressionFilterPlugin.cs
@@ -0,0 +1,12 @@
+using Microsoft.EntityFrameworkCore.Query;
+using System.Linq.Expressions;
+
+namespace EntityFrameworkCore.ClickHouse.NTS.Query.Internal;
+
+public class ClickHouseNetTopologySuiteEvaluatableExpressionFilterPlugin : IEvaluatableExpressionFilterPlugin
+{
+ public bool IsEvaluatableExpression(Expression expression)
+ {
+ return false;
+ }
+}
diff --git a/EntityFrameworkCore.ClickHouse.NTS/Query/Internal/ClickHouseNetTopologySuiteMemberTranslatorPlugin.cs b/EntityFrameworkCore.ClickHouse.NTS/Query/Internal/ClickHouseNetTopologySuiteMemberTranslatorPlugin.cs
new file mode 100644
index 0000000..5b79a17
--- /dev/null
+++ b/EntityFrameworkCore.ClickHouse.NTS/Query/Internal/ClickHouseNetTopologySuiteMemberTranslatorPlugin.cs
@@ -0,0 +1,21 @@
+using Microsoft.EntityFrameworkCore.Query;
+
+namespace EntityFrameworkCore.ClickHouse.NTS.Query.Internal;
+
+public class ClickHouseNetTopologySuiteMemberTranslatorPlugin : IMemberTranslatorPlugin
+{
+ public ClickHouseNetTopologySuiteMemberTranslatorPlugin(ISqlExpressionFactory sqlExpressionFactory)
+ {
+ Translators =
+ [
+ new ClickHouseGeometryMemberTranslator(),
+ new ClickHouseGeometryCollectionMemberTranslator(),
+ new ClickHouseLineStringMemberTranslator(),
+ new ClickHouseMultiLineStringMemberTranslator(),
+ new ClickHousePointMemberTranslator(sqlExpressionFactory),
+ new ClickHousePolygonMemberTranslator()
+ ];
+ }
+
+ public IEnumerable Translators { get; }
+}
diff --git a/EntityFrameworkCore.ClickHouse.NTS/Query/Internal/ClickHouseNetTopologySuiteMethodCallTranslatorPlugin.cs b/EntityFrameworkCore.ClickHouse.NTS/Query/Internal/ClickHouseNetTopologySuiteMethodCallTranslatorPlugin.cs
new file mode 100644
index 0000000..a4aca4f
--- /dev/null
+++ b/EntityFrameworkCore.ClickHouse.NTS/Query/Internal/ClickHouseNetTopologySuiteMethodCallTranslatorPlugin.cs
@@ -0,0 +1,20 @@
+using Microsoft.EntityFrameworkCore.Query;
+
+namespace EntityFrameworkCore.ClickHouse.NTS.Query.Internal;
+
+public class ClickHouseNetTopologySuiteMethodCallTranslatorPlugin : IMethodCallTranslatorPlugin
+{
+ public ClickHouseNetTopologySuiteMethodCallTranslatorPlugin()
+ {
+ Translators =
+ [
+ new ClickHouseGeometryMethodTranslator(),
+ new ClickHouseGeometryCollectionMethodTranslator(),
+ new ClickHouseLineStringMethodTranslator(),
+ new ClickHouseNetTopologySuiteDbFunctionsMethodCallTranslator(),
+ new ClickHousePolygonMethodTranslator()
+ ];
+ }
+
+ public IEnumerable Translators { get; }
+}
diff --git a/EntityFrameworkCore.ClickHouse.NTS/Query/Internal/ClickHousePointMemberTranslator.cs b/EntityFrameworkCore.ClickHouse.NTS/Query/Internal/ClickHousePointMemberTranslator.cs
new file mode 100644
index 0000000..df5e9e0
--- /dev/null
+++ b/EntityFrameworkCore.ClickHouse.NTS/Query/Internal/ClickHousePointMemberTranslator.cs
@@ -0,0 +1,38 @@
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Diagnostics;
+using Microsoft.EntityFrameworkCore.Query;
+using Microsoft.EntityFrameworkCore.Query.SqlExpressions;
+using NetTopologySuite.Geometries;
+using System.Reflection;
+
+namespace EntityFrameworkCore.ClickHouse.NTS.Query.Internal;
+
+public class ClickHousePointMemberTranslator : IMemberTranslator
+{
+ private readonly ISqlExpressionFactory _sqlExpressionFactory;
+
+ public ClickHousePointMemberTranslator(ISqlExpressionFactory sqlExpressionFactory)
+ {
+ _sqlExpressionFactory = sqlExpressionFactory;
+ }
+
+ public SqlExpression? Translate(SqlExpression? instance, MemberInfo member, Type returnType, IDiagnosticsLogger logger)
+ {
+ if (member.DeclaringType != typeof(Point))
+ {
+ return null;
+ }
+
+ if (member.Name == nameof(Point.X))
+ {
+ return _sqlExpressionFactory.Function(
+ name: "tupleElement",
+ arguments: [instance, _sqlExpressionFactory.Constant(1)],
+ returnType: typeof(double),
+ nullable: true,
+ argumentsPropagateNullability: [true, true]);
+ }
+
+ return null;
+ }
+}
diff --git a/EntityFrameworkCore.ClickHouse.NTS/Query/Internal/ClickHousePolygonMemberTranslator.cs b/EntityFrameworkCore.ClickHouse.NTS/Query/Internal/ClickHousePolygonMemberTranslator.cs
new file mode 100644
index 0000000..9e7f559
--- /dev/null
+++ b/EntityFrameworkCore.ClickHouse.NTS/Query/Internal/ClickHousePolygonMemberTranslator.cs
@@ -0,0 +1,15 @@
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Diagnostics;
+using Microsoft.EntityFrameworkCore.Query;
+using Microsoft.EntityFrameworkCore.Query.SqlExpressions;
+using System.Reflection;
+
+namespace EntityFrameworkCore.ClickHouse.NTS.Query.Internal;
+
+public class ClickHousePolygonMemberTranslator : IMemberTranslator
+{
+ public SqlExpression? Translate(SqlExpression? instance, MemberInfo member, Type returnType, IDiagnosticsLogger logger)
+ {
+ return null;
+ }
+}
diff --git a/EntityFrameworkCore.ClickHouse.NTS/Query/Internal/ClickHousePolygonMethodTranslator.cs b/EntityFrameworkCore.ClickHouse.NTS/Query/Internal/ClickHousePolygonMethodTranslator.cs
new file mode 100644
index 0000000..a7c9fcd
--- /dev/null
+++ b/EntityFrameworkCore.ClickHouse.NTS/Query/Internal/ClickHousePolygonMethodTranslator.cs
@@ -0,0 +1,19 @@
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Diagnostics;
+using Microsoft.EntityFrameworkCore.Query;
+using Microsoft.EntityFrameworkCore.Query.SqlExpressions;
+using System.Reflection;
+
+namespace EntityFrameworkCore.ClickHouse.NTS.Query.Internal;
+
+public class ClickHousePolygonMethodTranslator : IMethodCallTranslator
+{
+ public SqlExpression? Translate(
+ SqlExpression? instance,
+ MethodInfo method,
+ IReadOnlyList arguments,
+ IDiagnosticsLogger logger)
+ {
+ return null;
+ }
+}
diff --git a/EntityFrameworkCore.ClickHouse.NTS/Scaffolding/Internal/ClickHouseNetTopologySuiteCodeGeneratorPlugin.cs b/EntityFrameworkCore.ClickHouse.NTS/Scaffolding/Internal/ClickHouseNetTopologySuiteCodeGeneratorPlugin.cs
new file mode 100644
index 0000000..95ba95d
--- /dev/null
+++ b/EntityFrameworkCore.ClickHouse.NTS/Scaffolding/Internal/ClickHouseNetTopologySuiteCodeGeneratorPlugin.cs
@@ -0,0 +1,18 @@
+using ClickHouse.EntityFrameworkCore.Infrastructure;
+using EntityFrameworkCore.ClickHouse.NTS.Extensions;
+using Microsoft.EntityFrameworkCore.Design;
+using Microsoft.EntityFrameworkCore.Scaffolding;
+using System.Reflection;
+
+namespace EntityFrameworkCore.ClickHouse.NTS.Scaffolding.Internal;
+
+public class ClickHouseNetTopologySuiteCodeGeneratorPlugin : ProviderCodeGeneratorPlugin
+{
+ private static readonly MethodInfo UseNetTopologySuiteMethodInfo
+ = typeof(ClickHouseNetTopologySuiteDbContextOptionsBuilderExtensions).GetRuntimeMethod(
+ nameof(ClickHouseNetTopologySuiteDbContextOptionsBuilderExtensions.UseNetTopologySuite),
+ new[] { typeof(ClickHouseDbContextOptionsBuilder) })!;
+
+ public override MethodCallCodeFragment GenerateProviderOptions()
+ => new(UseNetTopologySuiteMethodInfo);
+}
diff --git a/EntityFrameworkCore.ClickHouse.NTS/Storage/Internal/ClickHouseNetTopologySuiteTypeMappingSourcePlugin.cs b/EntityFrameworkCore.ClickHouse.NTS/Storage/Internal/ClickHouseNetTopologySuiteTypeMappingSourcePlugin.cs
new file mode 100644
index 0000000..f128aae
--- /dev/null
+++ b/EntityFrameworkCore.ClickHouse.NTS/Storage/Internal/ClickHouseNetTopologySuiteTypeMappingSourcePlugin.cs
@@ -0,0 +1,46 @@
+using EntityFrameworkCore.ClickHouse.NTS.Storage.Internal.Mapping;
+using Microsoft.EntityFrameworkCore.Storage;
+using NetTopologySuite.Geometries;
+
+namespace EntityFrameworkCore.ClickHouse.NTS.Storage.Internal;
+
+public class ClickHouseNetTopologySuiteTypeMappingSourcePlugin : IRelationalTypeMappingSourcePlugin
+{
+ private static readonly RelationalTypeMapping PointTypeMapping = new ClickHousePointTypeMapping();
+ private static readonly RelationalTypeMapping RingTypeMapping = new ClickHouseRingTypeMapping();
+ private static readonly RelationalTypeMapping PolygonTypeMapping = new ClickHousePolygonTypeMapping();
+ private static readonly RelationalTypeMapping MultiPolygonTypeMapping = new ClickHouseMultiPolygonTypeMapping();
+ private static readonly RelationalTypeMapping GeometryTypeMapping = new ClickHouseGeometryTypeMapping();
+ private static readonly RelationalTypeMapping LineStringTypeMapping = new ClickHouseLineStringTypeMapping();
+
+ private static readonly Dictionary StoreTypeMappings = new(StringComparer.OrdinalIgnoreCase)
+ {
+ { "Point", PointTypeMapping },
+ { "Ring", RingTypeMapping },
+ { "Polygon", PolygonTypeMapping },
+ { "MultiPolygon", MultiPolygonTypeMapping },
+ { "Geometry", GeometryTypeMapping },
+ { "LineString", LineStringTypeMapping }
+ };
+
+ private static readonly Dictionary ClrTypeMappings = new()
+ {
+ { typeof(Point), PointTypeMapping },
+ { typeof(MultiPoint), RingTypeMapping },
+ { typeof(Polygon), PolygonTypeMapping },
+ { typeof(MultiPolygon), MultiPolygonTypeMapping },
+ { typeof(Geometry), GeometryTypeMapping },
+ { typeof(LineString), LineStringTypeMapping }
+ };
+
+ public RelationalTypeMapping? FindMapping(in RelationalTypeMappingInfo mappingInfo)
+ {
+ if (!string.IsNullOrWhiteSpace(mappingInfo.StoreTypeName) &&
+ StoreTypeMappings.TryGetValue(mappingInfo.StoreTypeName, out var mapping))
+ {
+ return mapping;
+ }
+
+ return ClrTypeMappings.TryGetValue(mappingInfo.ClrType, out mapping) ? mapping : null;
+ }
+}
diff --git a/EntityFrameworkCore.ClickHouse.NTS/Storage/Internal/Mapping/ClickHouseGeometryTypeMapping.cs b/EntityFrameworkCore.ClickHouse.NTS/Storage/Internal/Mapping/ClickHouseGeometryTypeMapping.cs
new file mode 100644
index 0000000..ec742b7
--- /dev/null
+++ b/EntityFrameworkCore.ClickHouse.NTS/Storage/Internal/Mapping/ClickHouseGeometryTypeMapping.cs
@@ -0,0 +1,47 @@
+using ClickHouse.EntityFrameworkCore.Extensions;
+using EntityFrameworkCore.ClickHouse.NTS.Storage.Json;
+using EntityFrameworkCore.ClickHouse.NTS.Storage.ValueConversion.Internal;
+using Microsoft.EntityFrameworkCore.Storage;
+using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+using NetTopologySuite.Geometries;
+using NetTopologySuite.IO;
+using System.Data.Common;
+
+namespace EntityFrameworkCore.ClickHouse.NTS.Storage.Internal.Mapping;
+
+public class ClickHouseGeometryTypeMapping : RelationalGeometryTypeMapping
+{
+ public ClickHouseGeometryTypeMapping()
+ : base(ClickHouseGeometryValueConverter.Instance, "Geometry", ClickHouseJsonGeometryWktReaderWriter.Instance)
+ {
+ }
+
+ protected ClickHouseGeometryTypeMapping(RelationalTypeMappingParameters parameters, ValueConverter? converter)
+ : base(parameters, converter)
+ {
+ }
+
+ protected override RelationalTypeMapping Clone(RelationalTypeMappingParameters parameters)
+ {
+ return new ClickHouseGeometryTypeMapping(parameters, (ValueConverter)base.Converter!);
+ }
+
+ protected override string AsText(object value)
+ {
+ return ((Geometry)value).AsText();
+ }
+
+ protected override int GetSrid(object value)
+ {
+ return ((Geometry)value).SRID;
+ }
+
+ protected override void ConfigureParameter(DbParameter parameter)
+ {
+ base.ConfigureParameter(parameter);
+ parameter.SetStoreType(StoreType);
+ parameter.
+ }
+
+ protected override Type WktReaderType => typeof(WKTReader);
+}
diff --git a/EntityFrameworkCore.ClickHouse.NTS/Storage/Internal/Mapping/ClickHouseLineStringTypeMapping.cs b/EntityFrameworkCore.ClickHouse.NTS/Storage/Internal/Mapping/ClickHouseLineStringTypeMapping.cs
new file mode 100644
index 0000000..0fb5225
--- /dev/null
+++ b/EntityFrameworkCore.ClickHouse.NTS/Storage/Internal/Mapping/ClickHouseLineStringTypeMapping.cs
@@ -0,0 +1,52 @@
+using ClickHouse.Driver.ADO.Parameters;
+using ClickHouse.EntityFrameworkCore.Extensions;
+using EntityFrameworkCore.ClickHouse.NTS.Storage.Json;
+using EntityFrameworkCore.ClickHouse.NTS.Storage.ValueConversion.Internal;
+using Microsoft.EntityFrameworkCore.Storage;
+using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+using NetTopologySuite.Geometries;
+using NetTopologySuite.IO;
+using System.Data.Common;
+
+namespace EntityFrameworkCore.ClickHouse.NTS.Storage.Internal.Mapping;
+
+public class ClickHouseLineStringTypeMapping : RelationalGeometryTypeMapping[]>
+{
+ public ClickHouseLineStringTypeMapping()
+ : base(
+ ClickHouseLineStringValueConverter.Instance,
+ Geometry.TypeNameLineString,
+ ClickHouseJsonGeometryWktReaderWriter.Instance)
+ {
+ }
+
+ protected ClickHouseLineStringTypeMapping(RelationalTypeMappingParameters parameters, ValueConverter[]>? converter)
+ : base(parameters, converter)
+ {
+ }
+
+ protected override RelationalTypeMapping Clone(RelationalTypeMappingParameters parameters)
+ {
+ return new ClickHouseLineStringTypeMapping(
+ parameters,
+ (ValueConverter[]>)Converter!);
+ }
+
+ protected override string AsText(object value)
+ {
+ return ((LineString)value).AsText();
+ }
+
+ protected override int GetSrid(object value)
+ {
+ return ((LineString)value).SRID;
+ }
+
+ protected override Type WktReaderType => typeof(WKTReader);
+
+ protected override void ConfigureParameter(DbParameter parameter)
+ {
+ base.ConfigureParameter(parameter);
+ parameter.SetStoreType(StoreType);
+ }
+}
diff --git a/EntityFrameworkCore.ClickHouse.NTS/Storage/Internal/Mapping/ClickHouseMultiPolygonTypeMapping.cs b/EntityFrameworkCore.ClickHouse.NTS/Storage/Internal/Mapping/ClickHouseMultiPolygonTypeMapping.cs
new file mode 100644
index 0000000..cbeb2fa
--- /dev/null
+++ b/EntityFrameworkCore.ClickHouse.NTS/Storage/Internal/Mapping/ClickHouseMultiPolygonTypeMapping.cs
@@ -0,0 +1,45 @@
+using ClickHouse.EntityFrameworkCore.Extensions;
+using EntityFrameworkCore.ClickHouse.NTS.Storage.Json;
+using EntityFrameworkCore.ClickHouse.NTS.Storage.ValueConversion.Internal;
+using Microsoft.EntityFrameworkCore.Storage;
+using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+using NetTopologySuite.Geometries;
+using NetTopologySuite.IO;
+using System.Data.Common;
+
+namespace EntityFrameworkCore.ClickHouse.NTS.Storage.Internal.Mapping;
+
+public class ClickHouseMultiPolygonTypeMapping : RelationalGeometryTypeMapping
+{
+ public ClickHouseMultiPolygonTypeMapping()
+ : base(new ClickHouseMultiPolygonValueConverter(), Geometry.TypeNameMultiPolygon, ClickHouseJsonGeometryWktReaderWriter.Instance)
+ {
+ }
+
+ protected ClickHouseMultiPolygonTypeMapping(RelationalTypeMappingParameters parameters, ValueConverter? converter) : base(parameters, converter)
+ {
+ }
+
+ protected override RelationalTypeMapping Clone(RelationalTypeMappingParameters parameters)
+ {
+ return new ClickHouseMultiPolygonTypeMapping(parameters, SpatialConverter);
+ }
+
+ protected override string AsText(object value)
+ {
+ return ((MultiPolygon)value).AsText();
+ }
+
+ protected override int GetSrid(object value)
+ {
+ return ((MultiPolygon)value).SRID;
+ }
+
+ protected override void ConfigureParameter(DbParameter parameter)
+ {
+ base.ConfigureParameter(parameter);
+ parameter.SetStoreType(StoreType);
+ }
+
+ protected override Type WktReaderType => typeof(WKTReader);
+}
diff --git a/EntityFrameworkCore.ClickHouse.NTS/Storage/Internal/Mapping/ClickHousePointTypeMapping.cs b/EntityFrameworkCore.ClickHouse.NTS/Storage/Internal/Mapping/ClickHousePointTypeMapping.cs
new file mode 100644
index 0000000..85029f3
--- /dev/null
+++ b/EntityFrameworkCore.ClickHouse.NTS/Storage/Internal/Mapping/ClickHousePointTypeMapping.cs
@@ -0,0 +1,44 @@
+using ClickHouse.Driver.ADO.Parameters;
+using ClickHouse.EntityFrameworkCore.Extensions;
+using EntityFrameworkCore.ClickHouse.NTS.Storage.Json;
+using EntityFrameworkCore.ClickHouse.NTS.Storage.ValueConversion.Internal;
+using Microsoft.EntityFrameworkCore.Storage;
+using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+using NetTopologySuite.Geometries;
+using NetTopologySuite.IO;
+using System.Data.Common;
+
+namespace EntityFrameworkCore.ClickHouse.NTS.Storage.Internal.Mapping;
+
+public class ClickHousePointTypeMapping : RelationalGeometryTypeMapping>
+{
+ public ClickHousePointTypeMapping()
+ : base(new ClickHousePointValueConverter(), Geometry.TypeNamePoint, ClickHouseJsonGeometryWktReaderWriter.Instance)
+ {
+ }
+
+ protected ClickHousePointTypeMapping(RelationalTypeMappingParameters parameters, ValueConverter>? converter)
+ : base(parameters, converter)
+ {
+ }
+
+ protected override RelationalTypeMapping Clone(RelationalTypeMappingParameters parameters)
+ {
+ return new ClickHousePointTypeMapping(parameters, SpatialConverter);
+ }
+
+ protected override string AsText(object value)
+ => ((Point)value).AsText();
+
+ protected override int GetSrid(object value)
+ => ((Point)value).SRID;
+
+ protected override Type WktReaderType
+ => typeof(WKTReader);
+
+ protected override void ConfigureParameter(DbParameter parameter)
+ {
+ base.ConfigureParameter(parameter);
+ parameter.SetStoreType(StoreType);
+ }
+}
diff --git a/EntityFrameworkCore.ClickHouse.NTS/Storage/Internal/Mapping/ClickHousePolygonTypeMapping.cs b/EntityFrameworkCore.ClickHouse.NTS/Storage/Internal/Mapping/ClickHousePolygonTypeMapping.cs
new file mode 100644
index 0000000..09adbf0
--- /dev/null
+++ b/EntityFrameworkCore.ClickHouse.NTS/Storage/Internal/Mapping/ClickHousePolygonTypeMapping.cs
@@ -0,0 +1,46 @@
+using ClickHouse.Driver.ADO.Parameters;
+using ClickHouse.EntityFrameworkCore.Extensions;
+using EntityFrameworkCore.ClickHouse.NTS.Storage.Json;
+using EntityFrameworkCore.ClickHouse.NTS.Storage.ValueConversion.Internal;
+using Microsoft.EntityFrameworkCore.Storage;
+using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+using NetTopologySuite.Geometries;
+using NetTopologySuite.IO;
+using System.Data.Common;
+
+namespace EntityFrameworkCore.ClickHouse.NTS.Storage.Internal.Mapping;
+
+public class ClickHousePolygonTypeMapping : RelationalGeometryTypeMapping
+{
+ public ClickHousePolygonTypeMapping()
+ : base(new ClickHousePolygonValueConverter(), Geometry.TypeNamePolygon, ClickHouseJsonGeometryWktReaderWriter.Instance)
+ {
+ }
+
+ protected ClickHousePolygonTypeMapping(RelationalTypeMappingParameters parameters, ValueConverter? converter) : base(parameters, converter)
+ {
+ }
+
+ protected override RelationalTypeMapping Clone(RelationalTypeMappingParameters parameters)
+ {
+ return new ClickHousePolygonTypeMapping(parameters, SpatialConverter);
+ }
+
+ protected override string AsText(object value)
+ {
+ return ((Polygon)value).AsText();
+ }
+
+ protected override int GetSrid(object value)
+ {
+ return ((Polygon)value).SRID;
+ }
+
+ protected override void ConfigureParameter(DbParameter parameter)
+ {
+ base.ConfigureParameter(parameter);
+ parameter.SetStoreType(StoreType);
+ }
+
+ protected override Type WktReaderType => typeof(WKTReader);
+}
diff --git a/EntityFrameworkCore.ClickHouse.NTS/Storage/Internal/Mapping/ClickHouseRingTypeMapping.cs b/EntityFrameworkCore.ClickHouse.NTS/Storage/Internal/Mapping/ClickHouseRingTypeMapping.cs
new file mode 100644
index 0000000..2149309
--- /dev/null
+++ b/EntityFrameworkCore.ClickHouse.NTS/Storage/Internal/Mapping/ClickHouseRingTypeMapping.cs
@@ -0,0 +1,45 @@
+using ClickHouse.Driver.ADO.Parameters;
+using ClickHouse.EntityFrameworkCore.Extensions;
+using EntityFrameworkCore.ClickHouse.NTS.Storage.Json;
+using Microsoft.EntityFrameworkCore.Storage;
+using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+using NetTopologySuite.Geometries;
+using NetTopologySuite.IO;
+using System.Data.Common;
+
+namespace EntityFrameworkCore.ClickHouse.NTS.Storage.Internal.Mapping;
+
+public class ClickHouseRingTypeMapping : RelationalGeometryTypeMapping
+{
+ public ClickHouseRingTypeMapping()
+ : base(null, "Ring", ClickHouseJsonGeometryWktReaderWriter.Instance)
+ {
+ }
+
+ protected ClickHouseRingTypeMapping(RelationalTypeMappingParameters parameters, ValueConverter? converter) : base(parameters, converter)
+ {
+ }
+
+ protected override RelationalTypeMapping Clone(RelationalTypeMappingParameters parameters)
+ {
+ return new ClickHouseRingTypeMapping(parameters, SpatialConverter);
+ }
+
+ protected override string AsText(object value)
+ {
+ return ((MultiPoint)value).AsText();
+ }
+
+ protected override int GetSrid(object value)
+ {
+ return ((MultiPoint)value).SRID;
+ }
+
+ protected override void ConfigureParameter(DbParameter parameter)
+ {
+ base.ConfigureParameter(parameter);
+ parameter.SetStoreType(StoreType);
+ }
+
+ protected override Type WktReaderType => typeof(WKTReader);
+}
diff --git a/EntityFrameworkCore.ClickHouse.NTS/Storage/Json/ClickHouseJsonGeometryWktReaderWriter.cs b/EntityFrameworkCore.ClickHouse.NTS/Storage/Json/ClickHouseJsonGeometryWktReaderWriter.cs
new file mode 100644
index 0000000..e993dbe
--- /dev/null
+++ b/EntityFrameworkCore.ClickHouse.NTS/Storage/Json/ClickHouseJsonGeometryWktReaderWriter.cs
@@ -0,0 +1,27 @@
+using System.Linq.Expressions;
+using System.Reflection;
+using System.Text.Json;
+
+using Microsoft.EntityFrameworkCore.Storage.Json;
+
+using NetTopologySuite.Geometries;
+using NetTopologySuite.IO;
+
+namespace EntityFrameworkCore.ClickHouse.NTS.Storage.Json;
+
+public class ClickHouseJsonGeometryWktReaderWriter : JsonValueReaderWriter
+{
+ private static readonly WKTReader WktReader = new();
+
+ private static readonly PropertyInfo InstanceProperty = typeof(ClickHouseJsonGeometryWktReaderWriter).GetProperty(nameof(Instance))!;
+
+ public static ClickHouseJsonGeometryWktReaderWriter Instance { get; } = new();
+
+ public override Geometry FromJsonTyped(ref Utf8JsonReaderManager manager, object? existingObject = null)
+ => WktReader.Read(manager.CurrentReader.GetString());
+
+ public override void ToJsonTyped(Utf8JsonWriter writer, Geometry value)
+ => writer.WriteStringValue(value.ToText());
+
+ public override Expression ConstructorExpression => Expression.Property(null, InstanceProperty);
+}
diff --git a/EntityFrameworkCore.ClickHouse.NTS/Storage/ValueConversion/Internal/ClickHouseGeometryValueConverter.cs b/EntityFrameworkCore.ClickHouse.NTS/Storage/ValueConversion/Internal/ClickHouseGeometryValueConverter.cs
new file mode 100644
index 0000000..24b23f2
--- /dev/null
+++ b/EntityFrameworkCore.ClickHouse.NTS/Storage/ValueConversion/Internal/ClickHouseGeometryValueConverter.cs
@@ -0,0 +1,27 @@
+using EntityFrameworkCore.ClickHouse.NTS.Extensions;
+using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+using NetTopologySuite.Geometries;
+
+namespace EntityFrameworkCore.ClickHouse.NTS.Storage.ValueConversion.Internal;
+
+public class ClickHouseGeometryValueConverter : ValueConverter
+{
+ internal static readonly ClickHouseGeometryValueConverter Instance = new();
+
+ public ClickHouseGeometryValueConverter()
+ : base(
+ v => ToProvider(v),
+ v => FromProvider(v))
+ {
+ }
+
+ private static object ToProvider(Geometry geometry)
+ {
+ return geometry.ToClickHouse();
+ }
+
+ private static Geometry FromProvider(object obj)
+ {
+ throw new NotImplementedException();
+ }
+}
\ No newline at end of file
diff --git a/EntityFrameworkCore.ClickHouse.NTS/Storage/ValueConversion/Internal/ClickHouseLineStringValueConverter.cs b/EntityFrameworkCore.ClickHouse.NTS/Storage/ValueConversion/Internal/ClickHouseLineStringValueConverter.cs
new file mode 100644
index 0000000..22e1005
--- /dev/null
+++ b/EntityFrameworkCore.ClickHouse.NTS/Storage/ValueConversion/Internal/ClickHouseLineStringValueConverter.cs
@@ -0,0 +1,20 @@
+using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+using NetTopologySuite.Geometries;
+
+namespace EntityFrameworkCore.ClickHouse.NTS.Storage.ValueConversion.Internal;
+
+public class ClickHouseLineStringValueConverter : ValueConverter[]>
+{
+ internal static readonly ClickHouseLineStringValueConverter Instance = new();
+
+ public ClickHouseLineStringValueConverter()
+ : base(
+ v => ConvertToProvider(v),
+ v => ConvertFromProvider(v))
+ {
+ }
+
+ private static Tuple[] ConvertToProvider(LineString value) => value.Coordinates.Select(c => new Tuple(c.X, c.Y)).ToArray();
+
+ private static LineString ConvertFromProvider(Tuple[] value) => new(value.Select(c => new Coordinate(c.Item1, c.Item2)).ToArray());
+}
diff --git a/EntityFrameworkCore.ClickHouse.NTS/Storage/ValueConversion/Internal/ClickHouseMultiPolygonValueConverter.cs b/EntityFrameworkCore.ClickHouse.NTS/Storage/ValueConversion/Internal/ClickHouseMultiPolygonValueConverter.cs
new file mode 100644
index 0000000..74e273f
--- /dev/null
+++ b/EntityFrameworkCore.ClickHouse.NTS/Storage/ValueConversion/Internal/ClickHouseMultiPolygonValueConverter.cs
@@ -0,0 +1,14 @@
+using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+using NetTopologySuite.Geometries;
+
+namespace EntityFrameworkCore.ClickHouse.NTS.Storage.ValueConversion.Internal;
+
+public class ClickHouseMultiPolygonValueConverter : ValueConverter
+{
+ public ClickHouseMultiPolygonValueConverter()
+ : base(
+ x => Array.Empty(),
+ x => new MultiPolygon(Array.Empty()))
+ {
+ }
+}
diff --git a/EntityFrameworkCore.ClickHouse.NTS/Storage/ValueConversion/Internal/ClickHousePointValueConverter.cs b/EntityFrameworkCore.ClickHouse.NTS/Storage/ValueConversion/Internal/ClickHousePointValueConverter.cs
new file mode 100644
index 0000000..a0fa4d7
--- /dev/null
+++ b/EntityFrameworkCore.ClickHouse.NTS/Storage/ValueConversion/Internal/ClickHousePointValueConverter.cs
@@ -0,0 +1,14 @@
+using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+using NetTopologySuite.Geometries;
+
+namespace EntityFrameworkCore.ClickHouse.NTS.Storage.ValueConversion.Internal;
+
+public class ClickHousePointValueConverter : ValueConverter>
+{
+ public ClickHousePointValueConverter()
+ : base(
+ x => new Tuple(x.X, x.Y),
+ y => new Point(y.Item1, y.Item2))
+ {
+ }
+}
diff --git a/EntityFrameworkCore.ClickHouse.NTS/Storage/ValueConversion/Internal/ClickHousePolygonValueConverter.cs b/EntityFrameworkCore.ClickHouse.NTS/Storage/ValueConversion/Internal/ClickHousePolygonValueConverter.cs
new file mode 100644
index 0000000..91bb46f
--- /dev/null
+++ b/EntityFrameworkCore.ClickHouse.NTS/Storage/ValueConversion/Internal/ClickHousePolygonValueConverter.cs
@@ -0,0 +1,14 @@
+using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+using NetTopologySuite.Geometries;
+
+namespace EntityFrameworkCore.ClickHouse.NTS.Storage.ValueConversion.Internal;
+
+public class ClickHousePolygonValueConverter : ValueConverter
+{
+ public ClickHousePolygonValueConverter()
+ : base(
+ x => Array.Empty(),
+ x => new Polygon(null))
+ {
+ }
+}
diff --git a/EntityFrameworkCore.ClickHouse.NTS/Storage/ValueConversion/Internal/GeometryValueConverter.cs b/EntityFrameworkCore.ClickHouse.NTS/Storage/ValueConversion/Internal/GeometryValueConverter.cs
new file mode 100644
index 0000000..cfadf26
--- /dev/null
+++ b/EntityFrameworkCore.ClickHouse.NTS/Storage/ValueConversion/Internal/GeometryValueConverter.cs
@@ -0,0 +1,15 @@
+using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+using NetTopologySuite.Geometries;
+
+namespace EntityFrameworkCore.ClickHouse.NTS.Storage.ValueConversion.Internal;
+
+public class GeometryValueConverter : ValueConverter where TGeometry : Geometry
+{
+ // TODO
+ public GeometryValueConverter()
+ : base(
+ g => Array.Empty(),
+ b => (TGeometry)null)
+ {
+ }
+}
diff --git a/EntityFrameworkCore.ClickHouse.sln b/EntityFrameworkCore.ClickHouse.sln
index b9d631d..1950351 100644
--- a/EntityFrameworkCore.ClickHouse.sln
+++ b/EntityFrameworkCore.ClickHouse.sln
@@ -12,6 +12,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
.editorconfig = .editorconfig
EndProjectSection
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EntityFrameworkCore.ClickHouse.NTS", "EntityFrameworkCore.ClickHouse.NTS\EntityFrameworkCore.ClickHouse.NTS.csproj", "{29871172-E937-41D4-8120-C0E730F2650E}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -26,6 +28,10 @@ Global
{301736C0-2341-4C89-87E2-A6492FB00705}.Debug|Any CPU.Build.0 = Debug|Any CPU
{301736C0-2341-4C89-87E2-A6492FB00705}.Release|Any CPU.ActiveCfg = Release|Any CPU
{301736C0-2341-4C89-87E2-A6492FB00705}.Release|Any CPU.Build.0 = Release|Any CPU
+ {29871172-E937-41D4-8120-C0E730F2650E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {29871172-E937-41D4-8120-C0E730F2650E}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {29871172-E937-41D4-8120-C0E730F2650E}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {29871172-E937-41D4-8120-C0E730F2650E}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/EntityFrameworkCore.ClickHouse/Extensions/StringExtensions.cs b/EntityFrameworkCore.ClickHouse/Extensions/StringExtensions.cs
index db02fa9..3f38694 100644
--- a/EntityFrameworkCore.ClickHouse/Extensions/StringExtensions.cs
+++ b/EntityFrameworkCore.ClickHouse/Extensions/StringExtensions.cs
@@ -12,4 +12,4 @@ internal static string GetNullableType(this string type)
}
private static bool IsJson(this string type) => Json.Equals(type, StringComparison.InvariantCultureIgnoreCase);
-}
\ No newline at end of file
+}