Browse Source

Merge pull request #25900 from abpframework/auto-merge/rel-10-5/4741

Merge branch rel-10.6 with rel-10.5
pull/25901/head
Volosoft Agent 1 week ago
committed by GitHub
parent
commit
c2be7b683e
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 2
      Directory.Packages.props
  2. 1
      docs/en/package-version-changes.md
  3. 20
      framework/src/Volo.Abp.EntityFrameworkCore.MySQL/Volo/Abp/EntityFrameworkCore/AbpDbContextConfigurationContextMySQLExtensions.cs
  4. 48
      framework/src/Volo.Abp.EntityFrameworkCore.MySQL/Volo/Abp/EntityFrameworkCore/MySQL/AbpMySQLDbContextOptionsExtension.cs
  5. 25
      framework/src/Volo.Abp.EntityFrameworkCore.MySQL/Volo/Abp/EntityFrameworkCore/MySQL/MySQLGuidArrayTypeMappingSourcePlugin.cs
  6. 45
      framework/src/Volo.Abp.Mapperly/Volo/Abp/Mapperly/MapperlyAutoObjectMappingProvider.cs
  7. 1
      framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo.Abp.EntityFrameworkCore.Tests.csproj
  8. 58
      framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/MySQL/MySQLGuidArrayTypeMappingSourcePlugin_Tests.cs
  9. 6
      framework/test/Volo.Abp.Mapperly.Tests/Mapperly/AbpAutoMapperExtensibleDtoExtensions_Tests.cs
  10. 3
      framework/test/Volo.Abp.Mapperly.Tests/Volo/Abp/Mapperly/AbpReverseMapperly_Tests.cs
  11. 87
      framework/test/Volo.Abp.Mapperly.Tests/Volo/Abp/Mapperly/MapExtraPropertiesDefaultSeed_Tests.cs
  12. 15
      framework/test/Volo.Abp.Mapperly.Tests/Volo/Abp/Mapperly/MapperlyTestModule.cs
  13. 58
      framework/test/Volo.Abp.Mapperly.Tests/Volo/Abp/Mapperly/SampleClasses/MapperlyMappers.cs
  14. 25
      modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/IdentityDbContextModelBuilderExtensions.cs

2
Directory.Packages.props

@ -142,7 +142,7 @@
<PackageVersion Include="Polly" Version="8.6.3" />
<PackageVersion Include="Polly.Extensions.Http" Version="3.0.0" />
<PackageVersion Include="Pomelo.EntityFrameworkCore.MySql" Version="9.0.0" />
<PackageVersion Include="MySql.EntityFrameworkCore" Version="10.0.1" />
<PackageVersion Include="MySql.EntityFrameworkCore" Version="10.0.9" />
<PackageVersion Include="Quartz" Version="3.15.0" />
<PackageVersion Include="Quartz.Extensions.DependencyInjection" Version="3.15.0" />
<PackageVersion Include="Quartz.Plugins.TimeZoneConverter" Version="3.15.0" />

1
docs/en/package-version-changes.md

@ -82,6 +82,7 @@
| Package | Old Version | New Version | PR |
|---------|-------------|-------------|-----|
| MySql.EntityFrameworkCore | 10.0.1 | 10.0.9 | #25896 |
| Scriban | 7.2.1 | 7.2.5 | #25862 |
| Swashbuckle.AspNetCore | 10.0.1 | 10.2.3 | #25759 |
| System.Security.Cryptography.Xml | 10.0.7 | 10.0.10 | #25862 |

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

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

45
framework/src/Volo.Abp.Mapperly/Volo/Abp/Mapperly/MapperlyAutoObjectMappingProvider.cs

