Browse Source

Add MySQL type mapping plugin for Guid[] query parameters

pull/25896/head
maliming 2 months ago
parent
commit
11b78a9e7c
No known key found for this signature in database GPG Key ID: A646B9CB645ECEA4
  1. 20
      framework/src/Volo.Abp.EntityFrameworkCore.MySQL/Volo/Abp/EntityFrameworkCore/AbpDbContextConfigurationContextMySQLExtensions.cs
  2. 48
      framework/src/Volo.Abp.EntityFrameworkCore.MySQL/Volo/Abp/EntityFrameworkCore/MySQL/AbpMySQLDbContextOptionsExtension.cs
  3. 25
      framework/src/Volo.Abp.EntityFrameworkCore.MySQL/Volo/Abp/EntityFrameworkCore/MySQL/MySQLGuidArrayTypeMappingSourcePlugin.cs
  4. 1
      framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo.Abp.EntityFrameworkCore.Tests.csproj
  5. 58
      framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/MySQL/MySQLGuidArrayTypeMappingSourcePlugin_Tests.cs

20
framework/src/Volo.Abp.EntityFrameworkCore.MySQL/Volo/Abp/EntityFrameworkCore/AbpDbContextConfigurationContextMySQLExtensions.cs

@ -1,7 +1,9 @@
using JetBrains.Annotations; using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using System; using System;
using Volo.Abp.EntityFrameworkCore.DependencyInjection; using Volo.Abp.EntityFrameworkCore.DependencyInjection;
using Volo.Abp.EntityFrameworkCore.MySQL;
namespace Volo.Abp.EntityFrameworkCore; namespace Volo.Abp.EntityFrameworkCore;
@ -11,21 +13,21 @@ public static class AbpDbContextConfigurationContextMySQLExtensions
[NotNull] this AbpDbContextConfigurationContext context, [NotNull] this AbpDbContextConfigurationContext context,
Action<MySql.EntityFrameworkCore.Infrastructure.MySQLDbContextOptionsBuilder>? mySQLOptionsAction = null) Action<MySql.EntityFrameworkCore.Infrastructure.MySQLDbContextOptionsBuilder>? mySQLOptionsAction = null)
{ {
if (context.ExistingConnection != null) var dbContextOptionsBuilder = context.ExistingConnection != null
{ ? context.DbContextOptions.UseMySQL(context.ExistingConnection, optionsBuilder =>
return context.DbContextOptions.UseMySQL(context.ExistingConnection, optionsBuilder =>
{ {
optionsBuilder.UseQuerySplittingBehavior(QuerySplittingBehavior.SplitQuery); optionsBuilder.UseQuerySplittingBehavior(QuerySplittingBehavior.SplitQuery);
mySQLOptionsAction?.Invoke(optionsBuilder); mySQLOptionsAction?.Invoke(optionsBuilder);
}); })
} : context.DbContextOptions.UseMySQL(context.ConnectionString, optionsBuilder =>
else
{
return context.DbContextOptions.UseMySQL(context.ConnectionString, optionsBuilder =>
{ {
optionsBuilder.UseQuerySplittingBehavior(QuerySplittingBehavior.SplitQuery); optionsBuilder.UseQuerySplittingBehavior(QuerySplittingBehavior.SplitQuery);
mySQLOptionsAction?.Invoke(optionsBuilder); mySQLOptionsAction?.Invoke(optionsBuilder);
}); });
}
((IDbContextOptionsBuilderInfrastructure)dbContextOptionsBuilder)
.AddOrUpdateExtension(new AbpMySQLDbContextOptionsExtension());
return dbContextOptionsBuilder;
} }
} }

48
framework/src/Volo.Abp.EntityFrameworkCore.MySQL/Volo/Abp/EntityFrameworkCore/MySQL/AbpMySQLDbContextOptionsExtension.cs

@ -0,0 +1,48 @@
using System.Collections.Generic;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Storage;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
namespace Volo.Abp.EntityFrameworkCore.MySQL;
/* Registers ABP services into the EF Core internal service provider to patch
* MySQL provider issues (currently the Guid[] type mapping plugin). */
internal sealed class AbpMySQLDbContextOptionsExtension : IDbContextOptionsExtension
{
public DbContextOptionsExtensionInfo Info => new ExtensionInfo(this);
public void ApplyServices(IServiceCollection services)
{
services.TryAddEnumerable(
ServiceDescriptor.Singleton<IRelationalTypeMappingSourcePlugin, MySQLGuidArrayTypeMappingSourcePlugin>());
}
public void Validate(IDbContextOptions options)
{
}
private sealed class ExtensionInfo : DbContextOptionsExtensionInfo
{
public ExtensionInfo(IDbContextOptionsExtension extension)
: base(extension)
{
}
public override bool IsDatabaseProvider => false;
public override string LogFragment => "using AbpMySQL ";
public override int GetServiceProviderHashCode() => 0;
public override bool ShouldUseSameServiceProvider(DbContextOptionsExtensionInfo other)
{
return other is ExtensionInfo;
}
public override void PopulateDebugInfo(IDictionary<string, string> debugInfo)
{
debugInfo["Volo.Abp.EntityFrameworkCore.MySQL"] = "1";
}
}
}

