From 3250e3656264a1e5538320ba96026f65d6a5fa9f Mon Sep 17 00:00:00 2001 From: Denis Ivanov Date: Thu, 26 Sep 2024 09:31:29 +0300 Subject: [PATCH 1/2] NTS --- ...workCore.ClickHouse.FunctionalTests.csproj | 1 + .../ClickHouseMigrationsSqlGeneratorTest.cs | 2 +- .../Query/SpatialQueryClickHouseFixture.cs | 47 +++++++++++++++ .../Query/SpatialQueryClickHouseTest.cs | 14 +++++ ...HouseNetTopologySuiteDesignTimeServices.cs | 19 ++++++ .../EntityFrameworkCore.ClickHouse.NTS.csproj | 17 ++++++ ...ySuiteDbContextOptionsBuilderExtensions.cs | 21 +++++++ ...opologySuiteServiceCollectionExtensions.cs | 28 +++++++++ ...ckHouseNetTopologySuiteOptionsExtension.cs | 58 +++++++++++++++++++ ...HouseGeometryCollectionMemberTranslator.cs | 15 +++++ ...HouseGeometryCollectionMethodTranslator.cs | 19 ++++++ .../ClickHouseGeometryMemberTranslator.cs | 15 +++++ .../ClickHouseGeometryMethodTranslator.cs | 19 ++++++ .../ClickHouseLineStringMemberTranslator.cs | 19 ++++++ .../ClickHouseLineStringMethodTranslator.cs | 19 ++++++ ...ickHouseMultiLineStringMemberTranslator.cs | 19 ++++++ ...uiteAggregateMethodCallTranslatorPlugin.cs | 16 +++++ ...tTopologySuiteAggregateMethodTranslator.cs | 19 ++++++ ...ogySuiteDbFunctionsMethodCallTranslator.cs | 19 ++++++ ...ySuiteEvaluatableExpressionFilterPlugin.cs | 12 ++++ ...eNetTopologySuiteMemberTranslatorPlugin.cs | 21 +++++++ ...TopologySuiteMethodCallTranslatorPlugin.cs | 20 +++++++ .../ClickHousePointMemberTranslator.cs | 38 ++++++++++++ .../ClickHousePolygonMemberTranslator.cs | 15 +++++ .../ClickHousePolygonMethodTranslator.cs | 19 ++++++ ...ouseNetTopologySuiteCodeGeneratorPlugin.cs | 18 ++++++ ...NetTopologySuiteTypeMappingSourcePlugin.cs | 43 ++++++++++++++ .../Mapping/ClickHouseGeometryTypeMapping.cs | 45 ++++++++++++++ .../ClickHouseMultiPolygonTypeMapping.cs | 46 +++++++++++++++ .../Mapping/ClickHousePointTypeMapping.cs | 45 ++++++++++++++ .../Mapping/ClickHousePolygonTypeMapping.cs | 46 +++++++++++++++ .../Mapping/ClickHouseRingTypeMapping.cs | 45 ++++++++++++++ .../ClickHouseJsonGeometryWktReaderWriter.cs | 27 +++++++++ .../ClickHouseMultiPolygonValueConverter.cs | 14 +++++ .../Internal/ClickHousePointValueConverter.cs | 14 +++++ .../ClickHousePolygonValueConverter.cs | 14 +++++ .../Internal/GeometryValueConverter.cs | 15 +++++ EntityFrameworkCore.ClickHouse.sln | 6 ++ 38 files changed, 888 insertions(+), 1 deletion(-) create mode 100644 EntityFrameworkCore.ClickHouse.FunctionalTests/Query/SpatialQueryClickHouseFixture.cs create mode 100644 EntityFrameworkCore.ClickHouse.FunctionalTests/Query/SpatialQueryClickHouseTest.cs create mode 100644 EntityFrameworkCore.ClickHouse.NTS/Design/Internal/ClickHouseNetTopologySuiteDesignTimeServices.cs create mode 100644 EntityFrameworkCore.ClickHouse.NTS/EntityFrameworkCore.ClickHouse.NTS.csproj create mode 100644 EntityFrameworkCore.ClickHouse.NTS/Extensions/ClickHouseNetTopologySuiteDbContextOptionsBuilderExtensions.cs create mode 100644 EntityFrameworkCore.ClickHouse.NTS/Extensions/ClickHouseNetTopologySuiteServiceCollectionExtensions.cs create mode 100644 EntityFrameworkCore.ClickHouse.NTS/Infrastructure/Internal/ClickHouseNetTopologySuiteOptionsExtension.cs create mode 100644 EntityFrameworkCore.ClickHouse.NTS/Query/Internal/ClickHouseGeometryCollectionMemberTranslator.cs create mode 100644 EntityFrameworkCore.ClickHouse.NTS/Query/Internal/ClickHouseGeometryCollectionMethodTranslator.cs create mode 100644 EntityFrameworkCore.ClickHouse.NTS/Query/Internal/ClickHouseGeometryMemberTranslator.cs create mode 100644 EntityFrameworkCore.ClickHouse.NTS/Query/Internal/ClickHouseGeometryMethodTranslator.cs create mode 100644 EntityFrameworkCore.ClickHouse.NTS/Query/Internal/ClickHouseLineStringMemberTranslator.cs create mode 100644 EntityFrameworkCore.ClickHouse.NTS/Query/Internal/ClickHouseLineStringMethodTranslator.cs create mode 100644 EntityFrameworkCore.ClickHouse.NTS/Query/Internal/ClickHouseMultiLineStringMemberTranslator.cs create mode 100644 EntityFrameworkCore.ClickHouse.NTS/Query/Internal/ClickHouseNetTopologySuiteAggregateMethodCallTranslatorPlugin.cs create mode 100644 EntityFrameworkCore.ClickHouse.NTS/Query/Internal/ClickHouseNetTopologySuiteAggregateMethodTranslator.cs create mode 100644 EntityFrameworkCore.ClickHouse.NTS/Query/Internal/ClickHouseNetTopologySuiteDbFunctionsMethodCallTranslator.cs create mode 100644 EntityFrameworkCore.ClickHouse.NTS/Query/Internal/ClickHouseNetTopologySuiteEvaluatableExpressionFilterPlugin.cs create mode 100644 EntityFrameworkCore.ClickHouse.NTS/Query/Internal/ClickHouseNetTopologySuiteMemberTranslatorPlugin.cs create mode 100644 EntityFrameworkCore.ClickHouse.NTS/Query/Internal/ClickHouseNetTopologySuiteMethodCallTranslatorPlugin.cs create mode 100644 EntityFrameworkCore.ClickHouse.NTS/Query/Internal/ClickHousePointMemberTranslator.cs create mode 100644 EntityFrameworkCore.ClickHouse.NTS/Query/Internal/ClickHousePolygonMemberTranslator.cs create mode 100644 EntityFrameworkCore.ClickHouse.NTS/Query/Internal/ClickHousePolygonMethodTranslator.cs create mode 100644 EntityFrameworkCore.ClickHouse.NTS/Scaffolding/Internal/ClickHouseNetTopologySuiteCodeGeneratorPlugin.cs create mode 100644 EntityFrameworkCore.ClickHouse.NTS/Storage/Internal/ClickHouseNetTopologySuiteTypeMappingSourcePlugin.cs create mode 100644 EntityFrameworkCore.ClickHouse.NTS/Storage/Internal/Mapping/ClickHouseGeometryTypeMapping.cs create mode 100644 EntityFrameworkCore.ClickHouse.NTS/Storage/Internal/Mapping/ClickHouseMultiPolygonTypeMapping.cs create mode 100644 EntityFrameworkCore.ClickHouse.NTS/Storage/Internal/Mapping/ClickHousePointTypeMapping.cs create mode 100644 EntityFrameworkCore.ClickHouse.NTS/Storage/Internal/Mapping/ClickHousePolygonTypeMapping.cs create mode 100644 EntityFrameworkCore.ClickHouse.NTS/Storage/Internal/Mapping/ClickHouseRingTypeMapping.cs create mode 100644 EntityFrameworkCore.ClickHouse.NTS/Storage/Json/ClickHouseJsonGeometryWktReaderWriter.cs create mode 100644 EntityFrameworkCore.ClickHouse.NTS/Storage/ValueConversion/Internal/ClickHouseMultiPolygonValueConverter.cs create mode 100644 EntityFrameworkCore.ClickHouse.NTS/Storage/ValueConversion/Internal/ClickHousePointValueConverter.cs create mode 100644 EntityFrameworkCore.ClickHouse.NTS/Storage/ValueConversion/Internal/ClickHousePolygonValueConverter.cs create mode 100644 EntityFrameworkCore.ClickHouse.NTS/Storage/ValueConversion/Internal/GeometryValueConverter.cs 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..ae58cc5 --- /dev/null +++ b/EntityFrameworkCore.ClickHouse.NTS/EntityFrameworkCore.ClickHouse.NTS.csproj @@ -0,0 +1,17 @@ + + + + net10.0 + enable + enable + + + + + + + + + + + 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/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..2094592 --- /dev/null +++ b/EntityFrameworkCore.ClickHouse.NTS/Storage/Internal/ClickHouseNetTopologySuiteTypeMappingSourcePlugin.cs @@ -0,0 +1,43 @@ +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 Dictionary StoreTypeMappings = new(StringComparer.OrdinalIgnoreCase) + { + { "Point", PointTypeMapping }, + { "Ring", RingTypeMapping }, + { "Polygon", PolygonTypeMapping }, + { "MultiPolygon", MultiPolygonTypeMapping }, + { "Geometry", GeometryTypeMapping } + }; + + private static readonly Dictionary ClrTypeMappings = new() + { + { typeof(Point), PointTypeMapping }, + { typeof(MultiPoint), RingTypeMapping }, + { typeof(Polygon), PolygonTypeMapping }, + { typeof(MultiPolygon), MultiPolygonTypeMapping }, + { typeof(Geometry), GeometryTypeMapping } + }; + + 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..b4fcf2b --- /dev/null +++ b/EntityFrameworkCore.ClickHouse.NTS/Storage/Internal/Mapping/ClickHouseGeometryTypeMapping.cs @@ -0,0 +1,45 @@ +using ClickHouse.Driver.ADO.Parameters; +using EntityFrameworkCore.ClickHouse.NTS.Storage.Json; +using Microsoft.EntityFrameworkCore.Storage; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using NetTopologySuite.Geometries; +using System.Data.Common; + +namespace EntityFrameworkCore.ClickHouse.NTS.Storage.Internal.Mapping; + +public class ClickHouseGeometryTypeMapping : RelationalGeometryTypeMapping +{ + public ClickHouseGeometryTypeMapping() + : base(null, "Geometry", ClickHouseJsonGeometryWktReaderWriter.Instance) + { + } + + protected ClickHouseGeometryTypeMapping(RelationalTypeMappingParameters parameters, ValueConverter? converter) : base(parameters, converter) + { + } + + protected override RelationalTypeMapping Clone(RelationalTypeMappingParameters parameters) + { + return new ClickHouseGeometryTypeMapping(parameters, SpatialConverter); + } + + protected override string AsText(object value) + { + throw new NotImplementedException(); + } + + protected override int GetSrid(object value) + { + throw new NotImplementedException(); + } + + protected override void ConfigureParameter(DbParameter parameter) + { + if (parameter is ClickHouseDbParameter p) + { + p.ClickHouseType = StoreType; + } + } + + protected override Type WktReaderType { get; } +} 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..6fb8e94 --- /dev/null +++ b/EntityFrameworkCore.ClickHouse.NTS/Storage/Internal/Mapping/ClickHouseMultiPolygonTypeMapping.cs @@ -0,0 +1,46 @@ +using ClickHouse.Driver.ADO.Parameters; +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 System.Data.Common; + +namespace EntityFrameworkCore.ClickHouse.NTS.Storage.Internal.Mapping; + +public class ClickHouseMultiPolygonTypeMapping : RelationalGeometryTypeMapping +{ + public ClickHouseMultiPolygonTypeMapping() + : base(new ClickHouseMultiPolygonValueConverter(), "MultiPolygon", 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) + { + throw new NotImplementedException(); + } + + protected override int GetSrid(object value) + { + throw new NotImplementedException(); + } + + protected override void ConfigureParameter(DbParameter parameter) + { + if (parameter is ClickHouseDbParameter p) + { + p.ClickHouseType = StoreType; + } + } + + protected override Type WktReaderType { get; } +} 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..07ff24b --- /dev/null +++ b/EntityFrameworkCore.ClickHouse.NTS/Storage/Internal/Mapping/ClickHousePointTypeMapping.cs @@ -0,0 +1,45 @@ +using ClickHouse.Driver.ADO.Parameters; +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(), "Point", 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) + => ((Geometry)value).AsText(); + + protected override int GetSrid(object value) + => ((Geometry)value).SRID; + + protected override Type WktReaderType + => typeof(WKTReader); + + protected override void ConfigureParameter(DbParameter parameter) + { + if (parameter is ClickHouseDbParameter p) + { + p.ClickHouseType = 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..20e1438 --- /dev/null +++ b/EntityFrameworkCore.ClickHouse.NTS/Storage/Internal/Mapping/ClickHousePolygonTypeMapping.cs @@ -0,0 +1,46 @@ +using ClickHouse.Driver.ADO.Parameters; +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 System.Data.Common; + +namespace EntityFrameworkCore.ClickHouse.NTS.Storage.Internal.Mapping; + +public class ClickHousePolygonTypeMapping : RelationalGeometryTypeMapping +{ + public ClickHousePolygonTypeMapping() + : base(new ClickHousePolygonValueConverter(), "Polygon", 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) + { + throw new NotImplementedException(); + } + + protected override int GetSrid(object value) + { + throw new NotImplementedException(); + } + + protected override void ConfigureParameter(DbParameter parameter) + { + if (parameter is ClickHouseDbParameter p) + { + p.ClickHouseType = StoreType; + } + } + + protected override Type WktReaderType { get; } +} 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..d506566 --- /dev/null +++ b/EntityFrameworkCore.ClickHouse.NTS/Storage/Internal/Mapping/ClickHouseRingTypeMapping.cs @@ -0,0 +1,45 @@ +using ClickHouse.Driver.ADO.Parameters; +using EntityFrameworkCore.ClickHouse.NTS.Storage.Json; +using Microsoft.EntityFrameworkCore.Storage; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using NetTopologySuite.Geometries; +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) + { + throw new NotImplementedException(); + } + + protected override int GetSrid(object value) + { + throw new NotImplementedException(); + } + + protected override void ConfigureParameter(DbParameter parameter) + { + if (parameter is ClickHouseDbParameter p) + { + p.ClickHouseType = StoreType; + } + } + + protected override Type WktReaderType { get; } +} diff --git a/EntityFrameworkCore.ClickHouse.NTS/Storage/Json/ClickHouseJsonGeometryWktReaderWriter.cs b/EntityFrameworkCore.ClickHouse.NTS/Storage/Json/ClickHouseJsonGeometryWktReaderWriter.cs new file mode 100644 index 0000000..5144fcb --- /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); +} \ No newline at end of file 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 From 1064598a789799acd95f380ae3b6fd99a6ff7ead Mon Sep 17 00:00:00 2001 From: Denis Ivanov Date: Tue, 9 Dec 2025 21:48:13 +0200 Subject: [PATCH 2/2] Add converters --- .../EntityFrameworkCore.ClickHouse.NTS.csproj | 9 ++++ .../Extensions/GeometryExtensions.cs | 42 +++++++++++++++ ...NetTopologySuiteTypeMappingSourcePlugin.cs | 7 ++- .../Mapping/ClickHouseGeometryTypeMapping.cs | 26 +++++----- .../ClickHouseLineStringTypeMapping.cs | 52 +++++++++++++++++++ .../ClickHouseMultiPolygonTypeMapping.cs | 17 +++--- .../Mapping/ClickHousePointTypeMapping.cs | 13 +++-- .../Mapping/ClickHousePolygonTypeMapping.cs | 16 +++--- .../Mapping/ClickHouseRingTypeMapping.cs | 14 ++--- .../ClickHouseJsonGeometryWktReaderWriter.cs | 2 +- .../ClickHouseGeometryValueConverter.cs | 27 ++++++++++ .../ClickHouseLineStringValueConverter.cs | 20 +++++++ .../Extensions/StringExtensions.cs | 2 +- 13 files changed, 200 insertions(+), 47 deletions(-) create mode 100644 EntityFrameworkCore.ClickHouse.NTS/Extensions/GeometryExtensions.cs create mode 100644 EntityFrameworkCore.ClickHouse.NTS/Storage/Internal/Mapping/ClickHouseLineStringTypeMapping.cs create mode 100644 EntityFrameworkCore.ClickHouse.NTS/Storage/ValueConversion/Internal/ClickHouseGeometryValueConverter.cs create mode 100644 EntityFrameworkCore.ClickHouse.NTS/Storage/ValueConversion/Internal/ClickHouseLineStringValueConverter.cs diff --git a/EntityFrameworkCore.ClickHouse.NTS/EntityFrameworkCore.ClickHouse.NTS.csproj b/EntityFrameworkCore.ClickHouse.NTS/EntityFrameworkCore.ClickHouse.NTS.csproj index ae58cc5..973c29b 100644 --- a/EntityFrameworkCore.ClickHouse.NTS/EntityFrameworkCore.ClickHouse.NTS.csproj +++ b/EntityFrameworkCore.ClickHouse.NTS/EntityFrameworkCore.ClickHouse.NTS.csproj @@ -14,4 +14,13 @@ + + + Extensions\ClickHouseDbParameterExtensions.cs + + + Extensions\StringExtensions.cs + + + 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/Storage/Internal/ClickHouseNetTopologySuiteTypeMappingSourcePlugin.cs b/EntityFrameworkCore.ClickHouse.NTS/Storage/Internal/ClickHouseNetTopologySuiteTypeMappingSourcePlugin.cs index 2094592..f128aae 100644 --- a/EntityFrameworkCore.ClickHouse.NTS/Storage/Internal/ClickHouseNetTopologySuiteTypeMappingSourcePlugin.cs +++ b/EntityFrameworkCore.ClickHouse.NTS/Storage/Internal/ClickHouseNetTopologySuiteTypeMappingSourcePlugin.cs @@ -11,6 +11,7 @@ public class ClickHouseNetTopologySuiteTypeMappingSourcePlugin : IRelationalType 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) { @@ -18,7 +19,8 @@ public class ClickHouseNetTopologySuiteTypeMappingSourcePlugin : IRelationalType { "Ring", RingTypeMapping }, { "Polygon", PolygonTypeMapping }, { "MultiPolygon", MultiPolygonTypeMapping }, - { "Geometry", GeometryTypeMapping } + { "Geometry", GeometryTypeMapping }, + { "LineString", LineStringTypeMapping } }; private static readonly Dictionary ClrTypeMappings = new() @@ -27,7 +29,8 @@ public class ClickHouseNetTopologySuiteTypeMappingSourcePlugin : IRelationalType { typeof(MultiPoint), RingTypeMapping }, { typeof(Polygon), PolygonTypeMapping }, { typeof(MultiPolygon), MultiPolygonTypeMapping }, - { typeof(Geometry), GeometryTypeMapping } + { typeof(Geometry), GeometryTypeMapping }, + { typeof(LineString), LineStringTypeMapping } }; public RelationalTypeMapping? FindMapping(in RelationalTypeMappingInfo mappingInfo) diff --git a/EntityFrameworkCore.ClickHouse.NTS/Storage/Internal/Mapping/ClickHouseGeometryTypeMapping.cs b/EntityFrameworkCore.ClickHouse.NTS/Storage/Internal/Mapping/ClickHouseGeometryTypeMapping.cs index b4fcf2b..ec742b7 100644 --- a/EntityFrameworkCore.ClickHouse.NTS/Storage/Internal/Mapping/ClickHouseGeometryTypeMapping.cs +++ b/EntityFrameworkCore.ClickHouse.NTS/Storage/Internal/Mapping/ClickHouseGeometryTypeMapping.cs @@ -1,45 +1,47 @@ -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 ClickHouseGeometryTypeMapping : RelationalGeometryTypeMapping +public class ClickHouseGeometryTypeMapping : RelationalGeometryTypeMapping { public ClickHouseGeometryTypeMapping() - : base(null, "Geometry", ClickHouseJsonGeometryWktReaderWriter.Instance) + : base(ClickHouseGeometryValueConverter.Instance, "Geometry", ClickHouseJsonGeometryWktReaderWriter.Instance) { } - protected ClickHouseGeometryTypeMapping(RelationalTypeMappingParameters parameters, ValueConverter? converter) : base(parameters, converter) + protected ClickHouseGeometryTypeMapping(RelationalTypeMappingParameters parameters, ValueConverter? converter) + : base(parameters, converter) { } protected override RelationalTypeMapping Clone(RelationalTypeMappingParameters parameters) { - return new ClickHouseGeometryTypeMapping(parameters, SpatialConverter); + return new ClickHouseGeometryTypeMapping(parameters, (ValueConverter)base.Converter!); } protected override string AsText(object value) { - throw new NotImplementedException(); + return ((Geometry)value).AsText(); } protected override int GetSrid(object value) { - throw new NotImplementedException(); + return ((Geometry)value).SRID; } protected override void ConfigureParameter(DbParameter parameter) { - if (parameter is ClickHouseDbParameter p) - { - p.ClickHouseType = StoreType; - } + base.ConfigureParameter(parameter); + parameter.SetStoreType(StoreType); + parameter. } - protected override Type WktReaderType { get; } + 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 index 6fb8e94..cbeb2fa 100644 --- a/EntityFrameworkCore.ClickHouse.NTS/Storage/Internal/Mapping/ClickHouseMultiPolygonTypeMapping.cs +++ b/EntityFrameworkCore.ClickHouse.NTS/Storage/Internal/Mapping/ClickHouseMultiPolygonTypeMapping.cs @@ -1,9 +1,10 @@ -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; @@ -11,7 +12,7 @@ namespace EntityFrameworkCore.ClickHouse.NTS.Storage.Internal.Mapping; public class ClickHouseMultiPolygonTypeMapping : RelationalGeometryTypeMapping { public ClickHouseMultiPolygonTypeMapping() - : base(new ClickHouseMultiPolygonValueConverter(), "MultiPolygon", ClickHouseJsonGeometryWktReaderWriter.Instance) + : base(new ClickHouseMultiPolygonValueConverter(), Geometry.TypeNameMultiPolygon, ClickHouseJsonGeometryWktReaderWriter.Instance) { } @@ -26,21 +27,19 @@ protected override RelationalTypeMapping Clone(RelationalTypeMappingParameters p protected override string AsText(object value) { - throw new NotImplementedException(); + return ((MultiPolygon)value).AsText(); } protected override int GetSrid(object value) { - throw new NotImplementedException(); + return ((MultiPolygon)value).SRID; } protected override void ConfigureParameter(DbParameter parameter) { - if (parameter is ClickHouseDbParameter p) - { - p.ClickHouseType = StoreType; - } + base.ConfigureParameter(parameter); + parameter.SetStoreType(StoreType); } - protected override Type WktReaderType { get; } + 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 index 07ff24b..85029f3 100644 --- a/EntityFrameworkCore.ClickHouse.NTS/Storage/Internal/Mapping/ClickHousePointTypeMapping.cs +++ b/EntityFrameworkCore.ClickHouse.NTS/Storage/Internal/Mapping/ClickHousePointTypeMapping.cs @@ -1,4 +1,5 @@ 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; @@ -12,7 +13,7 @@ namespace EntityFrameworkCore.ClickHouse.NTS.Storage.Internal.Mapping; public class ClickHousePointTypeMapping : RelationalGeometryTypeMapping> { public ClickHousePointTypeMapping() - : base(new ClickHousePointValueConverter(), "Point", ClickHouseJsonGeometryWktReaderWriter.Instance) + : base(new ClickHousePointValueConverter(), Geometry.TypeNamePoint, ClickHouseJsonGeometryWktReaderWriter.Instance) { } @@ -27,19 +28,17 @@ protected override RelationalTypeMapping Clone(RelationalTypeMappingParameters p } protected override string AsText(object value) - => ((Geometry)value).AsText(); + => ((Point)value).AsText(); protected override int GetSrid(object value) - => ((Geometry)value).SRID; + => ((Point)value).SRID; protected override Type WktReaderType => typeof(WKTReader); protected override void ConfigureParameter(DbParameter parameter) { - if (parameter is ClickHouseDbParameter p) - { - p.ClickHouseType = StoreType; - } + 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 index 20e1438..09adbf0 100644 --- a/EntityFrameworkCore.ClickHouse.NTS/Storage/Internal/Mapping/ClickHousePolygonTypeMapping.cs +++ b/EntityFrameworkCore.ClickHouse.NTS/Storage/Internal/Mapping/ClickHousePolygonTypeMapping.cs @@ -1,9 +1,11 @@ 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; @@ -11,7 +13,7 @@ namespace EntityFrameworkCore.ClickHouse.NTS.Storage.Internal.Mapping; public class ClickHousePolygonTypeMapping : RelationalGeometryTypeMapping { public ClickHousePolygonTypeMapping() - : base(new ClickHousePolygonValueConverter(), "Polygon", ClickHouseJsonGeometryWktReaderWriter.Instance) + : base(new ClickHousePolygonValueConverter(), Geometry.TypeNamePolygon, ClickHouseJsonGeometryWktReaderWriter.Instance) { } @@ -26,21 +28,19 @@ protected override RelationalTypeMapping Clone(RelationalTypeMappingParameters p protected override string AsText(object value) { - throw new NotImplementedException(); + return ((Polygon)value).AsText(); } protected override int GetSrid(object value) { - throw new NotImplementedException(); + return ((Polygon)value).SRID; } protected override void ConfigureParameter(DbParameter parameter) { - if (parameter is ClickHouseDbParameter p) - { - p.ClickHouseType = StoreType; - } + base.ConfigureParameter(parameter); + parameter.SetStoreType(StoreType); } - protected override Type WktReaderType { get; } + 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 index d506566..2149309 100644 --- a/EntityFrameworkCore.ClickHouse.NTS/Storage/Internal/Mapping/ClickHouseRingTypeMapping.cs +++ b/EntityFrameworkCore.ClickHouse.NTS/Storage/Internal/Mapping/ClickHouseRingTypeMapping.cs @@ -1,8 +1,10 @@ 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; @@ -25,21 +27,19 @@ protected override RelationalTypeMapping Clone(RelationalTypeMappingParameters p protected override string AsText(object value) { - throw new NotImplementedException(); + return ((MultiPoint)value).AsText(); } protected override int GetSrid(object value) { - throw new NotImplementedException(); + return ((MultiPoint)value).SRID; } protected override void ConfigureParameter(DbParameter parameter) { - if (parameter is ClickHouseDbParameter p) - { - p.ClickHouseType = StoreType; - } + base.ConfigureParameter(parameter); + parameter.SetStoreType(StoreType); } - protected override Type WktReaderType { get; } + protected override Type WktReaderType => typeof(WKTReader); } diff --git a/EntityFrameworkCore.ClickHouse.NTS/Storage/Json/ClickHouseJsonGeometryWktReaderWriter.cs b/EntityFrameworkCore.ClickHouse.NTS/Storage/Json/ClickHouseJsonGeometryWktReaderWriter.cs index 5144fcb..e993dbe 100644 --- a/EntityFrameworkCore.ClickHouse.NTS/Storage/Json/ClickHouseJsonGeometryWktReaderWriter.cs +++ b/EntityFrameworkCore.ClickHouse.NTS/Storage/Json/ClickHouseJsonGeometryWktReaderWriter.cs @@ -24,4 +24,4 @@ public override void ToJsonTyped(Utf8JsonWriter writer, Geometry value) => writer.WriteStringValue(value.ToText()); public override Expression ConstructorExpression => Expression.Property(null, InstanceProperty); -} \ No newline at end of file +} 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/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 +}