@ -7,6 +7,7 @@ using System.Linq.Expressions;
using System.Reflection;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.Data;
using Volo.Abp.DynamicProxy;
using Volo.Abp.ObjectExtending;
using Volo.Abp.ObjectMapping;
@ -44,9 +45,13 @@ public class MapperlyAutoObjectMappingProvider : IAutoObjectMappingProvider
var mapper = ServiceProvider.GetService<IAbpMapperlyMapper<TSource, TDestination>>();
if (mapper != null)
{
var mapExtraPropertiesAttribute = mapper.GetType().GetSingleAttributeOrNull<MapExtraPropertiesAttribute>();
mapper.BeforeMap((TSource)source);
var destination = mapper.Map((TSource)source);
TryMapExtraProperties(mapper.GetType().GetSingleAttributeOrNull<MapExtraPropertiesAttribute>(), (TSource)source, destination, new ExtraPropertyDictionary());
var destinationExtraProperties = mapExtraPropertiesAttribute == null
? new ExtraPropertyDictionary()
: GetDestinationExtraPropertiesSeed((TSource)source, destination);
TryMapExtraProperties(mapExtraPropertiesAttribute, (TSource)source, destination, destinationExtraProperties);
mapper.AfterMap((TSource)source, destination);
return destination;
}
@ -54,9 +59,13 @@ public class MapperlyAutoObjectMappingProvider : IAutoObjectMappingProvider
var reverseMapper = ServiceProvider.GetService<IAbpReverseMapperlyMapper<TDestination, TSource>>();
if (reverseMapper != null)
{
var mapExtraPropertiesAttribute = reverseMapper.GetType().GetSingleAttributeOrNull<MapExtraPropertiesAttribute>();
reverseMapper.BeforeReverseMap((TSource)source);
var destination = reverseMapper.ReverseMap((TSource)source);
TryMapExtraProperties(reverseMapper.GetType().GetSingleAttributeOrNull<MapExtraPropertiesAttribute>(), (TSource)source, destination, new ExtraPropertyDictionary());
var destinationExtraProperties = mapExtraPropertiesAttribute == null
? new ExtraPropertyDictionary()
: GetDestinationExtraPropertiesSeed((TSource)source, destination);
TryMapExtraProperties(mapExtraPropertiesAttribute, (TSource)source, destination, destinationExtraProperties);
reverseMapper.AfterReverseMap((TSource)source, destination);
return destination;
}
@ -219,6 +228,38 @@ public class MapperlyAutoObjectMappingProvider : IAutoObjectMappingProvider
return Expression.Lambda<Func<object, object, object, object?>>(callConvert, instanceParam, sourceParam, destinationParam).Compile();
}
protected virtual ExtraPropertyDictionary GetDestinationExtraPropertiesSeed<TSource, TDestination>(TSource source, TDestination destination)
{
var extraProperties = new ExtraPropertyDictionary();
if (source is not IHasExtraProperties sourceHasExtraProperties ||
destination is not IHasExtraProperties destinationHasExtraProperties ||
destinationHasExtraProperties.ExtraProperties is null)
{
return extraProperties;
}
//Keys that don't exist in the source can only be set by the destination's constructor
foreach (var property in destinationHasExtraProperties.ExtraProperties)
{
if (sourceHasExtraProperties.ExtraProperties == null || !sourceHasExtraProperties.ExtraProperties.ContainsKey(property.Key))
{
extraProperties[property.Key] = property.Value;
}
}
//Source keys may be copied by the generated mapper, reset registered ones to their default value and let the filter map the source value
var destinationType = ProxyHelper.UnProxy(destinationHasExtraProperties).GetType();
foreach (var property in ObjectExtensionManager.Instance.GetProperties(destinationType))
{
if (!extraProperties.ContainsKey(property.Name) && destinationHasExtraProperties.ExtraProperties.ContainsKey(property.Name))
{
extraProperties[property.Name] = property.GetDefaultValue();
}
}
return extraProperties;
}
protected virtual ExtraPropertyDictionary GetExtraProperties<TDestination>(TDestination destination)
{
var extraProperties = new ExtraPropertyDictionary();

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

@ -9,6 +9,7 @@
<ItemGroup>
<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="..\AbpTestBase\AbpTestBase.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; }
}
}

