Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix regression to allow custom value conversion mapping when the ClrType is of non-NTS type. plus added unit test. #1935

Open
wants to merge 4 commits into
base: 8.0-maint
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion Dependencies.targets
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
<PackageReference Update="MySqlConnector.DependencyInjection" Version="2.3.5" />

<PackageReference Update="NetTopologySuite" Version="2.5.0" />
<PackageReference Update="System.Text.Json" Version="8.0.3" />
<PackageReference Update="System.Text.Json" Version="8.0.5" />
<PackageReference Update="Newtonsoft.Json" Version="13.0.3" />

<PackageReference Update="Castle.Core" Version="5.1.1" />
Expand Down Expand Up @@ -46,6 +46,7 @@
<PackageReference Update="DotNetAnalyzers.DocumentationAnalyzers" Version="1.0.0-beta.59" />
<PackageReference Update="StyleCop.Analyzers" Version="1.1.118" PrivateAssets="All" />
<PackageReference Update="Microsoft.CodeAnalysis" Version="4.5.0" />
<PackageReference Update="Shouldly" Version="4.0.3" />
<PackageReference Update="Microsoft.CodeAnalysis.Features" Version="4.5.0" />
<PackageReference Update="Microsoft.CodeAnalysis.CSharp.Features" Version="4.5.0" />
<PackageReference Update="Microsoft.CodeAnalysis.VisualBasic.Features" Version="4.5.0" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,16 +80,28 @@ public virtual RelationalTypeMapping FindMapping(in RelationalTypeMappingInfo ma
string defaultStoreType = null;
Type defaultClrType = null;

return (clrType != null
&& TryGetDefaultStoreType(clrType, out defaultStoreType))
|| (storeTypeName != null
&& _spatialStoreTypeMappings.TryGetValue(storeTypeName, out defaultClrType))
? (RelationalTypeMapping)Activator.CreateInstance(
typeof(MySqlGeometryTypeMapping<>).MakeGenericType(clrType ?? defaultClrType ?? typeof(Geometry)),
_geometryServices,
storeTypeName ?? defaultStoreType ?? "geometry",
_options)
: null;
var hasDefaultStoreType = clrType != null
&& TryGetDefaultStoreType(clrType, out defaultStoreType);
var hasDefaultClrType = storeTypeName != null
&& _spatialStoreTypeMappings.TryGetValue(storeTypeName, out defaultClrType);

// NOTE: If the incoming user-specified 'clrType' is of the known calculated 'defaultClrType', ONLY then proceeed
// with the creation of 'MySqlGeometryTypeMapping'.
var hasDefaultStoreOrClrType = hasDefaultStoreType || hasDefaultClrType;
var isClrTypeNotAssignable = clrType != null
&& !clrType.IsAssignableFrom(defaultClrType);

if (!hasDefaultStoreOrClrType
|| (!hasDefaultStoreType && isClrTypeNotAssignable))
{
return null;
}

return (RelationalTypeMapping)Activator.CreateInstance(
typeof(MySqlGeometryTypeMapping<>).MakeGenericType(clrType ?? defaultClrType ?? typeof(Geometry)),
_geometryServices,
storeTypeName ?? defaultStoreType ?? "geometry",
_options);
}

private static bool TryGetDefaultStoreType(Type type, out string defaultStoreType)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" />
<PackageReference Include="Microsoft.Extensions.Configuration.FileExtensions" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" />
<PackageReference Include="Shouldly" />
</ItemGroup>

<ItemGroup Condition="'$(LocalEFCoreRepository)' == ''">
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
namespace Pomelo.EntityFrameworkCore.MySql.FunctionalTests.NTS;

using System;
using System.Linq;
using System.Threading.Tasks;

using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using NetTopologySuite.Geometries;
using Pomelo.EntityFrameworkCore.MySql.Tests;
using Shouldly;
using Xunit;

