From 3f7979f77c5dd6b43077b2e1f05c524ea359c04b Mon Sep 17 00:00:00 2001 From: zhenlei520 Date: Fri, 29 Apr 2022 17:02:44 +0800 Subject: [PATCH 1/8] feat(Mapping): Support Mapping --- .editorconfig | 5 +- Masa.Contrib.sln | 36 ++++ src/BuildingBlocks/MASA.BuildingBlocks | 2 +- .../DefaultMapping.cs | 34 ++++ .../DefaultMappingConfigProvider.cs | 192 ++++++++++++++++++ .../IMappingConfigProvider.cs | 9 + .../Internal/InvokeBuilder.cs | 40 ++++ .../Internal/Options/MapTypeOptions.cs | 19 ++ .../Internal/TypeAdapterSetterExpand.cs | 15 ++ .../Masa.Contrib.Data.Mapping.Mapster.csproj | 18 ++ .../README.md | 77 +++++++ .../README.zh-CN.md | 77 +++++++ .../ServiceCollectionExtensions.cs | 32 +++ .../_Imports.cs | 12 ++ .../BaseMappingTest.cs | 20 ++ .../Domain/Aggregates/Orders/Order.cs | 36 ++++ .../Domain/Aggregates/Orders/OrderItem.cs | 25 +++ .../Domain/Aggregates/Users/User.cs | 41 ++++ .../Domain/ValueObjects/AddressItem.cs | 25 +++ .../MappingFormTest.cs | 27 +++ .../MappingTest.cs | 191 +++++++++++++++++ ....Contrib.Data.Mapping.Mapster.Tests.csproj | 22 ++ .../Requests/AddressItemRequest.cs | 13 ++ .../Requests/Orders/OrderItemRequest.cs | 13 ++ .../Requests/Orders/OrderMultiRequest.cs | 13 ++ .../Requests/Orders/OrderRequest.cs | 11 + .../Requests/Users/CreateFullUserRequest.cs | 15 ++ .../Requests/Users/CreateUserRequest.cs | 9 + .../_Imports.cs | 16 ++ 29 files changed, 1043 insertions(+), 2 deletions(-) create mode 100644 src/Data/Mapping/Masa.Contrib.Data.Mapping.Mapster/DefaultMapping.cs create mode 100644 src/Data/Mapping/Masa.Contrib.Data.Mapping.Mapster/DefaultMappingConfigProvider.cs create mode 100644 src/Data/Mapping/Masa.Contrib.Data.Mapping.Mapster/IMappingConfigProvider.cs create mode 100644 src/Data/Mapping/Masa.Contrib.Data.Mapping.Mapster/Internal/InvokeBuilder.cs create mode 100644 src/Data/Mapping/Masa.Contrib.Data.Mapping.Mapster/Internal/Options/MapTypeOptions.cs create mode 100644 src/Data/Mapping/Masa.Contrib.Data.Mapping.Mapster/Internal/TypeAdapterSetterExpand.cs create mode 100644 src/Data/Mapping/Masa.Contrib.Data.Mapping.Mapster/Masa.Contrib.Data.Mapping.Mapster.csproj create mode 100644 src/Data/Mapping/Masa.Contrib.Data.Mapping.Mapster/README.md create mode 100644 src/Data/Mapping/Masa.Contrib.Data.Mapping.Mapster/README.zh-CN.md create mode 100644 src/Data/Mapping/Masa.Contrib.Data.Mapping.Mapster/ServiceCollectionExtensions.cs create mode 100644 src/Data/Mapping/Masa.Contrib.Data.Mapping.Mapster/_Imports.cs create mode 100644 test/Masa.Contrib.Data.Mapping.Mapster.Tests/BaseMappingTest.cs create mode 100644 test/Masa.Contrib.Data.Mapping.Mapster.Tests/Domain/Aggregates/Orders/Order.cs create mode 100644 test/Masa.Contrib.Data.Mapping.Mapster.Tests/Domain/Aggregates/Orders/OrderItem.cs create mode 100644 test/Masa.Contrib.Data.Mapping.Mapster.Tests/Domain/Aggregates/Users/User.cs create mode 100644 test/Masa.Contrib.Data.Mapping.Mapster.Tests/Domain/ValueObjects/AddressItem.cs create mode 100644 test/Masa.Contrib.Data.Mapping.Mapster.Tests/MappingFormTest.cs create mode 100644 test/Masa.Contrib.Data.Mapping.Mapster.Tests/MappingTest.cs create mode 100644 test/Masa.Contrib.Data.Mapping.Mapster.Tests/Masa.Contrib.Data.Mapping.Mapster.Tests.csproj create mode 100644 test/Masa.Contrib.Data.Mapping.Mapster.Tests/Requests/AddressItemRequest.cs create mode 100644 test/Masa.Contrib.Data.Mapping.Mapster.Tests/Requests/Orders/OrderItemRequest.cs create mode 100644 test/Masa.Contrib.Data.Mapping.Mapster.Tests/Requests/Orders/OrderMultiRequest.cs create mode 100644 test/Masa.Contrib.Data.Mapping.Mapster.Tests/Requests/Orders/OrderRequest.cs create mode 100644 test/Masa.Contrib.Data.Mapping.Mapster.Tests/Requests/Users/CreateFullUserRequest.cs create mode 100644 test/Masa.Contrib.Data.Mapping.Mapster.Tests/Requests/Users/CreateUserRequest.cs create mode 100644 test/Masa.Contrib.Data.Mapping.Mapster.Tests/_Imports.cs diff --git a/.editorconfig b/.editorconfig index 2250cdba0..4e1549bd7 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,4 +1,5 @@ [*.cs] +file_header_template = Copyright (c) MASA Stack All rights reserved.\nLicensed under the MIT License. See LICENSE.txt in the project root for license information. # CS8618: 在退出构造函数时,不可为 null 的字段必须包含非 null 值。请考虑声明为可以为 null。 dotnet_diagnostic.CS8618.severity = none @@ -29,4 +30,6 @@ indent_size = 2 # JSON files [*.json] -indent_size = 2 \ No newline at end of file +indent_size = 2 + +file_header_template = 添加文件头(add file header)示例程序\n Copyright (c) https://ittranslator.cn/ \ No newline at end of file diff --git a/Masa.Contrib.sln b/Masa.Contrib.sln index 8c8c7781c..401db2919 100644 --- a/Masa.Contrib.sln +++ b/Masa.Contrib.sln @@ -172,6 +172,14 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Storage", "Storage", "{1653 EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Masa.Contrib.Configuration.AutoMap.NoArgumentConstructor.Tests", "test\Masa.Contrib.Configuration.AutoMap.NoArgumentConstructor.Tests\Masa.Contrib.Configuration.AutoMap.NoArgumentConstructor.Tests.csproj", "{B8358ED1-C95A-4EC0-9756-FB32C931F204}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Masa.BuildingBlocks.Data.Mapping", "src\BuildingBlocks\MASA.BuildingBlocks\src\Data\Masa.BuildingBlocks.Data.Mapping\Masa.BuildingBlocks.Data.Mapping.csproj", "{5A3338F1-9963-4CAC-85A3-7AB263CB15B0}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Masa.Contrib.Data.Mapping.Mapster.Tests", "test\Masa.Contrib.Data.Mapping.Mapster.Tests\Masa.Contrib.Data.Mapping.Mapster.Tests.csproj", "{834A12D0-FBED-45B3-86EA-5EA114C516B5}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Mapping", "Mapping", "{4AC23B67-52F9-44E5-9586-79A1DB73E6F7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Masa.Contrib.Data.Mapping.Mapster", "src\Data\Mapping\Masa.Contrib.Data.Mapping.Mapster\Masa.Contrib.Data.Mapping.Mapster.csproj", "{D5EA7A25-0FD2-4545-9C1C-FF96E5E35145}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -676,6 +684,30 @@ Global {B8358ED1-C95A-4EC0-9756-FB32C931F204}.Release|Any CPU.Build.0 = Release|Any CPU {B8358ED1-C95A-4EC0-9756-FB32C931F204}.Release|x64.ActiveCfg = Release|Any CPU {B8358ED1-C95A-4EC0-9756-FB32C931F204}.Release|x64.Build.0 = Release|Any CPU + {5A3338F1-9963-4CAC-85A3-7AB263CB15B0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5A3338F1-9963-4CAC-85A3-7AB263CB15B0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5A3338F1-9963-4CAC-85A3-7AB263CB15B0}.Debug|x64.ActiveCfg = Debug|Any CPU + {5A3338F1-9963-4CAC-85A3-7AB263CB15B0}.Debug|x64.Build.0 = Debug|Any CPU + {5A3338F1-9963-4CAC-85A3-7AB263CB15B0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5A3338F1-9963-4CAC-85A3-7AB263CB15B0}.Release|Any CPU.Build.0 = Release|Any CPU + {5A3338F1-9963-4CAC-85A3-7AB263CB15B0}.Release|x64.ActiveCfg = Release|Any CPU + {5A3338F1-9963-4CAC-85A3-7AB263CB15B0}.Release|x64.Build.0 = Release|Any CPU + {834A12D0-FBED-45B3-86EA-5EA114C516B5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {834A12D0-FBED-45B3-86EA-5EA114C516B5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {834A12D0-FBED-45B3-86EA-5EA114C516B5}.Debug|x64.ActiveCfg = Debug|Any CPU + {834A12D0-FBED-45B3-86EA-5EA114C516B5}.Debug|x64.Build.0 = Debug|Any CPU + {834A12D0-FBED-45B3-86EA-5EA114C516B5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {834A12D0-FBED-45B3-86EA-5EA114C516B5}.Release|Any CPU.Build.0 = Release|Any CPU + {834A12D0-FBED-45B3-86EA-5EA114C516B5}.Release|x64.ActiveCfg = Release|Any CPU + {834A12D0-FBED-45B3-86EA-5EA114C516B5}.Release|x64.Build.0 = Release|Any CPU + {D5EA7A25-0FD2-4545-9C1C-FF96E5E35145}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D5EA7A25-0FD2-4545-9C1C-FF96E5E35145}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D5EA7A25-0FD2-4545-9C1C-FF96E5E35145}.Debug|x64.ActiveCfg = Debug|Any CPU + {D5EA7A25-0FD2-4545-9C1C-FF96E5E35145}.Debug|x64.Build.0 = Debug|Any CPU + {D5EA7A25-0FD2-4545-9C1C-FF96E5E35145}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D5EA7A25-0FD2-4545-9C1C-FF96E5E35145}.Release|Any CPU.Build.0 = Release|Any CPU + {D5EA7A25-0FD2-4545-9C1C-FF96E5E35145}.Release|x64.ActiveCfg = Release|Any CPU + {D5EA7A25-0FD2-4545-9C1C-FF96E5E35145}.Release|x64.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -761,6 +793,10 @@ Global {97532A33-A591-4DF5-A2C0-72527B78ED82} = {38E6C400-90C0-493E-9266-C1602E229F1B} {165391A5-034E-4894-8084-8DF7D4AA7518} = {42DF7AAC-362C-48F4-B76A-BDEEEFF17CC9} {B8358ED1-C95A-4EC0-9756-FB32C931F204} = {9EEE31DA-3165-4CB3-AAE9-27CC3A4DE669} + {5A3338F1-9963-4CAC-85A3-7AB263CB15B0} = {DC578D74-98F0-4F19-A230-CFA8DAEE0AF1} + {834A12D0-FBED-45B3-86EA-5EA114C516B5} = {38E6C400-90C0-493E-9266-C1602E229F1B} + {4AC23B67-52F9-44E5-9586-79A1DB73E6F7} = {E33ADF54-4D35-49B7-BDA6-412587CA39FF} + {D5EA7A25-0FD2-4545-9C1C-FF96E5E35145} = {4AC23B67-52F9-44E5-9586-79A1DB73E6F7} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {40383055-CC50-4600-AD9A-53C14F620D03} diff --git a/src/BuildingBlocks/MASA.BuildingBlocks b/src/BuildingBlocks/MASA.BuildingBlocks index d0443a3b2..db985617a 160000 --- a/src/BuildingBlocks/MASA.BuildingBlocks +++ b/src/BuildingBlocks/MASA.BuildingBlocks @@ -1 +1 @@ -Subproject commit d0443a3b2c1a82d1b2b4b4b3e6d163fb472f4960 +Subproject commit db985617a9ae4ce729d39ee36c75130703ac14dc diff --git a/src/Data/Mapping/Masa.Contrib.Data.Mapping.Mapster/DefaultMapping.cs b/src/Data/Mapping/Masa.Contrib.Data.Mapping.Mapster/DefaultMapping.cs new file mode 100644 index 000000000..b4dec16a7 --- /dev/null +++ b/src/Data/Mapping/Masa.Contrib.Data.Mapping.Mapster/DefaultMapping.cs @@ -0,0 +1,34 @@ +// Copyright (c) MASA Stack All rights reserved. +// Licensed under the MIT License. See LICENSE.txt in the project root for license information. + +namespace Masa.Contrib.Data.Mapping.Mapster; + +public class DefaultMapping : IMapping +{ + private readonly IMappingConfigProvider _provider; + + public DefaultMapping(IMappingConfigProvider provider) + => _provider = provider; + + public TDestination Map(TSource source, MapOptions? options = null) + { + ArgumentNullException.ThrowIfNull(source, nameof(source)); + + return source.Adapt(_provider.GetConfig(source.GetType(), typeof(TDestination), options)); + } + + public TDestination Map(object source, MapOptions? options = null) + { + ArgumentNullException.ThrowIfNull(source, nameof(source)); + + return source.Adapt(_provider.GetConfig(source.GetType(), typeof(TDestination), options)); + } + + public TDestination Map(TSource source, TDestination destination, MapOptions? options = null) + { + ArgumentNullException.ThrowIfNull(source, nameof(source)); + + Type destinationType = destination?.GetType() ?? typeof(TDestination); + return source.Adapt(destination, _provider.GetConfig(source.GetType(), destinationType, options)); + } +} diff --git a/src/Data/Mapping/Masa.Contrib.Data.Mapping.Mapster/DefaultMappingConfigProvider.cs b/src/Data/Mapping/Masa.Contrib.Data.Mapping.Mapster/DefaultMappingConfigProvider.cs new file mode 100644 index 000000000..7def4e769 --- /dev/null +++ b/src/Data/Mapping/Masa.Contrib.Data.Mapping.Mapster/DefaultMappingConfigProvider.cs @@ -0,0 +1,192 @@ +// Copyright (c) MASA Stack All rights reserved. +// Licensed under the MIT License. See LICENSE.txt in the project root for license information. + +namespace Masa.Contrib.Data.Mapping.Mapster; + +public class DefaultMappingConfigProvider : IMappingConfigProvider +{ + private readonly ConcurrentDictionary<(Type SourceType, Type DestinationType, MapOptions? mapOptions), TypeAdapterConfig?> + _store = new(); + + private readonly MapOptions _options; + + public DefaultMappingConfigProvider(MapOptions options) => _options = options; + + public TypeAdapterConfig GetConfig(Type sourceType, Type destinationType, MapOptions? options = null) + => GetConfigByCache(sourceType, destinationType, options); + + protected virtual TypeAdapterConfig GetConfigByCache(Type sourceType, Type destinationType, MapOptions? options) + { + TypeAdapterConfig? config = _store.GetOrAdd( + (sourceType, destinationType, options), + type => GetAdapterConfig(type.SourceType, type.DestinationType, options)); + + if (config == null) + { + if (IsShare(options)) + return TypeAdapterConfig.GlobalSettings; + + throw new ArgumentNullException(nameof(config), "Failed to get mapping configuration"); + } + + return config; + } + + protected virtual TypeAdapterConfig? GetAdapterConfig(Type sourceType, Type destinationType, MapOptions? options) + { + TypeAdapterConfig adapterConfig = GetDefaultConfig(options); + + var mapTypes = GetMapAndSelectorTypes(adapterConfig, sourceType, destinationType, options, true); + + foreach (var item in mapTypes) + { + var methodExecutor = InvokeBuilder.Build(item.SourceType, item.DestinationType); + methodExecutor.Invoke(adapterConfig, item.Constructor); + } + + return IsShare(options) ? null : adapterConfig; //When in shared mode, Config returns empty to save memory space + } + + //todo: In the follow-up, according to the situation, consider whether the configuration requires Fork, which is not processed for the time being + private List GetMapTypes( + TypeAdapterConfig adapterConfig, + Type sourceType, + Type destinationType, + MapOptions? options) + { + if (!NeedAutomaticMap(sourceType, destinationType)) + return new List(); + + List mapTypes = new(); + var sourceProperties = sourceType.GetProperties().ToList(); + var destinationProperties = destinationType.GetProperties().ToList(); + + List destinationConstructors = destinationType + .GetConstructors(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) + .Where(c => c.GetParameters().Length <= sourceProperties.Count) + .OrderByDescending(c => c.GetParameters().Length) + .ToList(); + + MapTypeOptions mapTypeOption = new(sourceType, destinationType) + { + Constructor = GetBestConstructor(destinationConstructors, sourceProperties) + }; + if (!RuleMapIsExist(adapterConfig, sourceType, destinationType)) + { + mapTypes.Add(mapTypeOption); + } + + List<(string Name, Type DdestinationPropertyType)> destinationPropertyList = destinationProperties + .Select(p => (p.Name.ToLower(), p.PropertyType)) + .Concat(mapTypeOption.Constructor.GetParameters().Select(p => (p.Name!.ToLower(), p.ParameterType))!) + .Distinct() + .ToList(); + + foreach (var sourceProperty in sourceProperties) + { + if (!sourceProperty.CanRead) + continue; + + var destinationProperty = destinationPropertyList.FirstOrDefault(p + => p.Name.Equals(sourceProperty.Name, StringComparison.OrdinalIgnoreCase)); + if (destinationProperty != default) + { + var subMapTypes = GetMapAndSelectorTypes(adapterConfig, sourceProperty.PropertyType, + destinationProperty.DdestinationPropertyType, options, false); + + if (!subMapTypes.Any() || mapTypes.Any(option => subMapTypes.Any(subOption + => subOption.SourceType == option.SourceType && subOption.DestinationType == option.DestinationType))) + continue; + + mapTypes.AddRange(subMapTypes); + } + } + + return mapTypes; + } + + private List GetMapAndSelectorTypes(TypeAdapterConfig adapterConfig, Type sourceType, Type destinationType, + MapOptions? options, bool isFirst) + { + bool sourcePropertyIsEnumerable = IsCollection(sourceType); + bool destinationPropertyIsEnumerable = IsCollection(destinationType); + if (!sourcePropertyIsEnumerable && !destinationPropertyIsEnumerable) + { + var subMapTypes = GetMapTypes( + adapterConfig, + sourceType, + destinationType, + options); + if (subMapTypes.Any()) return subMapTypes; + } + else if (sourcePropertyIsEnumerable && destinationPropertyIsEnumerable) + { + var subMapTypes = GetMapTypes(adapterConfig, + sourceType.GetGenericArguments()[0], + destinationType.GetGenericArguments()[0], + options); + + if (subMapTypes.Any()) return subMapTypes; + } + return new(); + } + + protected virtual bool IsCollection(Type type) + => type.IsGenericType && type.GetInterfaces().Any(x => x.GetGenericTypeDefinition() == typeof(IEnumerable<>)); + + protected virtual ConstructorInfo GetBestConstructor(List destinationConstructors, List sourceProperties) + { + if (destinationConstructors.Count <= 1) + return destinationConstructors.FirstOrDefault()!; + + foreach (var constructor in destinationConstructors) + { + if (IsPreciseMatch(constructor, sourceProperties)) + return constructor; + } + + throw new Exception("Failed to get the best constructor"); + } + + protected virtual bool IsPreciseMatch(ConstructorInfo destinationConstructor, List sourceProperties) + { + foreach (var parameter in destinationConstructor.GetParameters()) + { + if (!sourceProperties.Any(p + => p.Name.Equals(parameter.Name, StringComparison.OrdinalIgnoreCase) && p.PropertyType == parameter.ParameterType)) + { + return false; + } + } + return true; + } + + protected virtual List NotNeedAutomaticMapTypes => new() + { + typeof(string) + }; + + protected virtual bool NeedAutomaticMap(Type sourceType, Type destinationType) + => sourceType.IsClass && + !IsCollection(sourceType) && + (sourceType != destinationType || (sourceType != typeof(object) || destinationType != typeof(object))) && + !NotNeedAutomaticMapTypes.Contains(sourceType); + + protected virtual bool RuleMapIsExist(TypeAdapterConfig adapterConfig, Type sourceType, Type destinationType) + => adapterConfig.RuleMap.Any(r => r.Key == new TypeTuple(sourceType, destinationType)); + + protected virtual bool IsShare(MapOptions? options) => (options?.Mode ?? _options.Mode) == MapMode.Shared; + + /// + /// Get initial configuration + /// When currently in shared mode, return the default global settings + /// + /// + protected virtual TypeAdapterConfig GetDefaultConfig(MapOptions? options) + { + //todo: Other modes are currently not supported, and will be added in the future according to the situation + return IsShare(options) ? + TypeAdapterConfig.GlobalSettings : + throw new ArgumentException("Only shared configuration is supported", nameof(MapOptions.Mode)); + } +} diff --git a/src/Data/Mapping/Masa.Contrib.Data.Mapping.Mapster/IMappingConfigProvider.cs b/src/Data/Mapping/Masa.Contrib.Data.Mapping.Mapster/IMappingConfigProvider.cs new file mode 100644 index 000000000..60e9aa742 --- /dev/null +++ b/src/Data/Mapping/Masa.Contrib.Data.Mapping.Mapster/IMappingConfigProvider.cs @@ -0,0 +1,9 @@ +// Copyright (c) MASA Stack All rights reserved. +// Licensed under the MIT License. See LICENSE.txt in the project root for license information. + +namespace Masa.Contrib.Data.Mapping.Mapster; + +public interface IMappingConfigProvider +{ + TypeAdapterConfig GetConfig(Type sourceType, Type destinationType, MapOptions? options = null); +} diff --git a/src/Data/Mapping/Masa.Contrib.Data.Mapping.Mapster/Internal/InvokeBuilder.cs b/src/Data/Mapping/Masa.Contrib.Data.Mapping.Mapster/Internal/InvokeBuilder.cs new file mode 100644 index 000000000..6b91d39b6 --- /dev/null +++ b/src/Data/Mapping/Masa.Contrib.Data.Mapping.Mapster/Internal/InvokeBuilder.cs @@ -0,0 +1,40 @@ +// Copyright (c) MASA Stack All rights reserved. +// Licensed under the MIT License. See LICENSE.txt in the project root for license information. + +namespace Masa.Contrib.Data.Mapping.Mapster.Internal; + +internal class InvokeBuilder +{ + private static readonly MethodInfo NewConfigMethodInfo; + private static readonly Type TypeAdapterConfigType; + + static InvokeBuilder() + { + var typeAdapterSetterExpandType = typeof(TypeAdapterSetterExpand); + NewConfigMethodInfo = typeAdapterSetterExpandType.GetMethod(nameof(TypeAdapterSetterExpand.NewConfigByConstructor))!; + TypeAdapterConfigType = typeof(TypeAdapterConfig); + } + + internal delegate TypeAdapterSetter MethodExecutor(TypeAdapterConfig target, object parameter); + + public static MethodExecutor Build( + Type sourceType, + Type destinationType) + { + var methodInfo = NewConfigMethodInfo.MakeGenericMethod(sourceType, destinationType); + + ParameterExpression[] parameters = + { + Expression.Parameter(TypeAdapterConfigType, "adapterConfigParameter"), + Expression.Parameter(typeof(object), "constructorInfoParameter") + }; + var newConfigMethodCall = Expression.Call( + null, + methodInfo, + parameters + ); + + var lambda = Expression.Lambda(newConfigMethodCall, parameters); + return lambda.Compile(); + } +} diff --git a/src/Data/Mapping/Masa.Contrib.Data.Mapping.Mapster/Internal/Options/MapTypeOptions.cs b/src/Data/Mapping/Masa.Contrib.Data.Mapping.Mapster/Internal/Options/MapTypeOptions.cs new file mode 100644 index 000000000..7f93638c2 --- /dev/null +++ b/src/Data/Mapping/Masa.Contrib.Data.Mapping.Mapster/Internal/Options/MapTypeOptions.cs @@ -0,0 +1,19 @@ +// Copyright (c) MASA Stack All rights reserved. +// Licensed under the MIT License. See LICENSE.txt in the project root for license information. + +namespace Masa.Contrib.Data.Mapping.Mapster.Internal.Options; + +internal class MapTypeOptions +{ + public Type SourceType { get; } = default!; + + public Type DestinationType { get; } = default!; + + public ConstructorInfo Constructor { get; set; } = default!; + + public MapTypeOptions(Type sourceType, Type destinationType) + { + SourceType = sourceType; + DestinationType = destinationType; + } +} diff --git a/src/Data/Mapping/Masa.Contrib.Data.Mapping.Mapster/Internal/TypeAdapterSetterExpand.cs b/src/Data/Mapping/Masa.Contrib.Data.Mapping.Mapster/Internal/TypeAdapterSetterExpand.cs new file mode 100644 index 000000000..f119e2d95 --- /dev/null +++ b/src/Data/Mapping/Masa.Contrib.Data.Mapping.Mapster/Internal/TypeAdapterSetterExpand.cs @@ -0,0 +1,15 @@ +// Copyright (c) MASA Stack All rights reserved. +// Licensed under the MIT License. See LICENSE.txt in the project root for license information. + +namespace Masa.Contrib.Data.Mapping.Mapster.Internal; + +internal class TypeAdapterSetterExpand +{ + public static TypeAdapterSetter NewConfigByConstructor(TypeAdapterConfig adapterConfig, + object constructorInfo) + { + return adapterConfig + .NewConfig() + .MapToConstructor((constructorInfo as ConstructorInfo)!); + } +} diff --git a/src/Data/Mapping/Masa.Contrib.Data.Mapping.Mapster/Masa.Contrib.Data.Mapping.Mapster.csproj b/src/Data/Mapping/Masa.Contrib.Data.Mapping.Mapster/Masa.Contrib.Data.Mapping.Mapster.csproj new file mode 100644 index 000000000..5fe32eab6 --- /dev/null +++ b/src/Data/Mapping/Masa.Contrib.Data.Mapping.Mapster/Masa.Contrib.Data.Mapping.Mapster.csproj @@ -0,0 +1,18 @@ + + + + net6.0 + enable + enable + + + + + + + + + + + + diff --git a/src/Data/Mapping/Masa.Contrib.Data.Mapping.Mapster/README.md b/src/Data/Mapping/Masa.Contrib.Data.Mapping.Mapster/README.md new file mode 100644 index 000000000..662ac35b2 --- /dev/null +++ b/src/Data/Mapping/Masa.Contrib.Data.Mapping.Mapster/README.md @@ -0,0 +1,77 @@ +[中](README.zh-CN.md) | EN + +## Masa.Contrib.Data.Mapping.Mapster + +Masa.Contrib.Data.Mapping.Mapster is an object-to-object mapper based on [Mapster](https://github.com/MapsterMapper/Mapster). It adds automatic acquisition and uses the best constructor mapping on the original basis. Nested mapping is supported to reduce the workload of mapping. + +## Example: + +1. Install `Masa.Contrib.Data.Mapping.Mapster` + + ````c# + Install-Package Masa.Contrib.Data.Mapping.Mapster + ```` + +2. Using `Mapping` + + ```` C# + builder.Services.AddMapping(); + ```` + +3. Mapping objects + + ```` + IMapping mapper;// Get through DI + + var request = new + { + Name = "Teach you to learn Dapr...", + OrderItem = new OrderItem("Teach you to learn Dapr hand by hand", 49.9m) + }; + var order = mapper.Map(request);// Map the request to a new object + ```` + + Mapping class `Order`: + + ```` Order.cs + public class Order + { + public string Name { get; set; } + + public decimal TotalPrice { get; set; } + + public List OrderItems { get; set; } + + public Order(string name) + { + Name = name; + } + + public Order(string name, OrderItem orderItem) : this(name) + { + OrderItems = new List { orderItem }; + TotalPrice = OrderItems.Sum(item => item.Price * item.Number); + } + } + + public class OrderItem + { + public string Name { get; set; } + + public decimal Price { get; set; } + + public int Number { get; set; } + + public OrderItem(string name, decimal price) : this(name, price, 1) + { + + } + + public OrderItem(string name, decimal price, int number) + { + Name = name; + Price = price; + Number = number; + } + } + ```` \ No newline at end of file diff --git a/src/Data/Mapping/Masa.Contrib.Data.Mapping.Mapster/README.zh-CN.md b/src/Data/Mapping/Masa.Contrib.Data.Mapping.Mapster/README.zh-CN.md new file mode 100644 index 000000000..dbab048db --- /dev/null +++ b/src/Data/Mapping/Masa.Contrib.Data.Mapping.Mapster/README.zh-CN.md @@ -0,0 +1,77 @@ +中 | [EN](README.md) + +## Masa.Contrib.Data.Mapping.Mapster + +Masa.Contrib.Data.Mapping.Mapster是基于[Mapster](https://github.com/MapsterMapper/Mapster)的一个对象到对象的映射器,在原来的基础上增加自动获取并使用最佳构造函数映射,支持嵌套映射,减轻映射的工作量。 + +## 用例: + +1. 安装`Masa.Contrib.Data.Mapping.Mapster` + + ```c# + Install-Package Masa.Contrib.Data.Mapping.Mapster + ``` + +2. 使用`Mapping` + + ``` C# + builder.Services.AddMapping(); + ``` + +3. 映射对象 + + ``` + IMapping mapper;// 通过DI获取 + + var request = new + { + Name = "Teach you to learn Dapr ……", + OrderItem = new OrderItem("Teach you to learn Dapr hand by hand", 49.9m) + }; + var order = mapper.Map(request);// 将request映射到新的对象 + ``` + + 映射类`Order`: + + ``` Order.cs + public class Order + { + public string Name { get; set; } + + public decimal TotalPrice { get; set; } + + public List OrderItems { get; set; } + + public Order(string name) + { + Name = name; + } + + public Order(string name, OrderItem orderItem) : this(name) + { + OrderItems = new List { orderItem }; + TotalPrice = OrderItems.Sum(item => item.Price * item.Number); + } + } + + public class OrderItem + { + public string Name { get; set; } + + public decimal Price { get; set; } + + public int Number { get; set; } + + public OrderItem(string name, decimal price) : this(name, price, 1) + { + + } + + public OrderItem(string name, decimal price, int number) + { + Name = name; + Price = price; + Number = number; + } + } + ``` \ No newline at end of file diff --git a/src/Data/Mapping/Masa.Contrib.Data.Mapping.Mapster/ServiceCollectionExtensions.cs b/src/Data/Mapping/Masa.Contrib.Data.Mapping.Mapster/ServiceCollectionExtensions.cs new file mode 100644 index 000000000..b28cbb088 --- /dev/null +++ b/src/Data/Mapping/Masa.Contrib.Data.Mapping.Mapster/ServiceCollectionExtensions.cs @@ -0,0 +1,32 @@ +// Copyright (c) MASA Stack All rights reserved. +// Licensed under the MIT License. See LICENSE.txt in the project root for license information. + +namespace Masa.Contrib.Data.Mapping.Mapster; + +public static class ServiceCollectionExtensions +{ + public static IServiceCollection AddMapping(this IServiceCollection services) + => services.AddMapping(MapMode.Shared); + + public static IServiceCollection AddMapping(this IServiceCollection services, MapMode mode) + => services.AddMapping(new MapOptions() + { + Mode = mode + }); + + public static IServiceCollection AddMapping(this IServiceCollection services, MapOptions mapOptions) + { + if (services.Any(service => service.ImplementationType == typeof(MappingProvider))) + return services; + + services.AddSingleton(); + + services.TryAddSingleton(_ => new DefaultMappingConfigProvider(mapOptions)); + services.TryAddSingleton(); + return services; + } + + private class MappingProvider + { + } +} diff --git a/src/Data/Mapping/Masa.Contrib.Data.Mapping.Mapster/_Imports.cs b/src/Data/Mapping/Masa.Contrib.Data.Mapping.Mapster/_Imports.cs new file mode 100644 index 000000000..53048da12 --- /dev/null +++ b/src/Data/Mapping/Masa.Contrib.Data.Mapping.Mapster/_Imports.cs @@ -0,0 +1,12 @@ +global using System.Reflection; +global using System.Linq.Expressions; +global using Mapster; +global using Masa.BuildingBlocks.Data.Mapping; +global using Masa.BuildingBlocks.Data.Mapping.Options; +global using System.Collections.Concurrent; +global using Mapster.Models; +global using Masa.BuildingBlocks.Data.Mapping.Options.Enum; +global using Masa.Contrib.Data.Mapping.Mapster.Internal; +global using Masa.Contrib.Data.Mapping.Mapster.Internal.Options; +global using Microsoft.Extensions.DependencyInjection; +global using Microsoft.Extensions.DependencyInjection.Extensions; diff --git a/test/Masa.Contrib.Data.Mapping.Mapster.Tests/BaseMappingTest.cs b/test/Masa.Contrib.Data.Mapping.Mapster.Tests/BaseMappingTest.cs new file mode 100644 index 000000000..11c5b7ac8 --- /dev/null +++ b/test/Masa.Contrib.Data.Mapping.Mapster.Tests/BaseMappingTest.cs @@ -0,0 +1,20 @@ +// Copyright (c) MASA Stack All rights reserved. +// Licensed under the MIT License. See LICENSE.txt in the project root for license information. + +namespace Masa.Contrib.Data.Mapping.Mapster.Tests; + +[TestClass] +public class BaseMappingTest +{ + protected IServiceCollection _services; + protected IMapping _mapper = default!; + + [TestInitialize] + public void Initialize() + { + _services = new ServiceCollection(); + _services.AddMapping(); + var serviceProvider = _services.BuildServiceProvider(); + _mapper = serviceProvider.GetRequiredService(); + } +} diff --git a/test/Masa.Contrib.Data.Mapping.Mapster.Tests/Domain/Aggregates/Orders/Order.cs b/test/Masa.Contrib.Data.Mapping.Mapster.Tests/Domain/Aggregates/Orders/Order.cs new file mode 100644 index 000000000..cf21dff84 --- /dev/null +++ b/test/Masa.Contrib.Data.Mapping.Mapster.Tests/Domain/Aggregates/Orders/Order.cs @@ -0,0 +1,36 @@ +// Copyright (c) MASA Stack All rights reserved. +// Licensed under the MIT License. See LICENSE.txt in the project root for license information. + +namespace Masa.Contrib.Data.Mapping.Mapster.Tests.Domain.Aggregates.Orders; + +public class Order +{ + public Guid Id { get; set; } + + public string Name { get; set; } + + public decimal TotalPrice { get; set; } + + public List OrderItems { get; set; } + + private Order() + { + Id = Guid.NewGuid(); + } + + public Order(string name) : this() + { + Name = name; + } + + public Order(string name, OrderItem orderItem) : this(name, new List { orderItem }) + { + } + + public Order(string name, List orderItems) : this(name) + { + Name = name; + OrderItems = orderItems; + TotalPrice = orderItems.Sum(item => item.Price * item.Number); + } +} diff --git a/test/Masa.Contrib.Data.Mapping.Mapster.Tests/Domain/Aggregates/Orders/OrderItem.cs b/test/Masa.Contrib.Data.Mapping.Mapster.Tests/Domain/Aggregates/Orders/OrderItem.cs new file mode 100644 index 000000000..ff48919c7 --- /dev/null +++ b/test/Masa.Contrib.Data.Mapping.Mapster.Tests/Domain/Aggregates/Orders/OrderItem.cs @@ -0,0 +1,25 @@ +// Copyright (c) MASA Stack All rights reserved. +// Licensed under the MIT License. See LICENSE.txt in the project root for license information. + +namespace Masa.Contrib.Data.Mapping.Mapster.Tests.Domain.Aggregates.Orders; + +public class OrderItem +{ + public string Name { get; set; } + + public decimal Price { get; set; } + + public int Number { get; set; } + + public OrderItem(string name, decimal price) : this(name, price, 1) + { + + } + + public OrderItem(string name, decimal price, int number) + { + Name = name; + Price = price; + Number = number; + } +} diff --git a/test/Masa.Contrib.Data.Mapping.Mapster.Tests/Domain/Aggregates/Users/User.cs b/test/Masa.Contrib.Data.Mapping.Mapster.Tests/Domain/Aggregates/Users/User.cs new file mode 100644 index 000000000..ac3609192 --- /dev/null +++ b/test/Masa.Contrib.Data.Mapping.Mapster.Tests/Domain/Aggregates/Users/User.cs @@ -0,0 +1,41 @@ +// Copyright (c) MASA Stack All rights reserved. +// Licensed under the MIT License. See LICENSE.txt in the project root for license information. + +namespace Masa.Contrib.Data.Mapping.Mapster.Tests.Domain.Aggregates.Users; + +public class User +{ + public string Name { get; set; } + + public int Age { get; set; } + + public string Description { get; set; } + + public DateTime Birthday { get; set; } + + public AddressItem Hometown { get; set; } + + + public User() + { + + } + public User(string name) + { + Name = name; + } + + public User(string name, int age, string description, DateTime birthday) + : this(name) + { + Age = age; + Description = description; + Birthday = birthday; + } + + public User(string name, int age, string description, DateTime birthday, AddressItem hometown) + : this(name, age, description, birthday) + { + Hometown = hometown; + } +} diff --git a/test/Masa.Contrib.Data.Mapping.Mapster.Tests/Domain/ValueObjects/AddressItem.cs b/test/Masa.Contrib.Data.Mapping.Mapster.Tests/Domain/ValueObjects/AddressItem.cs new file mode 100644 index 000000000..b99b089b9 --- /dev/null +++ b/test/Masa.Contrib.Data.Mapping.Mapster.Tests/Domain/ValueObjects/AddressItem.cs @@ -0,0 +1,25 @@ +// Copyright (c) MASA Stack All rights reserved. +// Licensed under the MIT License. See LICENSE.txt in the project root for license information. + +namespace Masa.Contrib.Data.Mapping.Mapster.Tests.Domain.ValueObjects; + +public class AddressItem +{ + public string Province { get; set; } + + public string City { get; set; } + + public string Address { get; set; } + + public AddressItem(string fullAddress) : this(fullAddress.Split(',')[0], fullAddress.Split(',')[1], fullAddress.Split(',')[2]) + { + + } + + public AddressItem(string province, string city, string address) + { + Province = province; + City = city; + Address = address; + } +} diff --git a/test/Masa.Contrib.Data.Mapping.Mapster.Tests/MappingFormTest.cs b/test/Masa.Contrib.Data.Mapping.Mapster.Tests/MappingFormTest.cs new file mode 100644 index 000000000..f3ecb681c --- /dev/null +++ b/test/Masa.Contrib.Data.Mapping.Mapster.Tests/MappingFormTest.cs @@ -0,0 +1,27 @@ +// Copyright (c) MASA Stack All rights reserved. +// Licensed under the MIT License. See LICENSE.txt in the project root for license information. + +namespace Masa.Contrib.Data.Mapping.Mapster.Tests; + +[TestClass] +public class MappingFormTest : BaseMappingTest +{ + [TestMethod] + public void TestUseShareModeReturnMapRuleCountIs1() + { + var request = new CreateUserRequest() + { + Name = "Jim", + }; + _mapper.Map(request); + Assert.IsTrue(TypeAdapterConfig.GlobalSettings.RuleMap.Count == 1); + } + + [TestMethod] + public void TestAddMultiMapping() + { + _services.AddMapping(); + var mappings = _services.BuildServiceProvider().GetServices(); + Assert.IsTrue(mappings.Count() == 1); + } +} diff --git a/test/Masa.Contrib.Data.Mapping.Mapster.Tests/MappingTest.cs b/test/Masa.Contrib.Data.Mapping.Mapster.Tests/MappingTest.cs new file mode 100644 index 000000000..ceb5d22af --- /dev/null +++ b/test/Masa.Contrib.Data.Mapping.Mapster.Tests/MappingTest.cs @@ -0,0 +1,191 @@ +// Copyright (c) MASA Stack All rights reserved. +// Licensed under the MIT License. See LICENSE.txt in the project root for license information. + +namespace Masa.Contrib.Data.Mapping.Mapster.Tests; + +[TestClass] +public class MappingTest : BaseMappingTest +{ + [TestMethod] + public void TestCreateUserRequestMapToUserReturnUserNameEqualRequestName() + { + var request = new CreateUserRequest() + { + Name = "Jim", + }; + var user = _mapper.Map(request); + Assert.IsNotNull(user); + Assert.IsTrue(user.Name == request.Name); + } + + [TestMethod] + public void TestObjectMapToUserReturnUserIsNotNull() + { + var request = new + { + Name = "Jim", + Age = 18, + Birthday = DateTime.Now, + Description = "i am jim" + }; + + var user = _mapper.Map(request); + Assert.IsNotNull(user); + Assert.AreEqual(request.Name, user.Name); + Assert.AreEqual(request.Age, user.Age); + Assert.AreEqual(request.Birthday, user.Birthday); + Assert.AreEqual(request.Description, user.Description); + } + + [TestMethod] + public void TestObjectMapToUserAndSourceParameterGreatherThanDestinationControllerParameterLength() + { + var request = new + { + Name = "Jim", + Age = 18, + Birthday = DateTime.Now, + Description = "i am jim", + Tag = Array.Empty() + }; + + var user = _mapper.Map(request); + Assert.IsNotNull(user); + Assert.AreEqual(request.Name, user.Name); + Assert.AreEqual(request.Age, user.Age); + Assert.AreEqual(request.Birthday, user.Birthday); + Assert.AreEqual(request.Description, user.Description); + } + + [TestMethod] + public void TestCreateFullUserRequestMapToUserReturnHometownIsNotNull() + { + var request = new CreateFullUserRequest() + { + Name = "Jim", + Age = 18, + Birthday = DateTime.Now, + Hometown = new AddressItemRequest() + { + Province = "BeiJing", + City = "BeiJing", + Address = "National Sport Stadium" + } + }; + + var user = _mapper.Map(request); + Assert.IsNotNull(user); + Assert.AreEqual(request.Name, user.Name); + Assert.AreEqual(request.Age, user.Age); + Assert.AreEqual(request.Birthday, user.Birthday); + Assert.AreEqual(request.Description, user.Description); + Assert.IsNotNull(request.Hometown); + Assert.AreEqual(request.Hometown.Province, user.Hometown.Province); + Assert.AreEqual(request.Hometown.City, user.Hometown.City); + Assert.AreEqual(request.Hometown.Address, user.Hometown.Address); + } + + [TestMethod] + public void TestOrderRequestMapToOrderReturnTotalPriceIs10() + { + var request = new OrderRequest() + { + Name = "orderName", + OrderItem = new OrderItem("apple", 10) + }; + + var order = _mapper.Map(request); + Assert.IsNotNull(order); + Assert.AreEqual(order.Name, request.Name); + Assert.AreEqual(order.OrderItems.Count, 1); + Assert.AreEqual(order.OrderItems[0].Name, request.OrderItem.Name); + Assert.AreEqual(order.OrderItems[0].Price, request.OrderItem.Price); + Assert.AreEqual(order.OrderItems[0].Number, 1); + Assert.AreEqual(order.TotalPrice, 1 * 10); + } + + [TestMethod] + public void TestOrderMultiRequestMapToOrderReturnOrderItemsCountIs1AndTotalPriceIs10() + { + var request = new + { + Name = "Order Name", + OrderItems = new List() + { + new("Apple", 10) + } + }; + + var order = _mapper.Map(request); + Assert.IsNotNull(order); + Assert.AreEqual(order.Name, request.Name); + Assert.AreEqual(order.OrderItems.Count, 1); + Assert.AreEqual(order.OrderItems[0].Name, request.OrderItems[0].Name); + Assert.AreEqual(order.OrderItems[0].Price, request.OrderItems[0].Price); + Assert.AreEqual(order.OrderItems[0].Number, 1); + Assert.AreEqual(order.TotalPrice, 10); + } + + [TestMethod] + public void TestOrderMultiRequestMapToOrderReturnOrderItemsCountIs1() + { + var request = new OrderMultiRequest() + { + Name = "Order Name", + OrderItems = new List() + { + new() + { + Name = "Apple", + Price = 10, + Number = 1 + } + } + }; + + var order = _mapper.Map(request); + Assert.IsNotNull(order); + Assert.AreEqual(order.Name, request.Name); + Assert.AreEqual(order.OrderItems.Count, 1); + Assert.AreEqual(order.OrderItems[0].Name, request.OrderItems[0].Name); + Assert.AreEqual(order.OrderItems[0].Price, request.OrderItems[0].Price); + Assert.AreEqual(order.OrderItems[0].Number, 1); + Assert.AreEqual(order.TotalPrice, 0); + } + + [TestMethod] + public void TestMapToExistingObject() + { + var request = new + { + Name = "Jim", + Age = 18 + }; + User user = new User("Time") + { + Description = "Description", + }; + + var newUser = _mapper.Map(request, user); + Assert.IsNotNull(newUser); + Assert.IsTrue(newUser.Description == "Description"); + Assert.IsTrue(newUser.Name == "Jim"); + Assert.IsTrue(newUser.Age == 18); + } + + [TestMethod] + public void TestCreateUserRequestListMapToUsers() + { + List requests = new List() + { + new() + { + Name = "Jim" + } + }; + List users = new(); + var newUsers = _mapper.Map(requests, users); + Assert.IsTrue(newUsers.Count == 1); + Assert.IsTrue(newUsers[0].Name == "Jim"); + } +} diff --git a/test/Masa.Contrib.Data.Mapping.Mapster.Tests/Masa.Contrib.Data.Mapping.Mapster.Tests.csproj b/test/Masa.Contrib.Data.Mapping.Mapster.Tests/Masa.Contrib.Data.Mapping.Mapster.Tests.csproj new file mode 100644 index 000000000..9590f54b8 --- /dev/null +++ b/test/Masa.Contrib.Data.Mapping.Mapster.Tests/Masa.Contrib.Data.Mapping.Mapster.Tests.csproj @@ -0,0 +1,22 @@ + + + + net6.0 + enable + + false + + + + + + + + + + + + + + + diff --git a/test/Masa.Contrib.Data.Mapping.Mapster.Tests/Requests/AddressItemRequest.cs b/test/Masa.Contrib.Data.Mapping.Mapster.Tests/Requests/AddressItemRequest.cs new file mode 100644 index 000000000..8981f95dc --- /dev/null +++ b/test/Masa.Contrib.Data.Mapping.Mapster.Tests/Requests/AddressItemRequest.cs @@ -0,0 +1,13 @@ +// Copyright (c) MASA Stack All rights reserved. +// Licensed under the MIT License. See LICENSE.txt in the project root for license information. + +namespace Masa.Contrib.Data.Mapping.Mapster.Tests.Requests; + +public class AddressItemRequest +{ + public string Province { get; set; } + + public string City { get; set; } + + public string Address { get; set; } +} diff --git a/test/Masa.Contrib.Data.Mapping.Mapster.Tests/Requests/Orders/OrderItemRequest.cs b/test/Masa.Contrib.Data.Mapping.Mapster.Tests/Requests/Orders/OrderItemRequest.cs new file mode 100644 index 000000000..afdf4f0b5 --- /dev/null +++ b/test/Masa.Contrib.Data.Mapping.Mapster.Tests/Requests/Orders/OrderItemRequest.cs @@ -0,0 +1,13 @@ +// Copyright (c) MASA Stack All rights reserved. +// Licensed under the MIT License. See LICENSE.txt in the project root for license information. + +namespace Masa.Contrib.Data.Mapping.Mapster.Tests.Requests.Orders; + +public class OrderItemRequest +{ + public string Name { get; set; }= default!; + + public decimal Price { get; set; } + + public int Number { get; set; } +} diff --git a/test/Masa.Contrib.Data.Mapping.Mapster.Tests/Requests/Orders/OrderMultiRequest.cs b/test/Masa.Contrib.Data.Mapping.Mapster.Tests/Requests/Orders/OrderMultiRequest.cs new file mode 100644 index 000000000..a3a3a1e39 --- /dev/null +++ b/test/Masa.Contrib.Data.Mapping.Mapster.Tests/Requests/Orders/OrderMultiRequest.cs @@ -0,0 +1,13 @@ +// Copyright (c) MASA Stack All rights reserved. +// Licensed under the MIT License. See LICENSE.txt in the project root for license information. + +namespace Masa.Contrib.Data.Mapping.Mapster.Tests.Requests.Orders; + +public class OrderMultiRequest +{ + public Guid Id { get; set; } + + public string Name { get; set; } = default!; + + public List OrderItems { get; set; } = default!; +} diff --git a/test/Masa.Contrib.Data.Mapping.Mapster.Tests/Requests/Orders/OrderRequest.cs b/test/Masa.Contrib.Data.Mapping.Mapster.Tests/Requests/Orders/OrderRequest.cs new file mode 100644 index 000000000..9c8139dc2 --- /dev/null +++ b/test/Masa.Contrib.Data.Mapping.Mapster.Tests/Requests/Orders/OrderRequest.cs @@ -0,0 +1,11 @@ +// Copyright (c) MASA Stack All rights reserved. +// Licensed under the MIT License. See LICENSE.txt in the project root for license information. + +namespace Masa.Contrib.Data.Mapping.Mapster.Tests.Requests.Orders; + +public class OrderRequest +{ + public string Name { get; set; }= default!; + + public OrderItem OrderItem { get; set; }= default!; +} diff --git a/test/Masa.Contrib.Data.Mapping.Mapster.Tests/Requests/Users/CreateFullUserRequest.cs b/test/Masa.Contrib.Data.Mapping.Mapster.Tests/Requests/Users/CreateFullUserRequest.cs new file mode 100644 index 000000000..e6ccb82b6 --- /dev/null +++ b/test/Masa.Contrib.Data.Mapping.Mapster.Tests/Requests/Users/CreateFullUserRequest.cs @@ -0,0 +1,15 @@ +// Copyright (c) MASA Stack All rights reserved. +// Licensed under the MIT License. See LICENSE.txt in the project root for license information. + +namespace Masa.Contrib.Data.Mapping.Mapster.Tests.Requests.Users; + +public class CreateFullUserRequest : CreateUserRequest +{ + public int Age { get; set; } + + public string Description { get; set; } = default!; + + public DateTime Birthday { get; set; } + + public AddressItemRequest Hometown { get; set; } = default!; +} diff --git a/test/Masa.Contrib.Data.Mapping.Mapster.Tests/Requests/Users/CreateUserRequest.cs b/test/Masa.Contrib.Data.Mapping.Mapster.Tests/Requests/Users/CreateUserRequest.cs new file mode 100644 index 000000000..feb0041f9 --- /dev/null +++ b/test/Masa.Contrib.Data.Mapping.Mapster.Tests/Requests/Users/CreateUserRequest.cs @@ -0,0 +1,9 @@ +// Copyright (c) MASA Stack All rights reserved. +// Licensed under the MIT License. See LICENSE.txt in the project root for license information. + +namespace Masa.Contrib.Data.Mapping.Mapster.Tests.Requests.Users; + +public class CreateUserRequest +{ + public string Name { get; set; } = default!; +} diff --git a/test/Masa.Contrib.Data.Mapping.Mapster.Tests/_Imports.cs b/test/Masa.Contrib.Data.Mapping.Mapster.Tests/_Imports.cs new file mode 100644 index 000000000..5f1728879 --- /dev/null +++ b/test/Masa.Contrib.Data.Mapping.Mapster.Tests/_Imports.cs @@ -0,0 +1,16 @@ +// Copyright (c) MASA Stack All rights reserved. +// Licensed under the MIT License. See LICENSE.txt in the project root for license information. + +global using Mapster; +global using Microsoft.Extensions.DependencyInjection; +global using Microsoft.VisualStudio.TestTools.UnitTesting; +global using System; +global using System.Collections.Generic; +global using System.Linq; +global using Masa.Contrib.Data.Mapping.Mapster.Tests.Domain.Aggregates.Orders; +global using Masa.Contrib.Data.Mapping.Mapster.Tests.Domain.Aggregates.Users; +global using Masa.Contrib.Data.Mapping.Mapster.Tests.Requests; +global using Masa.Contrib.Data.Mapping.Mapster.Tests.Requests.Orders; +global using Masa.Contrib.Data.Mapping.Mapster.Tests.Requests.Users; +global using Masa.Contrib.Data.Mapping.Mapster.Tests.Domain.ValueObjects; +global using Masa.BuildingBlocks.Data.Mapping; From 828ab502e1001f3cb2338734882a53d7f5de65fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=90=AC=E9=9B=A8=E5=A3=B0?= Date: Thu, 5 May 2022 10:53:38 +0800 Subject: [PATCH 2/8] Update .editorconfig --- .editorconfig | 2 -- 1 file changed, 2 deletions(-) diff --git a/.editorconfig b/.editorconfig index 4e1549bd7..e0d8c1a2e 100644 --- a/.editorconfig +++ b/.editorconfig @@ -31,5 +31,3 @@ indent_size = 2 # JSON files [*.json] indent_size = 2 - -file_header_template = 添加文件头(add file header)示例程序\n Copyright (c) https://ittranslator.cn/ \ No newline at end of file From 9eb0e4bf7446f3fdb66d8febd1ca7781565af6f9 Mon Sep 17 00:00:00 2001 From: zhenlei520 Date: Thu, 5 May 2022 10:56:32 +0800 Subject: [PATCH 3/8] chore: Modify global using sorting --- .../Masa.Contrib.Data.Mapping.Mapster/_Imports.cs | 11 +++++++---- .../_Imports.cs | 14 +++++++------- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/src/Data/Mapping/Masa.Contrib.Data.Mapping.Mapster/_Imports.cs b/src/Data/Mapping/Masa.Contrib.Data.Mapping.Mapster/_Imports.cs index 53048da12..b9b142b57 100644 --- a/src/Data/Mapping/Masa.Contrib.Data.Mapping.Mapster/_Imports.cs +++ b/src/Data/Mapping/Masa.Contrib.Data.Mapping.Mapster/_Imports.cs @@ -1,12 +1,15 @@ -global using System.Reflection; -global using System.Linq.Expressions; +// Copyright (c) MASA Stack All rights reserved. +// Licensed under the MIT License. See LICENSE.txt in the project root for license information. + global using Mapster; +global using Mapster.Models; global using Masa.BuildingBlocks.Data.Mapping; global using Masa.BuildingBlocks.Data.Mapping.Options; -global using System.Collections.Concurrent; -global using Mapster.Models; global using Masa.BuildingBlocks.Data.Mapping.Options.Enum; global using Masa.Contrib.Data.Mapping.Mapster.Internal; global using Masa.Contrib.Data.Mapping.Mapster.Internal.Options; global using Microsoft.Extensions.DependencyInjection; global using Microsoft.Extensions.DependencyInjection.Extensions; +global using System.Collections.Concurrent; +global using System.Linq.Expressions; +global using System.Reflection; diff --git a/test/Masa.Contrib.Data.Mapping.Mapster.Tests/_Imports.cs b/test/Masa.Contrib.Data.Mapping.Mapster.Tests/_Imports.cs index 5f1728879..0f0e72afa 100644 --- a/test/Masa.Contrib.Data.Mapping.Mapster.Tests/_Imports.cs +++ b/test/Masa.Contrib.Data.Mapping.Mapster.Tests/_Imports.cs @@ -2,15 +2,15 @@ // Licensed under the MIT License. See LICENSE.txt in the project root for license information. global using Mapster; -global using Microsoft.Extensions.DependencyInjection; -global using Microsoft.VisualStudio.TestTools.UnitTesting; -global using System; -global using System.Collections.Generic; -global using System.Linq; +global using Masa.BuildingBlocks.Data.Mapping; global using Masa.Contrib.Data.Mapping.Mapster.Tests.Domain.Aggregates.Orders; global using Masa.Contrib.Data.Mapping.Mapster.Tests.Domain.Aggregates.Users; +global using Masa.Contrib.Data.Mapping.Mapster.Tests.Domain.ValueObjects; global using Masa.Contrib.Data.Mapping.Mapster.Tests.Requests; global using Masa.Contrib.Data.Mapping.Mapster.Tests.Requests.Orders; global using Masa.Contrib.Data.Mapping.Mapster.Tests.Requests.Users; -global using Masa.Contrib.Data.Mapping.Mapster.Tests.Domain.ValueObjects; -global using Masa.BuildingBlocks.Data.Mapping; +global using Microsoft.Extensions.DependencyInjection; +global using Microsoft.VisualStudio.TestTools.UnitTesting; +global using System; +global using System.Collections.Generic; +global using System.Linq; From 67d104a052541a5931640ab4d39342f9dea35745 Mon Sep 17 00:00:00 2001 From: zhenlei520 Date: Thu, 5 May 2022 18:23:59 +0800 Subject: [PATCH 4/8] chore: Optimization parameter naming --- .../DefaultMappingConfigProvider.cs | 25 ++++++++----------- .../Internal/InvokeBuilder.cs | 12 ++++----- .../README.md | 4 +-- .../README.zh-CN.md | 4 +-- 4 files changed, 20 insertions(+), 25 deletions(-) diff --git a/src/Data/Mapping/Masa.Contrib.Data.Mapping.Mapster/DefaultMappingConfigProvider.cs b/src/Data/Mapping/Masa.Contrib.Data.Mapping.Mapster/DefaultMappingConfigProvider.cs index 7def4e769..2bc9e9e48 100644 --- a/src/Data/Mapping/Masa.Contrib.Data.Mapping.Mapster/DefaultMappingConfigProvider.cs +++ b/src/Data/Mapping/Masa.Contrib.Data.Mapping.Mapster/DefaultMappingConfigProvider.cs @@ -5,8 +5,7 @@ namespace Masa.Contrib.Data.Mapping.Mapster; public class DefaultMappingConfigProvider : IMappingConfigProvider { - private readonly ConcurrentDictionary<(Type SourceType, Type DestinationType, MapOptions? mapOptions), TypeAdapterConfig?> - _store = new(); + private readonly ConcurrentDictionary<(Type SourceType, Type DestinationType, MapOptions? MapOptions), TypeAdapterConfig?> _store = new(); private readonly MapOptions _options; @@ -21,15 +20,7 @@ protected virtual TypeAdapterConfig GetConfigByCache(Type sourceType, Type desti (sourceType, destinationType, options), type => GetAdapterConfig(type.SourceType, type.DestinationType, options)); - if (config == null) - { - if (IsShare(options)) - return TypeAdapterConfig.GlobalSettings; - - throw new ArgumentNullException(nameof(config), "Failed to get mapping configuration"); - } - - return config; + return config ?? GetDefaultConfig(options); } protected virtual TypeAdapterConfig? GetAdapterConfig(Type sourceType, Type destinationType, MapOptions? options) @@ -137,7 +128,7 @@ protected virtual bool IsCollection(Type type) protected virtual ConstructorInfo GetBestConstructor(List destinationConstructors, List sourceProperties) { if (destinationConstructors.Count <= 1) - return destinationConstructors.FirstOrDefault()!; + return destinationConstructors.First(); foreach (var constructor in destinationConstructors) { @@ -185,8 +176,12 @@ protected virtual bool RuleMapIsExist(TypeAdapterConfig adapterConfig, Type sour protected virtual TypeAdapterConfig GetDefaultConfig(MapOptions? options) { //todo: Other modes are currently not supported, and will be added in the future according to the situation - return IsShare(options) ? - TypeAdapterConfig.GlobalSettings : - throw new ArgumentException("Only shared configuration is supported", nameof(MapOptions.Mode)); + switch (options?.Mode ?? _options.Mode) + { + case MapMode.Shared: + return TypeAdapterConfig.GlobalSettings; + default: + throw new ArgumentException("Only shared configuration is supported", nameof(MapOptions.Mode)); + } } } diff --git a/src/Data/Mapping/Masa.Contrib.Data.Mapping.Mapster/Internal/InvokeBuilder.cs b/src/Data/Mapping/Masa.Contrib.Data.Mapping.Mapster/Internal/InvokeBuilder.cs index 6b91d39b6..9ae65c460 100644 --- a/src/Data/Mapping/Masa.Contrib.Data.Mapping.Mapster/Internal/InvokeBuilder.cs +++ b/src/Data/Mapping/Masa.Contrib.Data.Mapping.Mapster/Internal/InvokeBuilder.cs @@ -5,14 +5,14 @@ namespace Masa.Contrib.Data.Mapping.Mapster.Internal; internal class InvokeBuilder { - private static readonly MethodInfo NewConfigMethodInfo; - private static readonly Type TypeAdapterConfigType; + private static readonly MethodInfo _newConfigMethodInfo; + private static readonly Type _typeAdapterConfigType; static InvokeBuilder() { var typeAdapterSetterExpandType = typeof(TypeAdapterSetterExpand); - NewConfigMethodInfo = typeAdapterSetterExpandType.GetMethod(nameof(TypeAdapterSetterExpand.NewConfigByConstructor))!; - TypeAdapterConfigType = typeof(TypeAdapterConfig); + _newConfigMethodInfo = typeAdapterSetterExpandType.GetMethod(nameof(TypeAdapterSetterExpand.NewConfigByConstructor))!; + _typeAdapterConfigType = typeof(TypeAdapterConfig); } internal delegate TypeAdapterSetter MethodExecutor(TypeAdapterConfig target, object parameter); @@ -21,11 +21,11 @@ public static MethodExecutor Build( Type sourceType, Type destinationType) { - var methodInfo = NewConfigMethodInfo.MakeGenericMethod(sourceType, destinationType); + var methodInfo = _newConfigMethodInfo.MakeGenericMethod(sourceType, destinationType); ParameterExpression[] parameters = { - Expression.Parameter(TypeAdapterConfigType, "adapterConfigParameter"), + Expression.Parameter(_typeAdapterConfigType, "adapterConfigParameter"), Expression.Parameter(typeof(object), "constructorInfoParameter") }; var newConfigMethodCall = Expression.Call( diff --git a/src/Data/Mapping/Masa.Contrib.Data.Mapping.Mapster/README.md b/src/Data/Mapping/Masa.Contrib.Data.Mapping.Mapster/README.md index 662ac35b2..c84e655ed 100644 --- a/src/Data/Mapping/Masa.Contrib.Data.Mapping.Mapster/README.md +++ b/src/Data/Mapping/Masa.Contrib.Data.Mapping.Mapster/README.md @@ -21,14 +21,14 @@ Masa.Contrib.Data.Mapping.Mapster is an object-to-object mapper based on [Mapste 3. Mapping objects ```` - IMapping mapper;// Get through DI + IMapping mapping;// Get through DI var request = new { Name = "Teach you to learn Dapr...", OrderItem = new OrderItem("Teach you to learn Dapr hand by hand", 49.9m) }; - var order = mapper.Map(request);// Map the request to a new object + var order = mapping.Map(request);// Map the request to a new object, Parameters with the same attribute name and type of the source object and the target object will be automatically mapped, or the constructor parameter name (case-insensitive) and type of the target object are the same as those of the source object, and they will be mapped through the constructor ```` Mapping class `Order`: diff --git a/src/Data/Mapping/Masa.Contrib.Data.Mapping.Mapster/README.zh-CN.md b/src/Data/Mapping/Masa.Contrib.Data.Mapping.Mapster/README.zh-CN.md index dbab048db..055cd5c0e 100644 --- a/src/Data/Mapping/Masa.Contrib.Data.Mapping.Mapster/README.zh-CN.md +++ b/src/Data/Mapping/Masa.Contrib.Data.Mapping.Mapster/README.zh-CN.md @@ -21,14 +21,14 @@ Masa.Contrib.Data.Mapping.Mapster是基于[Mapster](https://github.com/MapsterMa 3. 映射对象 ``` - IMapping mapper;// 通过DI获取 + IMapping mapping;// 通过DI获取 var request = new { Name = "Teach you to learn Dapr ……", OrderItem = new OrderItem("Teach you to learn Dapr hand by hand", 49.9m) }; - var order = mapper.Map(request);// 将request映射到新的对象 + var order = mapping.Map(request);// 将request映射到新的对象, 源对象与目标对象属性名称、类型一致的参数会自动映射、或者目标对象的构造函数参数名称(不区分大小写)、类型与源对象参数一致的,会通过构造函数映射 ``` 映射类`Order`: From 545b9c7fa2b0fa2f46628296c90f936cba11c9bd Mon Sep 17 00:00:00 2001 From: zhenlei520 Date: Fri, 6 May 2022 11:58:36 +0800 Subject: [PATCH 5/8] chore: IMapping changed its name to IMapper --- src/BuildingBlocks/MASA.BuildingBlocks | 2 +- .../{DefaultMapping.cs => DefaultMapper.cs} | 4 ++-- src/Data/Mapping/Masa.Contrib.Data.Mapping.Mapster/README.md | 4 ++-- .../Mapping/Masa.Contrib.Data.Mapping.Mapster/README.zh-CN.md | 4 ++-- .../ServiceCollectionExtensions.cs | 2 +- .../BaseMappingTest.cs | 4 ++-- .../MappingFormTest.cs | 2 +- 7 files changed, 11 insertions(+), 11 deletions(-) rename src/Data/Mapping/Masa.Contrib.Data.Mapping.Mapster/{DefaultMapping.cs => DefaultMapper.cs} (92%) diff --git a/src/BuildingBlocks/MASA.BuildingBlocks b/src/BuildingBlocks/MASA.BuildingBlocks index db985617a..0abbf7f2a 160000 --- a/src/BuildingBlocks/MASA.BuildingBlocks +++ b/src/BuildingBlocks/MASA.BuildingBlocks @@ -1 +1 @@ -Subproject commit db985617a9ae4ce729d39ee36c75130703ac14dc +Subproject commit 0abbf7f2aab54d8cbb7b8be7fb064c7749f30c05 diff --git a/src/Data/Mapping/Masa.Contrib.Data.Mapping.Mapster/DefaultMapping.cs b/src/Data/Mapping/Masa.Contrib.Data.Mapping.Mapster/DefaultMapper.cs similarity index 92% rename from src/Data/Mapping/Masa.Contrib.Data.Mapping.Mapster/DefaultMapping.cs rename to src/Data/Mapping/Masa.Contrib.Data.Mapping.Mapster/DefaultMapper.cs index b4dec16a7..ed893e326 100644 --- a/src/Data/Mapping/Masa.Contrib.Data.Mapping.Mapster/DefaultMapping.cs +++ b/src/Data/Mapping/Masa.Contrib.Data.Mapping.Mapster/DefaultMapper.cs @@ -3,11 +3,11 @@ namespace Masa.Contrib.Data.Mapping.Mapster; -public class DefaultMapping : IMapping +public class DefaultMapper : IMapper { private readonly IMappingConfigProvider _provider; - public DefaultMapping(IMappingConfigProvider provider) + public DefaultMapper(IMappingConfigProvider provider) => _provider = provider; public TDestination Map(TSource source, MapOptions? options = null) diff --git a/src/Data/Mapping/Masa.Contrib.Data.Mapping.Mapster/README.md b/src/Data/Mapping/Masa.Contrib.Data.Mapping.Mapster/README.md index c84e655ed..713814b2d 100644 --- a/src/Data/Mapping/Masa.Contrib.Data.Mapping.Mapster/README.md +++ b/src/Data/Mapping/Masa.Contrib.Data.Mapping.Mapster/README.md @@ -21,14 +21,14 @@ Masa.Contrib.Data.Mapping.Mapster is an object-to-object mapper based on [Mapste 3. Mapping objects ```` - IMapping mapping;// Get through DI + IMapping mapper;// Get through DI var request = new { Name = "Teach you to learn Dapr...", OrderItem = new OrderItem("Teach you to learn Dapr hand by hand", 49.9m) }; - var order = mapping.Map(request);// Map the request to a new object, Parameters with the same attribute name and type of the source object and the target object will be automatically mapped, or the constructor parameter name (case-insensitive) and type of the target object are the same as those of the source object, and they will be mapped through the constructor + var order = mapper.Map(request);// Map the request to a new object, Parameters with the same attribute name and type of the source object and the target object will be automatically mapped, or the constructor parameter name (case-insensitive) and type of the target object are the same as those of the source object, and they will be mapped through the constructor ```` Mapping class `Order`: diff --git a/src/Data/Mapping/Masa.Contrib.Data.Mapping.Mapster/README.zh-CN.md b/src/Data/Mapping/Masa.Contrib.Data.Mapping.Mapster/README.zh-CN.md index 055cd5c0e..e6407591e 100644 --- a/src/Data/Mapping/Masa.Contrib.Data.Mapping.Mapster/README.zh-CN.md +++ b/src/Data/Mapping/Masa.Contrib.Data.Mapping.Mapster/README.zh-CN.md @@ -21,14 +21,14 @@ Masa.Contrib.Data.Mapping.Mapster是基于[Mapster](https://github.com/MapsterMa 3. 映射对象 ``` - IMapping mapping;// 通过DI获取 + IMapper mapper;// 通过DI获取 var request = new { Name = "Teach you to learn Dapr ……", OrderItem = new OrderItem("Teach you to learn Dapr hand by hand", 49.9m) }; - var order = mapping.Map(request);// 将request映射到新的对象, 源对象与目标对象属性名称、类型一致的参数会自动映射、或者目标对象的构造函数参数名称(不区分大小写)、类型与源对象参数一致的,会通过构造函数映射 + var order = mapper.Map(request);// 将request映射到新的对象, 源对象与目标对象属性名称、类型一致的参数会自动映射、或者目标对象的构造函数参数名称(不区分大小写)、类型与源对象参数一致的,会通过构造函数映射 ``` 映射类`Order`: diff --git a/src/Data/Mapping/Masa.Contrib.Data.Mapping.Mapster/ServiceCollectionExtensions.cs b/src/Data/Mapping/Masa.Contrib.Data.Mapping.Mapster/ServiceCollectionExtensions.cs index b28cbb088..9bf9f05fd 100644 --- a/src/Data/Mapping/Masa.Contrib.Data.Mapping.Mapster/ServiceCollectionExtensions.cs +++ b/src/Data/Mapping/Masa.Contrib.Data.Mapping.Mapster/ServiceCollectionExtensions.cs @@ -22,7 +22,7 @@ public static IServiceCollection AddMapping(this IServiceCollection services, Ma services.AddSingleton(); services.TryAddSingleton(_ => new DefaultMappingConfigProvider(mapOptions)); - services.TryAddSingleton(); + services.TryAddSingleton(); return services; } diff --git a/test/Masa.Contrib.Data.Mapping.Mapster.Tests/BaseMappingTest.cs b/test/Masa.Contrib.Data.Mapping.Mapster.Tests/BaseMappingTest.cs index 11c5b7ac8..89fdba96e 100644 --- a/test/Masa.Contrib.Data.Mapping.Mapster.Tests/BaseMappingTest.cs +++ b/test/Masa.Contrib.Data.Mapping.Mapster.Tests/BaseMappingTest.cs @@ -7,7 +7,7 @@ namespace Masa.Contrib.Data.Mapping.Mapster.Tests; public class BaseMappingTest { protected IServiceCollection _services; - protected IMapping _mapper = default!; + protected IMapper _mapper = default!; [TestInitialize] public void Initialize() @@ -15,6 +15,6 @@ public void Initialize() _services = new ServiceCollection(); _services.AddMapping(); var serviceProvider = _services.BuildServiceProvider(); - _mapper = serviceProvider.GetRequiredService(); + _mapper = serviceProvider.GetRequiredService(); } } diff --git a/test/Masa.Contrib.Data.Mapping.Mapster.Tests/MappingFormTest.cs b/test/Masa.Contrib.Data.Mapping.Mapster.Tests/MappingFormTest.cs index f3ecb681c..c95d7acd7 100644 --- a/test/Masa.Contrib.Data.Mapping.Mapster.Tests/MappingFormTest.cs +++ b/test/Masa.Contrib.Data.Mapping.Mapster.Tests/MappingFormTest.cs @@ -21,7 +21,7 @@ public void TestUseShareModeReturnMapRuleCountIs1() public void TestAddMultiMapping() { _services.AddMapping(); - var mappings = _services.BuildServiceProvider().GetServices(); + var mappings = _services.BuildServiceProvider().GetServices(); Assert.IsTrue(mappings.Count() == 1); } } From 6e96601941f89280f3eef850213dea66496dc8b2 Mon Sep 17 00:00:00 2001 From: zhenlei520 Date: Fri, 6 May 2022 14:17:01 +0800 Subject: [PATCH 6/8] docs(Mapping): Modify the Mapping document --- .../Masa.Contrib.Data.Mapping.Mapster/README.md | 8 ++++++++ .../Masa.Contrib.Data.Mapping.Mapster/README.zh-CN.md | 11 ++++++++++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/Data/Mapping/Masa.Contrib.Data.Mapping.Mapster/README.md b/src/Data/Mapping/Masa.Contrib.Data.Mapping.Mapster/README.md index 713814b2d..e6e57fc95 100644 --- a/src/Data/Mapping/Masa.Contrib.Data.Mapping.Mapster/README.md +++ b/src/Data/Mapping/Masa.Contrib.Data.Mapping.Mapster/README.md @@ -4,6 +4,14 @@ Masa.Contrib.Data.Mapping.Mapster is an object-to-object mapper based on [Mapster](https://github.com/MapsterMapper/Mapster). It adds automatic acquisition and uses the best constructor mapping on the original basis. Nested mapping is supported to reduce the workload of mapping. +## Mapping Rules + +* When the target object has no constructor: use an empty constructor, which maps to fields and properties. + +* Target object has multiple constructors: get the best constructor map + + > Best constructor: The number of parameters of the target object constructor is searched in descending order, the parameter names are the same (case-insensitive), and the parameter types are the same as the source object properties + ## Example: 1. Install `Masa.Contrib.Data.Mapping.Mapster` diff --git a/src/Data/Mapping/Masa.Contrib.Data.Mapping.Mapster/README.zh-CN.md b/src/Data/Mapping/Masa.Contrib.Data.Mapping.Mapster/README.zh-CN.md index e6407591e..4fc18741b 100644 --- a/src/Data/Mapping/Masa.Contrib.Data.Mapping.Mapster/README.zh-CN.md +++ b/src/Data/Mapping/Masa.Contrib.Data.Mapping.Mapster/README.zh-CN.md @@ -4,6 +4,14 @@ Masa.Contrib.Data.Mapping.Mapster是基于[Mapster](https://github.com/MapsterMapper/Mapster)的一个对象到对象的映射器,在原来的基础上增加自动获取并使用最佳构造函数映射,支持嵌套映射,减轻映射的工作量。 +## 映射规则 + +* 目标对象没有构造函数时:使用空构造函数,映射到字段和属性。 + +* 目标对象存在多个构造函数:获取最佳构造函数映射 + + > 最佳构造函数: 目标对象构造函数参数数量从大到小降序查找,参数名称一致(不区分大小写)且参数类型与源对象属性一致 + ## 用例: 1. 安装`Masa.Contrib.Data.Mapping.Mapster` @@ -28,7 +36,8 @@ Masa.Contrib.Data.Mapping.Mapster是基于[Mapster](https://github.com/MapsterMa Name = "Teach you to learn Dapr ……", OrderItem = new OrderItem("Teach you to learn Dapr hand by hand", 49.9m) }; - var order = mapper.Map(request);// 将request映射到新的对象, 源对象与目标对象属性名称、类型一致的参数会自动映射、或者目标对象的构造函数参数名称(不区分大小写)、类型与源对象参数一致的,会通过构造函数映射 + var order = mapper.Map(request);// 将request映射到新的对象 + ``` 映射类`Order`: From 7c5576c0a6ec9c22d52d59a16bdac20c60c553cb Mon Sep 17 00:00:00 2001 From: zhenlei520 Date: Fri, 6 May 2022 14:26:18 +0800 Subject: [PATCH 7/8] docs(Mapping): Modify the Mapping document --- src/Data/Mapping/Masa.Contrib.Data.Mapping.Mapster/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Data/Mapping/Masa.Contrib.Data.Mapping.Mapster/README.md b/src/Data/Mapping/Masa.Contrib.Data.Mapping.Mapster/README.md index e6e57fc95..6be488089 100644 --- a/src/Data/Mapping/Masa.Contrib.Data.Mapping.Mapster/README.md +++ b/src/Data/Mapping/Masa.Contrib.Data.Mapping.Mapster/README.md @@ -29,7 +29,7 @@ Masa.Contrib.Data.Mapping.Mapster is an object-to-object mapper based on [Mapste 3. Mapping objects ```` - IMapping mapper;// Get through DI + IMapper mapper;// Get through DI var request = new { From 3c380c73dd51f17fe74aeaf8f84045287df2be5e Mon Sep 17 00:00:00 2001 From: zhenlei520 Date: Fri, 6 May 2022 14:29:53 +0800 Subject: [PATCH 8/8] chore: Modify mappings to mappers --- .../MappingFormTest.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/Masa.Contrib.Data.Mapping.Mapster.Tests/MappingFormTest.cs b/test/Masa.Contrib.Data.Mapping.Mapster.Tests/MappingFormTest.cs index c95d7acd7..485a85c4a 100644 --- a/test/Masa.Contrib.Data.Mapping.Mapster.Tests/MappingFormTest.cs +++ b/test/Masa.Contrib.Data.Mapping.Mapster.Tests/MappingFormTest.cs @@ -21,7 +21,7 @@ public void TestUseShareModeReturnMapRuleCountIs1() public void TestAddMultiMapping() { _services.AddMapping(); - var mappings = _services.BuildServiceProvider().GetServices(); - Assert.IsTrue(mappings.Count() == 1); + var mappers = _services.BuildServiceProvider().GetServices(); + Assert.IsTrue(mappers.Count() == 1); } }