6
framework/test/Volo.Abp.Mapperly.Tests/Mapperly/AbpAutoMapperExtensibleDtoExtensions_Tests.cs

@ -53,8 +53,10 @@ public class AbpAutoMapperExtensibleDtoExtensions_Tests : AbpIntegratedTest<Mapp
var personDto = _objectMapper.Map<ExtensibleTestPerson, ExtensibleTestPersonDto>(person);
personDto.GetProperty<string>("Name").ShouldBe("John"); //Defined in both classes
personDto.GetProperty<int>("ChildCount").ShouldBe(0); //Not defined in the source, but was set to the default value by ExtensibleTestPersonDto constructor
personDto.GetProperty("CityName").ShouldBeNull(); //Ignored, but was set to the default value by ExtensibleTestPersonDto constructor
personDto.HasProperty("ChildCount").ShouldBeTrue(); //Not defined in the source, but was set to the default value by ExtensibleTestPersonDto constructor
personDto.GetProperty<int>("ChildCount").ShouldBe(0);
personDto.HasProperty("CityName").ShouldBeTrue(); //Ignored, but was set to the default value by ExtensibleTestPersonDto constructor
personDto.GetProperty("CityName").ShouldBeNull();
personDto.HasProperty("Age").ShouldBeFalse(); //Not defined on the destination
personDto.HasProperty("Sex").ShouldBeFalse(); //Not defined in both classes
}

3
framework/test/Volo.Abp.Mapperly.Tests/Volo/Abp/Mapperly/AbpReverseMapperly_Tests.cs

@ -97,6 +97,7 @@ public class AbpReverseMapperly_Tests : AbpIntegratedTest<MapperlyTestModule>
var entity = _objectMapper.Map<ExtensibleReverseDto, ExtensibleReverseEntity>(dto);
entity.GetProperty<string>("Tag").ShouldBe("ok");
entity.HasProperty("Secret").ShouldBeFalse();
entity.HasProperty("Secret").ShouldBeTrue(); //Ignored, but was set to the default value by the ExtensibleObject constructor
entity.GetProperty<string>("Secret").ShouldBeNull();
}
}

87
framework/test/Volo.Abp.Mapperly.Tests/Volo/Abp/Mapperly/MapExtraPropertiesDefaultSeed_Tests.cs