public class CustomValueConvertersForNonNTSClrTypesMySqlTest
{
[Fact]
public async Task NonNTS_ClrType_CanBe_Mapped_to_CustomValueConverters()
{
var services = new ServiceCollection()
.AddDbContext<CustomDbContext>()
.AddSingleton(
new DbContextOptionsBuilder<CustomDbContext>()
.UseMySql(
AppConfig.ConnectionString,
AppConfig.ServerVersion,
options =>
{
options.UseNetTopologySuite();
})
.EnableSensitiveDataLogging()
.LogTo(Console.WriteLine, LogLevel.Debug)
.Options);

var provider = services.BuildServiceProvider();
await using var context = provider.GetRequiredService<CustomDbContext>();

// Validate `MySqlNetTopologySuiteTypeMappingSourcePlugin.FindMapping()` doesn't throw.
await Should.NotThrowAsync(context.Database.EnsureDeletedAsync());
await Should.NotThrowAsync(context.Database.EnsureCreatedAsync());

Check failure on line 41 in test/EFCore.MySql.FunctionalTests/NTS/CustomValueConvertersForNonNTSClrTypesMySqlTest.cs

View workflow job for this annotation

GitHub Actions / BuildAndTest (8.0.36-mysql, ubuntu-latest)

Pomelo.EntityFrameworkCore.MySql.FunctionalTests.NTS.CustomValueConvertersForNonNTSClrTypesMySqlTest.NonNTS_ClrType_CanBe_Mapped_to_CustomValueConverters

Shouldly.ShouldAssertException : `context.Database.EnsureDeletedAsync()` should not throw but threw MySqlConnector.MySqlException with message "No database selected"

Check failure on line 41 in test/EFCore.MySql.FunctionalTests/NTS/CustomValueConvertersForNonNTSClrTypesMySqlTest.cs

View workflow job for this annotation

GitHub Actions / BuildAndTest (5.7.44-mysql, ubuntu-latest)

Pomelo.EntityFrameworkCore.MySql.FunctionalTests.NTS.CustomValueConvertersForNonNTSClrTypesMySqlTest.NonNTS_ClrType_CanBe_Mapped_to_CustomValueConverters

Shouldly.ShouldAssertException : `context.Database.EnsureDeletedAsync()` should not throw but threw MySqlConnector.MySqlException with message "No database selected"

Check failure on line 41 in test/EFCore.MySql.FunctionalTests/NTS/CustomValueConvertersForNonNTSClrTypesMySqlTest.cs

View workflow job for this annotation

GitHub Actions / BuildAndTest (5.7.44-mysql, windows-latest)

Pomelo.EntityFrameworkCore.MySql.FunctionalTests.NTS.CustomValueConvertersForNonNTSClrTypesMySqlTest.NonNTS_ClrType_CanBe_Mapped_to_CustomValueConverters

Shouldly.ShouldAssertException : `context.Database.EnsureDeletedAsync()` should not throw but threw MySqlConnector.MySqlException with message "No database selected"

Check failure on line 41 in test/EFCore.MySql.FunctionalTests/NTS/CustomValueConvertersForNonNTSClrTypesMySqlTest.cs

View workflow job for this annotation

GitHub Actions / BuildAndTest (11.3.2-mariadb, ubuntu-latest)

Pomelo.EntityFrameworkCore.MySql.FunctionalTests.NTS.CustomValueConvertersForNonNTSClrTypesMySqlTest.NonNTS_ClrType_CanBe_Mapped_to_CustomValueConverters

Shouldly.ShouldAssertException : `context.Database.EnsureDeletedAsync()` should not throw but threw MySqlConnector.MySqlException with message "No database selected"

Check failure on line 41 in test/EFCore.MySql.FunctionalTests/NTS/CustomValueConvertersForNonNTSClrTypesMySqlTest.cs

View workflow job for this annotation

GitHub Actions / BuildAndTest (11.3.2-mariadb, windows-latest)

Pomelo.EntityFrameworkCore.MySql.FunctionalTests.NTS.CustomValueConvertersForNonNTSClrTypesMySqlTest.NonNTS_ClrType_CanBe_Mapped_to_CustomValueConverters

Shouldly.ShouldAssertException : `context.Database.EnsureDeletedAsync()` should not throw but threw MySqlConnector.MySqlException with message "No database selected"

Check failure on line 41 in test/EFCore.MySql.FunctionalTests/NTS/CustomValueConvertersForNonNTSClrTypesMySqlTest.cs

View workflow job for this annotation

GitHub Actions / BuildAndTest (11.2.3-mariadb, ubuntu-latest)

Pomelo.EntityFrameworkCore.MySql.FunctionalTests.NTS.CustomValueConvertersForNonNTSClrTypesMySqlTest.NonNTS_ClrType_CanBe_Mapped_to_CustomValueConverters

Shouldly.ShouldAssertException : `context.Database.EnsureDeletedAsync()` should not throw but threw MySqlConnector.MySqlException with message "No database selected"

Check failure on line 41 in test/EFCore.MySql.FunctionalTests/NTS/CustomValueConvertersForNonNTSClrTypesMySqlTest.cs

View workflow job for this annotation

GitHub Actions / BuildAndTest (11.2.3-mariadb, windows-latest)

Pomelo.EntityFrameworkCore.MySql.FunctionalTests.NTS.CustomValueConvertersForNonNTSClrTypesMySqlTest.NonNTS_ClrType_CanBe_Mapped_to_CustomValueConverters

Shouldly.ShouldAssertException : `context.Database.EnsureDeletedAsync()` should not throw but threw MySqlConnector.MySqlException with message "No database selected"

Check failure on line 41 in test/EFCore.MySql.FunctionalTests/NTS/CustomValueConvertersForNonNTSClrTypesMySqlTest.cs

View workflow job for this annotation

GitHub Actions / BuildAndTest (11.1.4-mariadb, ubuntu-latest)

Pomelo.EntityFrameworkCore.MySql.FunctionalTests.NTS.CustomValueConvertersForNonNTSClrTypesMySqlTest.NonNTS_ClrType_CanBe_Mapped_to_CustomValueConverters

Shouldly.ShouldAssertException : `context.Database.EnsureDeletedAsync()` should not throw but threw MySqlConnector.MySqlException with message "No database selected"

Check failure on line 41 in test/EFCore.MySql.FunctionalTests/NTS/CustomValueConvertersForNonNTSClrTypesMySqlTest.cs

View workflow job for this annotation

GitHub Actions / BuildAndTest (11.1.4-mariadb, windows-latest)

Pomelo.EntityFrameworkCore.MySql.FunctionalTests.NTS.CustomValueConvertersForNonNTSClrTypesMySqlTest.NonNTS_ClrType_CanBe_Mapped_to_CustomValueConverters

Shouldly.ShouldAssertException : `context.Database.EnsureDeletedAsync()` should not throw but threw MySqlConnector.MySqlException with message "No database selected"

Check failure on line 41 in test/EFCore.MySql.FunctionalTests/NTS/CustomValueConvertersForNonNTSClrTypesMySqlTest.cs

View workflow job for this annotation

GitHub Actions / BuildAndTest (11.0.5-mariadb, ubuntu-latest)

Pomelo.EntityFrameworkCore.MySql.FunctionalTests.NTS.CustomValueConvertersForNonNTSClrTypesMySqlTest.NonNTS_ClrType_CanBe_Mapped_to_CustomValueConverters

Shouldly.ShouldAssertException : `context.Database.EnsureDeletedAsync()` should not throw but threw MySqlConnector.MySqlException with message "No database selected"

Check failure on line 41 in test/EFCore.MySql.FunctionalTests/NTS/CustomValueConvertersForNonNTSClrTypesMySqlTest.cs

View workflow job for this annotation

GitHub Actions / BuildAndTest (11.0.5-mariadb, windows-latest)

Pomelo.EntityFrameworkCore.MySql.FunctionalTests.NTS.CustomValueConvertersForNonNTSClrTypesMySqlTest.NonNTS_ClrType_CanBe_Mapped_to_CustomValueConverters

Shouldly.ShouldAssertException : `context.Database.EnsureDeletedAsync()` should not throw but threw MySqlConnector.MySqlException with message "No database selected"

Check failure on line 41 in test/EFCore.MySql.FunctionalTests/NTS/CustomValueConvertersForNonNTSClrTypesMySqlTest.cs

View workflow job for this annotation

GitHub Actions / BuildAndTest (10.11.7-mariadb, ubuntu-latest)

Pomelo.EntityFrameworkCore.MySql.FunctionalTests.NTS.CustomValueConvertersForNonNTSClrTypesMySqlTest.NonNTS_ClrType_CanBe_Mapped_to_CustomValueConverters

Shouldly.ShouldAssertException : `context.Database.EnsureDeletedAsync()` should not throw but threw MySqlConnector.MySqlException with message "No database selected"

Check failure on line 41 in test/EFCore.MySql.FunctionalTests/NTS/CustomValueConvertersForNonNTSClrTypesMySqlTest.cs

View workflow job for this annotation

GitHub Actions / BuildAndTest (10.11.7-mariadb, windows-latest)

Pomelo.EntityFrameworkCore.MySql.FunctionalTests.NTS.CustomValueConvertersForNonNTSClrTypesMySqlTest.NonNTS_ClrType_CanBe_Mapped_to_CustomValueConverters

Shouldly.ShouldAssertException : `context.Database.EnsureDeletedAsync()` should not throw but threw MySqlConnector.MySqlException with message "No database selected"

Check failure on line 41 in test/EFCore.MySql.FunctionalTests/NTS/CustomValueConvertersForNonNTSClrTypesMySqlTest.cs

View workflow job for this annotation

GitHub Actions / BuildAndTest (10.10.7-mariadb, ubuntu-latest)

Pomelo.EntityFrameworkCore.MySql.FunctionalTests.NTS.CustomValueConvertersForNonNTSClrTypesMySqlTest.NonNTS_ClrType_CanBe_Mapped_to_CustomValueConverters

Shouldly.ShouldAssertException : `context.Database.EnsureDeletedAsync()` should not throw but threw MySqlConnector.MySqlException with message "No database selected"

Check failure on line 41 in test/EFCore.MySql.FunctionalTests/NTS/CustomValueConvertersForNonNTSClrTypesMySqlTest.cs

View workflow job for this annotation

GitHub Actions / BuildAndTest (10.10.7-mariadb, windows-latest)

Pomelo.EntityFrameworkCore.MySql.FunctionalTests.NTS.CustomValueConvertersForNonNTSClrTypesMySqlTest.NonNTS_ClrType_CanBe_Mapped_to_CustomValueConverters

Shouldly.ShouldAssertException : `context.Database.EnsureDeletedAsync()` should not throw but threw MySqlConnector.MySqlException with message "No database selected"

Check failure on line 41 in test/EFCore.MySql.FunctionalTests/NTS/CustomValueConvertersForNonNTSClrTypesMySqlTest.cs

View workflow job for this annotation

GitHub Actions / BuildAndTest (10.6.17-mariadb, ubuntu-latest)

Pomelo.EntityFrameworkCore.MySql.FunctionalTests.NTS.CustomValueConvertersForNonNTSClrTypesMySqlTest.NonNTS_ClrType_CanBe_Mapped_to_CustomValueConverters

Shouldly.ShouldAssertException : `context.Database.EnsureDeletedAsync()` should not throw but threw MySqlConnector.MySqlException with message "No database selected"

Check failure on line 41 in test/EFCore.MySql.FunctionalTests/NTS/CustomValueConvertersForNonNTSClrTypesMySqlTest.cs

View workflow job for this annotation

GitHub Actions / BuildAndTest (10.6.17-mariadb, windows-latest)

Pomelo.EntityFrameworkCore.MySql.FunctionalTests.NTS.CustomValueConvertersForNonNTSClrTypesMySqlTest.NonNTS_ClrType_CanBe_Mapped_to_CustomValueConverters

Shouldly.ShouldAssertException : `context.Database.EnsureDeletedAsync()` should not throw but threw MySqlConnector.MySqlException with message "No database selected"

Check failure on line 41 in test/EFCore.MySql.FunctionalTests/NTS/CustomValueConvertersForNonNTSClrTypesMySqlTest.cs

View workflow job for this annotation

GitHub Actions / BuildAndTest (10.5.24-mariadb, ubuntu-latest)

Pomelo.EntityFrameworkCore.MySql.FunctionalTests.NTS.CustomValueConvertersForNonNTSClrTypesMySqlTest.NonNTS_ClrType_CanBe_Mapped_to_CustomValueConverters

Shouldly.ShouldAssertException : `context.Database.EnsureDeletedAsync()` should not throw but threw MySqlConnector.MySqlException with message "No database selected"

Check failure on line 41 in test/EFCore.MySql.FunctionalTests/NTS/CustomValueConvertersForNonNTSClrTypesMySqlTest.cs

View workflow job for this annotation

GitHub Actions / BuildAndTest (10.5.24-mariadb, windows-latest)

Pomelo.EntityFrameworkCore.MySql.FunctionalTests.NTS.CustomValueConvertersForNonNTSClrTypesMySqlTest.NonNTS_ClrType_CanBe_Mapped_to_CustomValueConverters

Shouldly.ShouldAssertException : `context.Database.EnsureDeletedAsync()` should not throw but threw MySqlConnector.MySqlException with message "No database selected"

Check failure on line 41 in test/EFCore.MySql.FunctionalTests/NTS/CustomValueConvertersForNonNTSClrTypesMySqlTest.cs

View workflow job for this annotation

GitHub Actions / BuildAndTest (10.4.33-mariadb, ubuntu-latest)

Pomelo.EntityFrameworkCore.MySql.FunctionalTests.NTS.CustomValueConvertersForNonNTSClrTypesMySqlTest.NonNTS_ClrType_CanBe_Mapped_to_CustomValueConverters

Shouldly.ShouldAssertException : `context.Database.EnsureDeletedAsync()` should not throw but threw MySqlConnector.MySqlException with message "No database selected"

Check failure on line 41 in test/EFCore.MySql.FunctionalTests/NTS/CustomValueConvertersForNonNTSClrTypesMySqlTest.cs

View workflow job for this annotation

GitHub Actions / BuildAndTest (10.4.33-mariadb, windows-latest)

Pomelo.EntityFrameworkCore.MySql.FunctionalTests.NTS.CustomValueConvertersForNonNTSClrTypesMySqlTest.NonNTS_ClrType_CanBe_Mapped_to_CustomValueConverters

Shouldly.ShouldAssertException : `context.Database.EnsureDeletedAsync()` should not throw but threw MySqlConnector.MySqlException with message "No database selected"
}
}

