mirror of https://github.com/abpframework/abp.git
5 changed files with 143 additions and 9 deletions
@ -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"; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -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; |
||||
|
} |
||||
|
} |
||||
@ -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…
Reference in new issue