@ -0,0 +1,87 @@
using System;
using Microsoft.Extensions.DependencyInjection;
using Shouldly;
using Volo.Abp.Data;
using Volo.Abp.ObjectMapping;
using Volo.Abp.Testing;
using Xunit;
namespace Volo.Abp.Mapperly;
public class MapExtraPropertiesDefaultSeed_Tests : AbpIntegratedTest<MapperlyTestModule>
{
private readonly IObjectMapper _objectMapper;
public MapExtraPropertiesDefaultSeed_Tests()
{
_objectMapper = ServiceProvider.GetRequiredService<IObjectMapper>();
}
[Fact]
public void Single_Parameter_Map_Should_Preserve_Constructor_Seeded_Properties()
{
var entity = new ExtensibleSeededEntity { Id = Guid.NewGuid() }
.SetProperty("Tag", "ok");
var dto = _objectMapper.Map<ExtensibleSeededEntity, ExtensibleSeededDto>(entity);
dto.GetProperty<string>("Tag").ShouldBe("ok"); //Defined in both classes
dto.GetProperty<string>("CreatedBy").ShouldBe("system"); //Set by the ExtensibleSeededDto constructor
dto.HasProperty("DtoOnly").ShouldBeTrue(); //Not defined in the source, but was set to the default value by the ExtensibleObject constructor
dto.GetProperty("DtoOnly").ShouldBeNull();
}
[Fact]
public void Single_Parameter_Map_Should_Not_Change_The_Source_Extra_Properties()
{
var entity = new ExtensibleSeededEntity { Id = Guid.NewGuid() }
.SetProperty("Tag", "ok")
.SetProperty("CreatedBy", "leaked");
var originalReference = entity.ExtraProperties;
_objectMapper.Map<ExtensibleSeededEntity, ExtensibleSeededDto>(entity);
ReferenceEquals(entity.ExtraProperties, originalReference).ShouldBeTrue();
entity.GetProperty<string>("Tag").ShouldBe("ok");
entity.GetProperty<string>("CreatedBy").ShouldBe("leaked");
}
[Fact]
public void Single_Parameter_Map_Should_Not_Seed_Defaults_When_Destination_Disables_Them()
{
var entity = new ExtensibleSeededEntity { Id = Guid.NewGuid() }
.SetProperty("Tag", "ok");
var dto = _objectMapper.Map<ExtensibleSeededEntity, ExtensibleNonSeededDto>(entity);
dto.GetProperty<string>("Tag").ShouldBe("ok"); //Defined in both classes
dto.HasProperty("DtoOnly").ShouldBeFalse(); //ExtensibleNonSeededDto constructor disables the default values seeding
}
[Fact]
public void Single_Parameter_Map_Should_Not_Leak_Filtered_Source_Values_Into_Registered_Keys()
{
var entity = new ExtensibleSeededEntity { Id = Guid.NewGuid() }
.SetProperty("Tag", "ok")
.SetProperty("DtoOnly", "leaked");
var dto = _objectMapper.Map<ExtensibleSeededEntity, ExtensibleNonSeededDto>(entity);
dto.GetProperty<string>("Tag").ShouldBe("ok"); //Defined in both classes
dto.GetProperty("DtoOnly").ShouldBeNull(); //Not defined in the source, the source value must not leak
}
[Fact]
public void Single_Parameter_Map_Should_Not_Invoke_Default_Value_Factories_Without_MapExtraProperties_Attribute()
{
var entity = new ExtensibleSeededEntity { Id = Guid.NewGuid() }
.SetProperty("Tag", "ok");
var callsBefore = ExtensibleNoAttributeDto.CountedDefaultValueFactoryCalls;
var dto = _objectMapper.Map<ExtensibleSeededEntity, ExtensibleNoAttributeDto>(entity);
(ExtensibleNoAttributeDto.CountedDefaultValueFactoryCalls - callsBefore).ShouldBe(1); //Only the ExtensibleNoAttributeDto constructor seeding
dto.HasProperty("Counted").ShouldBeTrue();
}
}

15
framework/test/Volo.Abp.Mapperly.Tests/Volo/Abp/Mapperly/MapperlyTestModule.cs

@ -21,7 +21,20 @@ public class MapperlyTestModule : AbpModule
.AddOrUpdateProperty<ExtensibleReverseEntity, string>("Tag")
.AddOrUpdateProperty<ExtensibleReverseEntity, string>("Secret")
.AddOrUpdateProperty<ExtensibleReverseDto, string>("Tag")
.AddOrUpdateProperty<ExtensibleReverseDto, string>("Secret");
.AddOrUpdateProperty<ExtensibleReverseDto, string>("Secret")
.AddOrUpdateProperty<ExtensibleSeededEntity, string>("Tag")
.AddOrUpdateProperty<ExtensibleSeededDto, string>("Tag")
.AddOrUpdateProperty<ExtensibleSeededDto, string>("DtoOnly")
.AddOrUpdateProperty<ExtensibleNonSeededDto, string>("Tag")
.AddOrUpdateProperty<ExtensibleNonSeededDto, string>("DtoOnly")
.AddOrUpdateProperty<ExtensibleNoAttributeDto, string>("Counted", options =>
{
options.DefaultValueFactory = () =>
{
ExtensibleNoAttributeDto.CountedDefaultValueFactoryCalls++;
return null!;
};
});
});
}
}

58
framework/test/Volo.Abp.Mapperly.Tests/Volo/Abp/Mapperly/SampleClasses/MapperlyMappers.cs