public sealed class CustomDbContext(DbContextOptions<CustomDbContext> options) : DbContext(options)
{
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
var testClass = modelBuilder.Entity<TestClass>();
testClass.Property(t => t.Vertices)
.HasColumnType("GEOMETRY")
.HasConversion(new MysqlGeometryWkbValueConverter());

var testClass2 = modelBuilder.Entity<TestClass2>();
testClass2.Property(t => t.Vertices)
.HasColumnType("GEOMETRY");
}
}

public class TestClass
{
public int Id { get; set; }
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
public byte[] Vertices { get; set; }
#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
}

public class TestClass2
{
public int Id { get; set; }

#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
public Geometry Vertices { get; set; }
#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
}

/// <summary>
/// MySql's internal geometry format is WKB with an initial 4
/// bytes for the SRID:
/// https://dev.mysql.com/doc/refman/5.7/en/gis-data-formats.html
/// </summary>
public class MysqlGeometryWkbValueConverter : ValueConverter<byte[], byte[]>
{
public MysqlGeometryWkbValueConverter()
: base(
clr => AddSRID(clr),
col => StripSRID(col))
{
}

private static byte[] AddSRID(byte[] wkb) =>
new byte[] { 0, 0, 0, 0, }.Concat(wkb).ToArray();

private static byte[] StripSRID(byte[] col) =>
col.Skip(4).ToArray();
}
Loading