25
framework/src/Volo.Abp.EntityFrameworkCore.MySQL/Volo/Abp/EntityFrameworkCore/MySQL/MySQLGuidArrayTypeMappingSourcePlugin.cs

@ -0,0 +1,25 @@
using System;
using System.Data;
using Microsoft.EntityFrameworkCore.Storage;
namespace Volo.Abp.EntityFrameworkCore.MySQL;
/* MySql.EntityFrameworkCore (up to 10.0.9) maps Guid[] query parameters to its
* scalar GUID mapping and throws NullReferenceException at parameter binding.
* This plugin runs before the provider's own lookup and returns the collection
* mapping the provider already builds for List<Guid>. Remove once the provider
* handles Guid[] parameters. */
internal sealed class MySQLGuidArrayTypeMappingSourcePlugin : IRelationalTypeMappingSourcePlugin
{
public RelationalTypeMapping? FindMapping(in RelationalTypeMappingInfo mappingInfo)
{
if (mappingInfo.ClrType == typeof(Guid[]) && mappingInfo.ElementTypeMapping is not null)
{
return new StringTypeMapping("longtext", DbType.String).Clone(
clrType: typeof(Guid[]),
elementMapping: mappingInfo.ElementTypeMapping);
}
return null;
}
}

1
framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo.Abp.EntityFrameworkCore.Tests.csproj

@ -9,6 +9,7 @@
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\..\src\Volo.Abp.Autofac\Volo.Abp.Autofac.csproj" /> <ProjectReference Include="..\..\src\Volo.Abp.Autofac\Volo.Abp.Autofac.csproj" />
<ProjectReference Include="..\..\src\Volo.Abp.EntityFrameworkCore.MySQL\Volo.Abp.EntityFrameworkCore.MySQL.csproj" />
<ProjectReference Include="..\..\src\Volo.Abp.EntityFrameworkCore.Sqlite\Volo.Abp.EntityFrameworkCore.Sqlite.csproj" /> <ProjectReference Include="..\..\src\Volo.Abp.EntityFrameworkCore.Sqlite\Volo.Abp.EntityFrameworkCore.Sqlite.csproj" />
<ProjectReference Include="..\AbpTestBase\AbpTestBase.csproj" /> <ProjectReference Include="..\AbpTestBase\AbpTestBase.csproj" />
<ProjectReference Include="..\Volo.Abp.EntityFrameworkCore.Tests.SecondContext\Volo.Abp.EntityFrameworkCore.Tests.SecondContext.csproj" /> <ProjectReference Include="..\Volo.Abp.EntityFrameworkCore.Tests.SecondContext\Volo.Abp.EntityFrameworkCore.Tests.SecondContext.csproj" />

58
framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/MySQL/MySQLGuidArrayTypeMappingSourcePlugin_Tests.cs

@ -0,0 +1,58 @@
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Storage;
using Microsoft.Extensions.DependencyInjection;
using Shouldly;
using Volo.Abp.EntityFrameworkCore.DependencyInjection;
using Xunit;
namespace Volo.Abp.EntityFrameworkCore.MySQL;
public class MySQLGuidArrayTypeMappingSourcePlugin_Tests
{
/* Runs without a MySQL server: type mapping lookup is metadata-only, no
* connection is opened. When upgrading the provider, verify its native
* Guid[] mapping without the plugin registered; once the provider handles
* Guid[] itself, remove MySQLGuidArrayTypeMappingSourcePlugin together
* with this test. */
[Fact]
public void UseMySQL_Should_Map_Guid_Array_Parameter_To_A_Collection_Mapping()
{
var services = new ServiceCollection();
services.AddLogging();
var configurationContext = new AbpDbContextConfigurationContext(
"Server=localhost;Database=_;Uid=_;Pwd=_;",
services.BuildServiceProvider(),
null,
null);
configurationContext.UseMySQL();
using var dbContext = new PluginTestDbContext(configurationContext.DbContextOptions.Options);
var typeMappingSource = dbContext.GetService<IRelationalTypeMappingSource>();
var elementMapping = typeMappingSource.FindMapping(typeof(Guid))!;
var mapping = typeMappingSource.FindMapping(typeof(Guid[]), dbContext.Model, elementMapping);
mapping.ShouldNotBeNull();
mapping.ClrType.ShouldBe(typeof(Guid[]));
mapping.StoreType.ShouldBe("longtext");
mapping.ElementTypeMapping.ShouldBe(elementMapping);
}
private class PluginTestDbContext : DbContext
{
public PluginTestDbContext(DbContextOptions options)
: base(options)
{
}
public DbSet<PluginTestEntity> Entities => Set<PluginTestEntity>();
}
private class PluginTestEntity
{
public Guid Id { get; set; }
}
}
Loading…
Cancel
Save