@ -105,4 +105,62 @@ public partial class ExtensibleReverseMapper : TwoWayMapperBase<ExtensibleRevers
public override partial ExtensibleReverseEntity ReverseMap(ExtensibleReverseDto source);
public override partial void ReverseMap(ExtensibleReverseDto source, ExtensibleReverseEntity destination);
}
public class ExtensibleSeededEntity : ExtensibleObject
{
public Guid Id { get; set; }
}
public class ExtensibleSeededDto : ExtensibleObject
{
public Guid Id { get; set; }
public ExtensibleSeededDto()
{
this.SetProperty("CreatedBy", "system");
}
}
public class ExtensibleNonSeededDto : ExtensibleObject
{
public Guid Id { get; set; }
public ExtensibleNonSeededDto()
: base(false)
{
}
}
public class ExtensibleNoAttributeDto : ExtensibleObject
{
public static int CountedDefaultValueFactoryCalls;
public Guid Id { get; set; }
}
[Mapper]
[MapExtraProperties]
public partial class ExtensibleSeededMapper : MapperBase<ExtensibleSeededEntity, ExtensibleSeededDto>
{
public override partial ExtensibleSeededDto Map(ExtensibleSeededEntity source);
public override partial void Map(ExtensibleSeededEntity source, ExtensibleSeededDto destination);
}
[Mapper]
[MapExtraProperties]
public partial class ExtensibleNonSeededMapper : MapperBase<ExtensibleSeededEntity, ExtensibleNonSeededDto>
{
public override partial ExtensibleNonSeededDto Map(ExtensibleSeededEntity source);
public override partial void Map(ExtensibleSeededEntity source, ExtensibleNonSeededDto destination);
}
[Mapper]
public partial class ExtensibleNoAttributeMapper : MapperBase<ExtensibleSeededEntity, ExtensibleNoAttributeDto>
{
public override partial ExtensibleNoAttributeDto Map(ExtensibleSeededEntity source);
public override partial void Map(ExtensibleSeededEntity source, ExtensibleNoAttributeDto destination);
}

25
modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/IdentityDbContextModelBuilderExtensions.cs

@ -1,6 +1,8 @@
using System;
using System.Text.Json;
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.ChangeTracking;
using Volo.Abp.EntityFrameworkCore.Modeling;
using Volo.Abp.Users.EntityFrameworkCore;
@ -187,7 +189,28 @@ public static class IdentityDbContextModelBuilderExtensions
b.HasKey(p => p.CredentialId);
b.Property(p => p.CredentialId).HasMaxLength(IdentityUserPasskeyConsts.MaxCredentialIdLength); // Defined in WebAuthn spec to be no longer than 1023 bytes
b.OwnsOne(p => p.Data).ToJson();
if (builder.IsUsingMySQL())
{
/* MySQL providers do not support EF Core JSON columns (ToJson),
* so store Data as a serialized json column with the same column
* name and content. The comparer detects in-place mutations
* (e.g. sign count updates on login). */
b.Property(p => p.Data)
.HasColumnName(nameof(IdentityUserPasskey.Data))
.HasColumnType("json")
.HasConversion(
d => JsonSerializer.Serialize(d, (JsonSerializerOptions?)null),
s => JsonSerializer.Deserialize<IdentityPasskeyData>(s, (JsonSerializerOptions?)null)!,
new ValueComparer<IdentityPasskeyData>(
(l, r) => JsonSerializer.Serialize(l, (JsonSerializerOptions?)null) == JsonSerializer.Serialize(r, (JsonSerializerOptions?)null),
v => JsonSerializer.Serialize(v, (JsonSerializerOptions?)null).GetHashCode(),
v => JsonSerializer.Deserialize<IdentityPasskeyData>(JsonSerializer.Serialize(v, (JsonSerializerOptions?)null), (JsonSerializerOptions?)null)!));
}
else
{
b.OwnsOne(p => p.Data).ToJson();
}
b.ApplyObjectExtensionMappings();
});

Loading…
Cancel
Save