From 2bd6c6e60fa03be2278a03f3bf23078fbf6d3f67 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Thu, 3 Sep 2020 20:03:13 +0100 Subject: [PATCH 01/65] Start of AgileMapper.Buildable project --- .../AgileMapper.Buildable.csproj | 30 +++++++++++++++++++ AgileMapper.sln | 9 ++++++ AgileMapper/AgileMapper.csproj | 2 -- AgileMapper/Properties/AssemblyInfo.cs | 2 +- 4 files changed, 40 insertions(+), 3 deletions(-) create mode 100644 AgileMapper.Buildable/AgileMapper.Buildable.csproj diff --git a/AgileMapper.Buildable/AgileMapper.Buildable.csproj b/AgileMapper.Buildable/AgileMapper.Buildable.csproj new file mode 100644 index 000000000..9a4b68831 --- /dev/null +++ b/AgileMapper.Buildable/AgileMapper.Buildable.csproj @@ -0,0 +1,30 @@ + + + + net461;netstandard1.5 + 8.0 + AgileObjects.AgileMapper.Buildable + AgileObjects.AgileMapper.Buildable + AgileObjects.AgileMapper.Buildable + false + false + true + true + + AgileObjects.AgileMapper.Buildable + AgileMapper.Buildable + + Mapper, Mapping, Mappings, ViewModel, DTO, NetStandard + https://github.com/AgileObjects/AgileMapper + MIT + + 0.1.0 + 0.1.0.0 + 0.1.0.0 + + + + + + + diff --git a/AgileMapper.sln b/AgileMapper.sln index 7342d6df9..d7a3d105a 100644 --- a/AgileMapper.sln +++ b/AgileMapper.sln @@ -61,6 +61,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AgileMapper.UnitTests", "Ag EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AgileMapper.UnitTests.NetCore3.1", "AgileMapper.UnitTests.NetCore3.1\AgileMapper.UnitTests.NetCore3.1.csproj", "{A4D51412-D8C0-4462-B7C9-1ABDA4AE13B2}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Buildable", "Buildable", "{C0C3194B-D7C8-470F-8B7D-47BA75E0EDAE}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AgileMapper.Buildable", "AgileMapper.Buildable\AgileMapper.Buildable.csproj", "{F55FFD5B-EF1F-4937-B2D2-B3324F9962D6}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -155,6 +159,10 @@ Global {A4D51412-D8C0-4462-B7C9-1ABDA4AE13B2}.Debug|Any CPU.Build.0 = Debug|Any CPU {A4D51412-D8C0-4462-B7C9-1ABDA4AE13B2}.Release|Any CPU.ActiveCfg = Release|Any CPU {A4D51412-D8C0-4462-B7C9-1ABDA4AE13B2}.Release|Any CPU.Build.0 = Release|Any CPU + {F55FFD5B-EF1F-4937-B2D2-B3324F9962D6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F55FFD5B-EF1F-4937-B2D2-B3324F9962D6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F55FFD5B-EF1F-4937-B2D2-B3324F9962D6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F55FFD5B-EF1F-4937-B2D2-B3324F9962D6}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -183,6 +191,7 @@ Global {A6A0E6FF-E114-4F26-99E9-CAD1C03BD743} = {88D48136-E176-4AF7-ACA4-A2954F3BD55B} {5085AA3F-A5B7-40E2-9309-7E0B81FBE113} = {88D48136-E176-4AF7-ACA4-A2954F3BD55B} {A4D51412-D8C0-4462-B7C9-1ABDA4AE13B2} = {88D48136-E176-4AF7-ACA4-A2954F3BD55B} + {F55FFD5B-EF1F-4937-B2D2-B3324F9962D6} = {C0C3194B-D7C8-470F-8B7D-47BA75E0EDAE} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {C39E9BDB-0077-445C-8F09-801CAE1AF8AB} diff --git a/AgileMapper/AgileMapper.csproj b/AgileMapper/AgileMapper.csproj index 88b116af3..9340bc920 100644 --- a/AgileMapper/AgileMapper.csproj +++ b/AgileMapper/AgileMapper.csproj @@ -16,8 +16,6 @@ A zero-configuration, highly-configurable, unopinionated object mapper with viewable execution plans. Flattens, unflattens, deep clones, merges, updates and projects queries. Targets .NET 3.5+ and .NET Standard 1.0+ Mapper, Mapping, Mappings, ViewModel, DTO, NetStandard https://github.com/AgileObjects/AgileMapper - 1.6.1 - $(PackageTargetFallback);dnxcore50 MIT ./Icon.png - Fixing complex type collection Dictionary mapping, re: #203 diff --git a/AgileMapper/Properties/AssemblyInfo.cs b/AgileMapper/Properties/AssemblyInfo.cs index ef2e6891b..821441a79 100644 --- a/AgileMapper/Properties/AssemblyInfo.cs +++ b/AgileMapper/Properties/AssemblyInfo.cs @@ -4,7 +4,7 @@ using System.Security; [assembly: AssemblyTitle("AgileObjects.AgileMapper")] -[assembly: AssemblyDescription("A zero-configuration, highly-configurable object-object mapper with viewable execution plans. Performs deep clones, updates and merges via extension methods, or a static or instance API. Targets .NET Standard 1.0+ and .NET 3.5+")] +[assembly: AssemblyDescription("A zero-configuration, highly-configurable, unopinionated object mapper with viewable execution plans. Flattens, unflattens, deep clones, merges, updates and projects queries. Targets .NET 3.5+ and .NET Standard 1.0+")] [assembly: AllowPartiallyTrustedCallers] [assembly: CLSCompliant(true)] From 9939b6f3ad0bae6f64c21926962a2cce1718673d Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Fri, 4 Sep 2020 08:20:05 +0100 Subject: [PATCH 02/65] Start of buildable mapper work / Moving test classes to UnitTests.Common --- .../AgileMapper.Buildable.UnitTests.csproj | 29 ++++++++++++ .../WhenBuildingMapperSourceCode.cs | 25 +++++++++++ .../AgileMapper.Buildable.csproj | 5 +++ .../BuildableMapperExtensions.cs | 45 +++++++++++++++++++ ...gileMapper.PerformanceTester.Net461.csproj | 4 +- .../packages.config | 2 +- .../TestClasses/PublicField.cs | 2 +- .../TestClasses/PublicProperty.cs | 2 +- .../WhenConfiguringDataSources.cs | 2 +- .../WhenConfiguringMappingCallbacks.cs | 1 + .../WhenConvertingToStrings.cs | 2 +- .../WhenValidatingMappings.cs | 1 + .../WhenViewingMappingPlans.cs | 3 +- ...ConfiguringConstructorDataSourcesInline.cs | 1 + .../WhenConfiguringDataSourcesInline.cs | 1 + ...ConfiguringDataSourcesInlineIncorrectly.cs | 1 + .../WhenConfiguringNameMatchingInline.cs | 1 + .../WhenConfiguringObjectMappingInline.cs | 1 + .../WhenConfiguringStringFormattingInline.cs | 1 + .../WhenConfiguringTypeIdentifiersInline.cs | 1 + .../WhenIgnoringMembersInlineIncorrectly.cs | 1 + ...enIgnoringSourceMemberInlineIncorrectly.cs | 1 + ...gnoringSourceMembersByValueFilterInline.cs | 1 + .../WhenApplyingMapperConfigurations.cs | 1 + .../WhenConfiguringConstructorDataSources.cs | 1 + .../WhenConfiguringDataSourcesIncorrectly.cs | 1 + .../WhenConfiguringDerivedTypes.cs | 1 + .../WhenConfiguringNameMatching.cs | 1 + .../WhenConfiguringObjectCreation.cs | 1 + .../WhenConfiguringObjectCreationCallbacks.cs | 1 + .../WhenConfiguringObjectMapping.cs | 1 + .../WhenConfiguringReverseDataSources.cs | 1 + ...onfiguringReverseDataSourcesIncorrectly.cs | 1 + .../WhenConfiguringSequentialDataSources.cs | 1 + .../WhenConfiguringSimpleTypeCreation.cs | 1 + .../WhenConfiguringStringFormatting.cs | 1 + .../WhenConfiguringToTargetDataSources.cs | 1 + ...nfiguringToTargetDataSourcesIncorrectly.cs | 1 + ...enConfiguringToTargetInsteadDataSources.cs | 1 + .../Configuration/WhenIgnoringMembers.cs | 1 + .../WhenIgnoringMembersByFilter.cs | 1 + .../WhenIgnoringMembersByGlobalFilter.cs | 1 + .../WhenIgnoringMembersIncorrectly.cs | 1 + .../WhenIgnoringSourceMembers.cs | 1 + .../WhenIgnoringSourceMembersByFilter.cs | 1 + ...WhenIgnoringSourceMembersByGlobalFilter.cs | 1 + .../WhenIgnoringSourceMembersByValueFilter.cs | 1 + .../WhenIgnoringSourceMembersIncorrectly.cs | 1 + .../Configuration/WhenMappingToNull.cs | 1 + .../Configuration/WhenResolvingServices.cs | 1 + ...ConfiguringDictionaryMappingIncorrectly.cs | 1 + .../WhenConfiguringNestedDictionaryMapping.cs | 1 + .../WhenConfiguringSourceDictionaryMapping.cs | 1 + .../WhenFlatteningToDictionaries.cs | 1 + ...MappingFromDictionariesOnToComplexTypes.cs | 2 +- ...ngFromDictionariesOnToEnumerableMembers.cs | 1 + ...MappingFromDictionariesOverComplexTypes.cs | 1 + ...appingFromDictionariesToNewComplexTypes.cs | 1 + ...gFromDictionariesToNewEnumerableMembers.cs | 1 + ...MappingFromDictionariesToNewEnumerables.cs | 1 + .../WhenMappingFromDictionaryMembers.cs | 1 + .../WhenMappingOnToDictionaries.cs | 1 + .../WhenMappingOnToDictionaryMembers.cs | 1 + .../WhenMappingOverDictionaries.cs | 1 + .../WhenMappingOverDictionaryMembers.cs | 1 + .../WhenMappingToNewDictionaries.cs | 1 + .../WhenUnflatteningFromDictionaries.cs | 1 + .../WhenConfiguringSourceDynamicMapping.cs | 1 + .../Dynamics/WhenFlatteningToDynamics.cs | 1 + ...appingFromDynamicsOverEnumerableMembers.cs | 1 + .../WhenMappingFromDynamicsOverEnumerables.cs | 1 + ...henMappingFromDynamicsToNewComplexTypes.cs | 1 + ...ppingFromDynamicsToNewEnumerableMembers.cs | 1 + .../Dynamics/WhenMappingOnToDynamicMembers.cs | 1 + .../Dynamics/WhenMappingOverDynamicMembers.cs | 1 + .../WhenMappingToNewDynamicMembers.cs | 1 + .../WhenMappingToNewEnumerablesOfDynamic.cs | 1 + .../Internal/WhenEquatingExpressions.cs | 1 + .../WhenMappingViaExtensionMethods.cs | 1 + .../WhenUnflatteningViaExtensionMethods.cs | 1 + .../WhenCloningConstructorDataSources.cs | 1 + .../MapperCloning/WhenCloningDataSources.cs | 1 + .../WhenCloningStringFormatting.cs | 1 + ...henCreatingTargetMembersFromExpressions.cs | 1 + .../Members/WhenDeterminingRecursion.cs | 1 + .../Members/WhenFindingDataSources.cs | 2 +- .../Members/WhenFindingSourceMembers.cs | 1 + .../Members/WhenFindingTargetMembers.cs | 1 + .../WhenMatchingSourceToTargetMembers.cs | 1 + .../WhenConvertingToBools.cs | 1 + .../WhenConvertingToDateTimeOffsets.cs | 2 +- .../WhenConvertingToDateTimes.cs | 2 +- .../WhenConvertingToFlagsEnums.cs | 1 + .../WhenConvertingToGuids.cs | 1 + .../WhenConvertingToTimeSpans.cs | 2 +- .../WhenConfiguringStructCreationCallbacks.cs | 1 + .../WhenConfiguringStructDataSources.cs | 1 + .../WhenMappingFromDictionariesToStructs.cs | 1 + .../Structs/WhenMappingOnToStructs.cs | 1 + .../Structs/WhenMappingOverStructMembers.cs | 1 + .../Structs/WhenMappingOverStructs.cs | 1 + .../Structs/WhenMappingToNewStructMembers.cs | 1 + .../Structs/WhenMappingToNewStructs.cs | 1 + .../Structs/WhenMappingToStructEnumerables.cs | 1 + .../WhenMappingCircularReferences.cs | 1 + .../WhenMappingDerivedTypes.cs | 1 + .../WhenMappingOnToComplexTypeMembers.cs | 1 + .../WhenMappingOnToComplexTypes.cs | 1 + .../WhenMappingOnToEnumerableMembers.cs | 1 + .../WhenMappingOverComplexTypeMembers.cs | 1 + .../WhenMappingOverComplexTypes.cs | 1 + .../WhenMappingOverEnumerableMembers.cs | 1 + .../WhenMappingOverEnumerables.cs | 1 + .../WhenMappingToConstructors.cs | 1 + .../WhenMappingToMetaMembers.cs | 1 + .../WhenMappingToNewComplexTypeMembers.cs | 1 + .../WhenMappingToNewComplexTypes.cs | 1 + .../WhenMappingToNewEnumerableMembers.cs | 1 + .../WhenMappingToNewEnumerables.cs | 1 + .../WhenUnflatteningFromQueryStrings.cs | 1 + .../WhenUsingFactoryMethods.cs | 1 + .../WhenViewingMappingPlans.cs | 5 ++- AgileMapper.sln | 7 +++ AgileMapper/AgileMapper.csproj | 2 +- AgileMapper/IMapper.cs | 3 +- AgileMapper/Mapper.cs | 5 ++- AgileMapper/Plans/MappingPlanSet.cs | 14 +++--- 127 files changed, 247 insertions(+), 25 deletions(-) create mode 100644 AgileMapper.Buildable.UnitTests/AgileMapper.Buildable.UnitTests.csproj create mode 100644 AgileMapper.Buildable.UnitTests/WhenBuildingMapperSourceCode.cs create mode 100644 AgileMapper.Buildable/BuildableMapperExtensions.cs rename {AgileMapper.UnitTests => AgileMapper.UnitTests.Common}/TestClasses/PublicField.cs (51%) rename {AgileMapper.UnitTests => AgileMapper.UnitTests.Common}/TestClasses/PublicProperty.cs (65%) diff --git a/AgileMapper.Buildable.UnitTests/AgileMapper.Buildable.UnitTests.csproj b/AgileMapper.Buildable.UnitTests/AgileMapper.Buildable.UnitTests.csproj new file mode 100644 index 000000000..9a82333f9 --- /dev/null +++ b/AgileMapper.Buildable.UnitTests/AgileMapper.Buildable.UnitTests.csproj @@ -0,0 +1,29 @@ + + + + net461 + 8.0 + AgileObjects.AgileMapper.Buildable.UnitTests + AgileObjects.AgileMapper.Buildable.UnitTests + true + + 0649;1701;1702 + full + false + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + diff --git a/AgileMapper.Buildable.UnitTests/WhenBuildingMapperSourceCode.cs b/AgileMapper.Buildable.UnitTests/WhenBuildingMapperSourceCode.cs new file mode 100644 index 000000000..ce2b34100 --- /dev/null +++ b/AgileMapper.Buildable.UnitTests/WhenBuildingMapperSourceCode.cs @@ -0,0 +1,25 @@ +namespace AgileObjects.AgileMapper.Buildable.UnitTests +{ + using AgileMapper.UnitTests.Common; + using AgileObjects.AgileMapper.UnitTests.Common.TestClasses; + using Xunit; + + public class WhenBuildingMapperSourceCode + { + [Fact] + public void ShouldBuildASingleSimpleSourceCodeFileViaTheInstanceApi() + { + using (var mapper = Mapper.CreateNew()) + { + mapper.GetPlanFor>().ToANew>(); + + var sourceCodeExpressions = mapper.BuildSourceCode(); + + var sourceCode = sourceCodeExpressions + .ShouldHaveSingleItem() + .ToSourceCode() + .ShouldNotBeNull(); + } + } + } +} diff --git a/AgileMapper.Buildable/AgileMapper.Buildable.csproj b/AgileMapper.Buildable/AgileMapper.Buildable.csproj index 9a4b68831..ca1c36f97 100644 --- a/AgileMapper.Buildable/AgileMapper.Buildable.csproj +++ b/AgileMapper.Buildable/AgileMapper.Buildable.csproj @@ -10,6 +10,7 @@ false true true + false AgileObjects.AgileMapper.Buildable AgileMapper.Buildable @@ -23,6 +24,10 @@ 0.1.0.0 + + + + diff --git a/AgileMapper.Buildable/BuildableMapperExtensions.cs b/AgileMapper.Buildable/BuildableMapperExtensions.cs new file mode 100644 index 000000000..bf330acc0 --- /dev/null +++ b/AgileMapper.Buildable/BuildableMapperExtensions.cs @@ -0,0 +1,45 @@ +namespace AgileObjects.AgileMapper.Buildable +{ + using System.Collections.Generic; + using System.Linq; + using System.Linq.Expressions; + using BuildableExpressions; + using BuildableExpressions.SourceCode; + + /// + /// Provides extension methods for building AgileMapper mapper source files. + /// + public static class BuildableMapperExtensions + { + /// + /// Builds s for the configured mappers in this + /// . + /// + /// + /// The for which to build s. + /// + /// + /// A for each mapper configured in this . + /// + public static IEnumerable BuildSourceCode( + this IMapper mapper) + { + return mapper + .GetPlanExpressionsInCache() + .ToMapperSourceCodeExpressions(); + } + + /// + /// Converts these into s. + /// + /// The mapping Expressions to convert. + /// A for each of these . + public static IEnumerable ToMapperSourceCodeExpressions( + this IEnumerable mappingExpressions) + { + return mappingExpressions + .Select(exp => SourceCodeFactory.SourceCode(exp)) + .ToList(); + } + } +} diff --git a/AgileMapper.PerformanceTester.Net461/AgileMapper.PerformanceTester.Net461.csproj b/AgileMapper.PerformanceTester.Net461/AgileMapper.PerformanceTester.Net461.csproj index 587322d49..1b1e3ba4c 100644 --- a/AgileMapper.PerformanceTester.Net461/AgileMapper.PerformanceTester.Net461.csproj +++ b/AgileMapper.PerformanceTester.Net461/AgileMapper.PerformanceTester.Net461.csproj @@ -37,8 +37,8 @@ ..\packages\AgileObjects.NetStandardPolyfills.1.4.1\lib\net40\AgileObjects.NetStandardPolyfills.dll - - ..\packages\AgileObjects.ReadableExpressions.2.5.1\lib\net40\AgileObjects.ReadableExpressions.dll + + ..\packages\AgileObjects.ReadableExpressions.3.0.0-preview1\lib\net40\AgileObjects.ReadableExpressions.dll ..\packages\AutoMapper.7.0.1\lib\net45\AutoMapper.dll diff --git a/AgileMapper.PerformanceTester.Net461/packages.config b/AgileMapper.PerformanceTester.Net461/packages.config index 6c40706b4..cab55e0c7 100644 --- a/AgileMapper.PerformanceTester.Net461/packages.config +++ b/AgileMapper.PerformanceTester.Net461/packages.config @@ -1,7 +1,7 @@  - + diff --git a/AgileMapper.UnitTests/TestClasses/PublicField.cs b/AgileMapper.UnitTests.Common/TestClasses/PublicField.cs similarity index 51% rename from AgileMapper.UnitTests/TestClasses/PublicField.cs rename to AgileMapper.UnitTests.Common/TestClasses/PublicField.cs index 296b0147c..d956a0ddd 100644 --- a/AgileMapper.UnitTests/TestClasses/PublicField.cs +++ b/AgileMapper.UnitTests.Common/TestClasses/PublicField.cs @@ -1,4 +1,4 @@ -namespace AgileObjects.AgileMapper.UnitTests.TestClasses +namespace AgileObjects.AgileMapper.UnitTests.Common.TestClasses { public class PublicField { diff --git a/AgileMapper.UnitTests/TestClasses/PublicProperty.cs b/AgileMapper.UnitTests.Common/TestClasses/PublicProperty.cs similarity index 65% rename from AgileMapper.UnitTests/TestClasses/PublicProperty.cs rename to AgileMapper.UnitTests.Common/TestClasses/PublicProperty.cs index 0932c623f..6614bd9c3 100644 --- a/AgileMapper.UnitTests/TestClasses/PublicProperty.cs +++ b/AgileMapper.UnitTests.Common/TestClasses/PublicProperty.cs @@ -1,4 +1,4 @@ -namespace AgileObjects.AgileMapper.UnitTests.TestClasses +namespace AgileObjects.AgileMapper.UnitTests.Common.TestClasses { public class PublicProperty { diff --git a/AgileMapper.UnitTests.NonParallel/Configuration/WhenConfiguringDataSources.cs b/AgileMapper.UnitTests.NonParallel/Configuration/WhenConfiguringDataSources.cs index 1b2c04f03..e4cb7a9d4 100644 --- a/AgileMapper.UnitTests.NonParallel/Configuration/WhenConfiguringDataSources.cs +++ b/AgileMapper.UnitTests.NonParallel/Configuration/WhenConfiguringDataSources.cs @@ -1,7 +1,7 @@ namespace AgileObjects.AgileMapper.UnitTests.NonParallel.Configuration { using Common; - using TestClasses; + using Common.TestClasses; using Xunit; public class WhenConfiguringDataSources : NonParallelTestsBase diff --git a/AgileMapper.UnitTests.NonParallel/Configuration/WhenConfiguringMappingCallbacks.cs b/AgileMapper.UnitTests.NonParallel/Configuration/WhenConfiguringMappingCallbacks.cs index c64af2613..6d364a143 100644 --- a/AgileMapper.UnitTests.NonParallel/Configuration/WhenConfiguringMappingCallbacks.cs +++ b/AgileMapper.UnitTests.NonParallel/Configuration/WhenConfiguringMappingCallbacks.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; using Common; + using Common.TestClasses; using TestClasses; using Xunit; diff --git a/AgileMapper.UnitTests.NonParallel/SimpleTypeConversion/WhenConvertingToStrings.cs b/AgileMapper.UnitTests.NonParallel/SimpleTypeConversion/WhenConvertingToStrings.cs index 11dd821a7..3d979f81b 100644 --- a/AgileMapper.UnitTests.NonParallel/SimpleTypeConversion/WhenConvertingToStrings.cs +++ b/AgileMapper.UnitTests.NonParallel/SimpleTypeConversion/WhenConvertingToStrings.cs @@ -3,7 +3,7 @@ using System; using System.Globalization; using Common; - using TestClasses; + using Common.TestClasses; using Xunit; public class WhenConvertingToStrings diff --git a/AgileMapper.UnitTests.NonParallel/WhenValidatingMappings.cs b/AgileMapper.UnitTests.NonParallel/WhenValidatingMappings.cs index 2f7f53dbe..c0f93c7e5 100644 --- a/AgileMapper.UnitTests.NonParallel/WhenValidatingMappings.cs +++ b/AgileMapper.UnitTests.NonParallel/WhenValidatingMappings.cs @@ -1,6 +1,7 @@ namespace AgileObjects.AgileMapper.UnitTests.NonParallel { using Common; + using Common.TestClasses; using TestClasses; using Validation; using Xunit; diff --git a/AgileMapper.UnitTests.NonParallel/WhenViewingMappingPlans.cs b/AgileMapper.UnitTests.NonParallel/WhenViewingMappingPlans.cs index 1d6220acb..190e23cfa 100644 --- a/AgileMapper.UnitTests.NonParallel/WhenViewingMappingPlans.cs +++ b/AgileMapper.UnitTests.NonParallel/WhenViewingMappingPlans.cs @@ -1,5 +1,6 @@ namespace AgileObjects.AgileMapper.UnitTests.NonParallel { + using AgileMapper.Extensions.Internal; using Common; using TestClasses; using Xunit; @@ -47,7 +48,7 @@ public void ShouldShowAllCachedMappingPlanExpressionsViaTheStaticApi() var plan = Mapper.GetPlanExpressionsInCache(); - plan.ShouldNotBeNull(); + plan.ShouldNotBeNull().Any().ShouldBeTrue(); }); } } diff --git a/AgileMapper.UnitTests/Configuration/Inline/WhenConfiguringConstructorDataSourcesInline.cs b/AgileMapper.UnitTests/Configuration/Inline/WhenConfiguringConstructorDataSourcesInline.cs index 0a5c24bbe..31c0996c1 100644 --- a/AgileMapper.UnitTests/Configuration/Inline/WhenConfiguringConstructorDataSourcesInline.cs +++ b/AgileMapper.UnitTests/Configuration/Inline/WhenConfiguringConstructorDataSourcesInline.cs @@ -2,6 +2,7 @@ { using System; using Common; + using Common.TestClasses; using TestClasses; #if !NET35 using Xunit; diff --git a/AgileMapper.UnitTests/Configuration/Inline/WhenConfiguringDataSourcesInline.cs b/AgileMapper.UnitTests/Configuration/Inline/WhenConfiguringDataSourcesInline.cs index 7ea4ea930..b3a19169d 100644 --- a/AgileMapper.UnitTests/Configuration/Inline/WhenConfiguringDataSourcesInline.cs +++ b/AgileMapper.UnitTests/Configuration/Inline/WhenConfiguringDataSourcesInline.cs @@ -5,6 +5,7 @@ using AgileMapper.Extensions; using AgileMapper.Members; using Common; + using Common.TestClasses; using TestClasses; #if !NET35 using NetStandardPolyfills; diff --git a/AgileMapper.UnitTests/Configuration/Inline/WhenConfiguringDataSourcesInlineIncorrectly.cs b/AgileMapper.UnitTests/Configuration/Inline/WhenConfiguringDataSourcesInlineIncorrectly.cs index 9371b8085..e43c9747d 100644 --- a/AgileMapper.UnitTests/Configuration/Inline/WhenConfiguringDataSourcesInlineIncorrectly.cs +++ b/AgileMapper.UnitTests/Configuration/Inline/WhenConfiguringDataSourcesInlineIncorrectly.cs @@ -3,6 +3,7 @@ using System; using AgileMapper.Configuration; using Common; + using Common.TestClasses; using TestClasses; #if !NET35 using Xunit; diff --git a/AgileMapper.UnitTests/Configuration/Inline/WhenConfiguringNameMatchingInline.cs b/AgileMapper.UnitTests/Configuration/Inline/WhenConfiguringNameMatchingInline.cs index 43c673eea..282fb1386 100644 --- a/AgileMapper.UnitTests/Configuration/Inline/WhenConfiguringNameMatchingInline.cs +++ b/AgileMapper.UnitTests/Configuration/Inline/WhenConfiguringNameMatchingInline.cs @@ -2,6 +2,7 @@ { using AgileMapper.Configuration; using Common; + using Common.TestClasses; using TestClasses; #if !NET35 using Xunit; diff --git a/AgileMapper.UnitTests/Configuration/Inline/WhenConfiguringObjectMappingInline.cs b/AgileMapper.UnitTests/Configuration/Inline/WhenConfiguringObjectMappingInline.cs index c948663bb..5e2689857 100644 --- a/AgileMapper.UnitTests/Configuration/Inline/WhenConfiguringObjectMappingInline.cs +++ b/AgileMapper.UnitTests/Configuration/Inline/WhenConfiguringObjectMappingInline.cs @@ -7,6 +7,7 @@ using AgileMapper.Extensions.Internal; using Api.Configuration; using Common; + using Common.TestClasses; using TestClasses; #if !NET35 using Xunit; diff --git a/AgileMapper.UnitTests/Configuration/Inline/WhenConfiguringStringFormattingInline.cs b/AgileMapper.UnitTests/Configuration/Inline/WhenConfiguringStringFormattingInline.cs index c923a849a..dd3af2b2b 100644 --- a/AgileMapper.UnitTests/Configuration/Inline/WhenConfiguringStringFormattingInline.cs +++ b/AgileMapper.UnitTests/Configuration/Inline/WhenConfiguringStringFormattingInline.cs @@ -3,6 +3,7 @@ using System; using AgileMapper.Configuration; using Common; + using Common.TestClasses; using TestClasses; #if !NET35 using Xunit; diff --git a/AgileMapper.UnitTests/Configuration/Inline/WhenConfiguringTypeIdentifiersInline.cs b/AgileMapper.UnitTests/Configuration/Inline/WhenConfiguringTypeIdentifiersInline.cs index b060c15e0..164ec7b65 100644 --- a/AgileMapper.UnitTests/Configuration/Inline/WhenConfiguringTypeIdentifiersInline.cs +++ b/AgileMapper.UnitTests/Configuration/Inline/WhenConfiguringTypeIdentifiersInline.cs @@ -5,6 +5,7 @@ using System.Linq; using AgileMapper.Configuration; using Common; + using Common.TestClasses; using TestClasses; #if !NET35 using Xunit; diff --git a/AgileMapper.UnitTests/Configuration/Inline/WhenIgnoringMembersInlineIncorrectly.cs b/AgileMapper.UnitTests/Configuration/Inline/WhenIgnoringMembersInlineIncorrectly.cs index 25fa53df5..37e37f12b 100644 --- a/AgileMapper.UnitTests/Configuration/Inline/WhenIgnoringMembersInlineIncorrectly.cs +++ b/AgileMapper.UnitTests/Configuration/Inline/WhenIgnoringMembersInlineIncorrectly.cs @@ -2,6 +2,7 @@ { using AgileMapper.Configuration; using Common; + using Common.TestClasses; using TestClasses; #if !NET35 using Xunit; diff --git a/AgileMapper.UnitTests/Configuration/Inline/WhenIgnoringSourceMemberInlineIncorrectly.cs b/AgileMapper.UnitTests/Configuration/Inline/WhenIgnoringSourceMemberInlineIncorrectly.cs index ca466a297..f0df0a2db 100644 --- a/AgileMapper.UnitTests/Configuration/Inline/WhenIgnoringSourceMemberInlineIncorrectly.cs +++ b/AgileMapper.UnitTests/Configuration/Inline/WhenIgnoringSourceMemberInlineIncorrectly.cs @@ -2,6 +2,7 @@ { using AgileMapper.Configuration; using Common; + using Common.TestClasses; using TestClasses; #if !NET35 using Xunit; diff --git a/AgileMapper.UnitTests/Configuration/Inline/WhenIgnoringSourceMembersByValueFilterInline.cs b/AgileMapper.UnitTests/Configuration/Inline/WhenIgnoringSourceMembersByValueFilterInline.cs index 9509688e9..3a5143aff 100644 --- a/AgileMapper.UnitTests/Configuration/Inline/WhenIgnoringSourceMembersByValueFilterInline.cs +++ b/AgileMapper.UnitTests/Configuration/Inline/WhenIgnoringSourceMembersByValueFilterInline.cs @@ -6,6 +6,7 @@ using AgileMapper.Extensions.Internal; using AgileObjects.AgileMapper.Configuration; using Common; + using Common.TestClasses; using TestClasses; #if !NET35 using Xunit; diff --git a/AgileMapper.UnitTests/Configuration/WhenApplyingMapperConfigurations.cs b/AgileMapper.UnitTests/Configuration/WhenApplyingMapperConfigurations.cs index 9da790d1c..244d96045 100644 --- a/AgileMapper.UnitTests/Configuration/WhenApplyingMapperConfigurations.cs +++ b/AgileMapper.UnitTests/Configuration/WhenApplyingMapperConfigurations.cs @@ -5,6 +5,7 @@ using System.Linq; using AgileMapper.Configuration; using Common; + using Common.TestClasses; using MoreTestClasses; using NetStandardPolyfills; using TestClasses; diff --git a/AgileMapper.UnitTests/Configuration/WhenConfiguringConstructorDataSources.cs b/AgileMapper.UnitTests/Configuration/WhenConfiguringConstructorDataSources.cs index 28a151d86..fa48297af 100644 --- a/AgileMapper.UnitTests/Configuration/WhenConfiguringConstructorDataSources.cs +++ b/AgileMapper.UnitTests/Configuration/WhenConfiguringConstructorDataSources.cs @@ -3,6 +3,7 @@ namespace AgileObjects.AgileMapper.UnitTests.Configuration using System; using AgileMapper.Extensions; using Common; + using Common.TestClasses; using TestClasses; #if !NET35 using Xunit; diff --git a/AgileMapper.UnitTests/Configuration/WhenConfiguringDataSourcesIncorrectly.cs b/AgileMapper.UnitTests/Configuration/WhenConfiguringDataSourcesIncorrectly.cs index 81b1f9cf5..55381f946 100644 --- a/AgileMapper.UnitTests/Configuration/WhenConfiguringDataSourcesIncorrectly.cs +++ b/AgileMapper.UnitTests/Configuration/WhenConfiguringDataSourcesIncorrectly.cs @@ -5,6 +5,7 @@ using System.Linq; using AgileMapper.Configuration; using Common; + using Common.TestClasses; using TestClasses; #if !NET35 using Xunit; diff --git a/AgileMapper.UnitTests/Configuration/WhenConfiguringDerivedTypes.cs b/AgileMapper.UnitTests/Configuration/WhenConfiguringDerivedTypes.cs index 278a36acf..698f9075f 100644 --- a/AgileMapper.UnitTests/Configuration/WhenConfiguringDerivedTypes.cs +++ b/AgileMapper.UnitTests/Configuration/WhenConfiguringDerivedTypes.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using AgileMapper.Extensions.Internal; using Common; + using Common.TestClasses; using TestClasses; #if !NET35 using Xunit; diff --git a/AgileMapper.UnitTests/Configuration/WhenConfiguringNameMatching.cs b/AgileMapper.UnitTests/Configuration/WhenConfiguringNameMatching.cs index 2183e6b70..5f1f9cc85 100644 --- a/AgileMapper.UnitTests/Configuration/WhenConfiguringNameMatching.cs +++ b/AgileMapper.UnitTests/Configuration/WhenConfiguringNameMatching.cs @@ -5,6 +5,7 @@ using AgileMapper.Configuration; using AgileMapper.Extensions.Internal; using Common; + using Common.TestClasses; using TestClasses; using static System.Environment; #if !NET35 diff --git a/AgileMapper.UnitTests/Configuration/WhenConfiguringObjectCreation.cs b/AgileMapper.UnitTests/Configuration/WhenConfiguringObjectCreation.cs index 7feb448ef..f818fc267 100644 --- a/AgileMapper.UnitTests/Configuration/WhenConfiguringObjectCreation.cs +++ b/AgileMapper.UnitTests/Configuration/WhenConfiguringObjectCreation.cs @@ -6,6 +6,7 @@ using System.Linq; using AgileMapper.Members; using Common; + using Common.TestClasses; using TestClasses; #if !NET35 using Xunit; diff --git a/AgileMapper.UnitTests/Configuration/WhenConfiguringObjectCreationCallbacks.cs b/AgileMapper.UnitTests/Configuration/WhenConfiguringObjectCreationCallbacks.cs index d27a1ff4b..e838f4234 100644 --- a/AgileMapper.UnitTests/Configuration/WhenConfiguringObjectCreationCallbacks.cs +++ b/AgileMapper.UnitTests/Configuration/WhenConfiguringObjectCreationCallbacks.cs @@ -7,6 +7,7 @@ using System.Linq; using AgileMapper.Members; using Common; + using Common.TestClasses; using ReadableExpressions.Extensions; using TestClasses; #if !NET35 diff --git a/AgileMapper.UnitTests/Configuration/WhenConfiguringObjectMapping.cs b/AgileMapper.UnitTests/Configuration/WhenConfiguringObjectMapping.cs index 6e168d77d..ebd82f4fe 100644 --- a/AgileMapper.UnitTests/Configuration/WhenConfiguringObjectMapping.cs +++ b/AgileMapper.UnitTests/Configuration/WhenConfiguringObjectMapping.cs @@ -5,6 +5,7 @@ using System.Linq; using AgileMapper.Extensions.Internal; using Common; + using Common.TestClasses; using TestClasses; #if !NET35 using Xunit; diff --git a/AgileMapper.UnitTests/Configuration/WhenConfiguringReverseDataSources.cs b/AgileMapper.UnitTests/Configuration/WhenConfiguringReverseDataSources.cs index 1df1ff1bf..0d92752d1 100644 --- a/AgileMapper.UnitTests/Configuration/WhenConfiguringReverseDataSources.cs +++ b/AgileMapper.UnitTests/Configuration/WhenConfiguringReverseDataSources.cs @@ -2,6 +2,7 @@ { using System; using Common; + using Common.TestClasses; using TestClasses; #if !NET35 using Xunit; diff --git a/AgileMapper.UnitTests/Configuration/WhenConfiguringReverseDataSourcesIncorrectly.cs b/AgileMapper.UnitTests/Configuration/WhenConfiguringReverseDataSourcesIncorrectly.cs index 02fdecd06..637fd292c 100644 --- a/AgileMapper.UnitTests/Configuration/WhenConfiguringReverseDataSourcesIncorrectly.cs +++ b/AgileMapper.UnitTests/Configuration/WhenConfiguringReverseDataSourcesIncorrectly.cs @@ -3,6 +3,7 @@ using System; using AgileMapper.Configuration; using Common; + using Common.TestClasses; using TestClasses; #if !NET35 using Xunit; diff --git a/AgileMapper.UnitTests/Configuration/WhenConfiguringSequentialDataSources.cs b/AgileMapper.UnitTests/Configuration/WhenConfiguringSequentialDataSources.cs index b6e37dfe8..5fedf6d71 100644 --- a/AgileMapper.UnitTests/Configuration/WhenConfiguringSequentialDataSources.cs +++ b/AgileMapper.UnitTests/Configuration/WhenConfiguringSequentialDataSources.cs @@ -4,6 +4,7 @@ using AgileMapper.Configuration; using AgileMapper.Extensions.Internal; using Common; + using Common.TestClasses; using TestClasses; #if !NET35 using Xunit; diff --git a/AgileMapper.UnitTests/Configuration/WhenConfiguringSimpleTypeCreation.cs b/AgileMapper.UnitTests/Configuration/WhenConfiguringSimpleTypeCreation.cs index 73e2395bc..1466835f0 100644 --- a/AgileMapper.UnitTests/Configuration/WhenConfiguringSimpleTypeCreation.cs +++ b/AgileMapper.UnitTests/Configuration/WhenConfiguringSimpleTypeCreation.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using AgileMapper.Extensions.Internal; using Common; + using Common.TestClasses; using TestClasses; #if !NET35 using Xunit; diff --git a/AgileMapper.UnitTests/Configuration/WhenConfiguringStringFormatting.cs b/AgileMapper.UnitTests/Configuration/WhenConfiguringStringFormatting.cs index 6c1395a3f..5e19945a0 100644 --- a/AgileMapper.UnitTests/Configuration/WhenConfiguringStringFormatting.cs +++ b/AgileMapper.UnitTests/Configuration/WhenConfiguringStringFormatting.cs @@ -3,6 +3,7 @@ using System; using AgileMapper.Configuration; using Common; + using Common.TestClasses; using TestClasses; #if !NET35 using Xunit; diff --git a/AgileMapper.UnitTests/Configuration/WhenConfiguringToTargetDataSources.cs b/AgileMapper.UnitTests/Configuration/WhenConfiguringToTargetDataSources.cs index da63c9e38..c716844ac 100644 --- a/AgileMapper.UnitTests/Configuration/WhenConfiguringToTargetDataSources.cs +++ b/AgileMapper.UnitTests/Configuration/WhenConfiguringToTargetDataSources.cs @@ -5,6 +5,7 @@ using System.Linq; using AgileMapper.Extensions; using Common; + using Common.TestClasses; using TestClasses; #if !NET35 using Xunit; diff --git a/AgileMapper.UnitTests/Configuration/WhenConfiguringToTargetDataSourcesIncorrectly.cs b/AgileMapper.UnitTests/Configuration/WhenConfiguringToTargetDataSourcesIncorrectly.cs index 83252a68b..7bffdba33 100644 --- a/AgileMapper.UnitTests/Configuration/WhenConfiguringToTargetDataSourcesIncorrectly.cs +++ b/AgileMapper.UnitTests/Configuration/WhenConfiguringToTargetDataSourcesIncorrectly.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using AgileMapper.Configuration; using Common; + using Common.TestClasses; using TestClasses; #if !NET35 using Xunit; diff --git a/AgileMapper.UnitTests/Configuration/WhenConfiguringToTargetInsteadDataSources.cs b/AgileMapper.UnitTests/Configuration/WhenConfiguringToTargetInsteadDataSources.cs index 911411b5e..d4d0d68f9 100644 --- a/AgileMapper.UnitTests/Configuration/WhenConfiguringToTargetInsteadDataSources.cs +++ b/AgileMapper.UnitTests/Configuration/WhenConfiguringToTargetInsteadDataSources.cs @@ -5,6 +5,7 @@ using System.Linq; using AgileMapper.Extensions; using Common; + using Common.TestClasses; using TestClasses; #if !NET35 using Xunit; diff --git a/AgileMapper.UnitTests/Configuration/WhenIgnoringMembers.cs b/AgileMapper.UnitTests/Configuration/WhenIgnoringMembers.cs index 3f4eaa4a0..f4ee6b690 100644 --- a/AgileMapper.UnitTests/Configuration/WhenIgnoringMembers.cs +++ b/AgileMapper.UnitTests/Configuration/WhenIgnoringMembers.cs @@ -6,6 +6,7 @@ namespace AgileObjects.AgileMapper.UnitTests.Configuration using AgileMapper.Configuration; using AgileMapper.Configuration.MemberIgnores; using Common; + using Common.TestClasses; using NetStandardPolyfills; using TestClasses; #if !NET35 diff --git a/AgileMapper.UnitTests/Configuration/WhenIgnoringMembersByFilter.cs b/AgileMapper.UnitTests/Configuration/WhenIgnoringMembersByFilter.cs index 928a1f356..fafb9fe41 100644 --- a/AgileMapper.UnitTests/Configuration/WhenIgnoringMembersByFilter.cs +++ b/AgileMapper.UnitTests/Configuration/WhenIgnoringMembersByFilter.cs @@ -3,6 +3,7 @@ namespace AgileObjects.AgileMapper.UnitTests.Configuration using System; using AgileMapper.Extensions.Internal; using Common; + using Common.TestClasses; using NetStandardPolyfills; using TestClasses; #if !NET35 diff --git a/AgileMapper.UnitTests/Configuration/WhenIgnoringMembersByGlobalFilter.cs b/AgileMapper.UnitTests/Configuration/WhenIgnoringMembersByGlobalFilter.cs index 2269dc44b..5aebd3787 100644 --- a/AgileMapper.UnitTests/Configuration/WhenIgnoringMembersByGlobalFilter.cs +++ b/AgileMapper.UnitTests/Configuration/WhenIgnoringMembersByGlobalFilter.cs @@ -3,6 +3,7 @@ namespace AgileObjects.AgileMapper.UnitTests.Configuration using System; using AgileMapper.Extensions.Internal; using Common; + using Common.TestClasses; using NetStandardPolyfills; using TestClasses; #if !NET35 diff --git a/AgileMapper.UnitTests/Configuration/WhenIgnoringMembersIncorrectly.cs b/AgileMapper.UnitTests/Configuration/WhenIgnoringMembersIncorrectly.cs index 23b3ce73d..dd0842db0 100644 --- a/AgileMapper.UnitTests/Configuration/WhenIgnoringMembersIncorrectly.cs +++ b/AgileMapper.UnitTests/Configuration/WhenIgnoringMembersIncorrectly.cs @@ -2,6 +2,7 @@ namespace AgileObjects.AgileMapper.UnitTests.Configuration { using AgileMapper.Configuration; using Common; + using Common.TestClasses; using TestClasses; #if !NET35 using Xunit; diff --git a/AgileMapper.UnitTests/Configuration/WhenIgnoringSourceMembers.cs b/AgileMapper.UnitTests/Configuration/WhenIgnoringSourceMembers.cs index ba5f4abed..466154143 100644 --- a/AgileMapper.UnitTests/Configuration/WhenIgnoringSourceMembers.cs +++ b/AgileMapper.UnitTests/Configuration/WhenIgnoringSourceMembers.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Linq; using Common; + using Common.TestClasses; using TestClasses; #if !NET35 using Xunit; diff --git a/AgileMapper.UnitTests/Configuration/WhenIgnoringSourceMembersByFilter.cs b/AgileMapper.UnitTests/Configuration/WhenIgnoringSourceMembersByFilter.cs index 86f075c4c..67c500013 100644 --- a/AgileMapper.UnitTests/Configuration/WhenIgnoringSourceMembersByFilter.cs +++ b/AgileMapper.UnitTests/Configuration/WhenIgnoringSourceMembersByFilter.cs @@ -3,6 +3,7 @@ using System; using AgileMapper.Extensions.Internal; using Common; + using Common.TestClasses; using NetStandardPolyfills; using TestClasses; #if !NET35 diff --git a/AgileMapper.UnitTests/Configuration/WhenIgnoringSourceMembersByGlobalFilter.cs b/AgileMapper.UnitTests/Configuration/WhenIgnoringSourceMembersByGlobalFilter.cs index f56dc4f25..6efd209fb 100644 --- a/AgileMapper.UnitTests/Configuration/WhenIgnoringSourceMembersByGlobalFilter.cs +++ b/AgileMapper.UnitTests/Configuration/WhenIgnoringSourceMembersByGlobalFilter.cs @@ -1,6 +1,7 @@ namespace AgileObjects.AgileMapper.UnitTests.Configuration { using Common; + using Common.TestClasses; using TestClasses; #if !NET35 using Xunit; diff --git a/AgileMapper.UnitTests/Configuration/WhenIgnoringSourceMembersByValueFilter.cs b/AgileMapper.UnitTests/Configuration/WhenIgnoringSourceMembersByValueFilter.cs index 1331556f8..88dcc8466 100644 --- a/AgileMapper.UnitTests/Configuration/WhenIgnoringSourceMembersByValueFilter.cs +++ b/AgileMapper.UnitTests/Configuration/WhenIgnoringSourceMembersByValueFilter.cs @@ -6,6 +6,7 @@ using System.Globalization; using System.Linq; using Common; + using Common.TestClasses; using TestClasses; #if !NET35 using Xunit; diff --git a/AgileMapper.UnitTests/Configuration/WhenIgnoringSourceMembersIncorrectly.cs b/AgileMapper.UnitTests/Configuration/WhenIgnoringSourceMembersIncorrectly.cs index 9646b5e19..9980e2fa5 100644 --- a/AgileMapper.UnitTests/Configuration/WhenIgnoringSourceMembersIncorrectly.cs +++ b/AgileMapper.UnitTests/Configuration/WhenIgnoringSourceMembersIncorrectly.cs @@ -2,6 +2,7 @@ { using AgileMapper.Configuration; using Common; + using Common.TestClasses; using TestClasses; #if !NET35 using Xunit; diff --git a/AgileMapper.UnitTests/Configuration/WhenMappingToNull.cs b/AgileMapper.UnitTests/Configuration/WhenMappingToNull.cs index 87087d4ff..f6eb1a8a0 100644 --- a/AgileMapper.UnitTests/Configuration/WhenMappingToNull.cs +++ b/AgileMapper.UnitTests/Configuration/WhenMappingToNull.cs @@ -7,6 +7,7 @@ using AgileMapper.Configuration; using AgileMapper.Extensions.Internal; using Common; + using Common.TestClasses; using TestClasses; #if !NET35 using Xunit; diff --git a/AgileMapper.UnitTests/Configuration/WhenResolvingServices.cs b/AgileMapper.UnitTests/Configuration/WhenResolvingServices.cs index db672d932..6c3b2dc64 100644 --- a/AgileMapper.UnitTests/Configuration/WhenResolvingServices.cs +++ b/AgileMapper.UnitTests/Configuration/WhenResolvingServices.cs @@ -5,6 +5,7 @@ using AgileMapper.Configuration; using AgileMapper.Extensions; using Common; + using Common.TestClasses; using TestClasses; #if NET_STANDARD using Microsoft.Extensions.DependencyInjection; diff --git a/AgileMapper.UnitTests/Dictionaries/Configuration/WhenConfiguringDictionaryMappingIncorrectly.cs b/AgileMapper.UnitTests/Dictionaries/Configuration/WhenConfiguringDictionaryMappingIncorrectly.cs index 682eac4a1..88429a1d9 100644 --- a/AgileMapper.UnitTests/Dictionaries/Configuration/WhenConfiguringDictionaryMappingIncorrectly.cs +++ b/AgileMapper.UnitTests/Dictionaries/Configuration/WhenConfiguringDictionaryMappingIncorrectly.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using AgileMapper.Configuration; using Common; + using Common.TestClasses; using TestClasses; #if !NET35 using Xunit; diff --git a/AgileMapper.UnitTests/Dictionaries/Configuration/WhenConfiguringNestedDictionaryMapping.cs b/AgileMapper.UnitTests/Dictionaries/Configuration/WhenConfiguringNestedDictionaryMapping.cs index 7028e8606..ab4a4bb4d 100644 --- a/AgileMapper.UnitTests/Dictionaries/Configuration/WhenConfiguringNestedDictionaryMapping.cs +++ b/AgileMapper.UnitTests/Dictionaries/Configuration/WhenConfiguringNestedDictionaryMapping.cs @@ -2,6 +2,7 @@ { using System.Collections.Generic; using Common; + using Common.TestClasses; using TestClasses; #if !NET35 using Xunit; diff --git a/AgileMapper.UnitTests/Dictionaries/Configuration/WhenConfiguringSourceDictionaryMapping.cs b/AgileMapper.UnitTests/Dictionaries/Configuration/WhenConfiguringSourceDictionaryMapping.cs index c8615e86b..21d7bb3c7 100644 --- a/AgileMapper.UnitTests/Dictionaries/Configuration/WhenConfiguringSourceDictionaryMapping.cs +++ b/AgileMapper.UnitTests/Dictionaries/Configuration/WhenConfiguringSourceDictionaryMapping.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; using Common; + using Common.TestClasses; using TestClasses; #if !NET35 using Xunit; diff --git a/AgileMapper.UnitTests/Dictionaries/WhenFlatteningToDictionaries.cs b/AgileMapper.UnitTests/Dictionaries/WhenFlatteningToDictionaries.cs index 566fcc3c2..08951714f 100644 --- a/AgileMapper.UnitTests/Dictionaries/WhenFlatteningToDictionaries.cs +++ b/AgileMapper.UnitTests/Dictionaries/WhenFlatteningToDictionaries.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; using Common; + using Common.TestClasses; using TestClasses; #if !NET35 using Xunit; diff --git a/AgileMapper.UnitTests/Dictionaries/WhenMappingFromDictionariesOnToComplexTypes.cs b/AgileMapper.UnitTests/Dictionaries/WhenMappingFromDictionariesOnToComplexTypes.cs index 2e1017727..c7d8ae340 100644 --- a/AgileMapper.UnitTests/Dictionaries/WhenMappingFromDictionariesOnToComplexTypes.cs +++ b/AgileMapper.UnitTests/Dictionaries/WhenMappingFromDictionariesOnToComplexTypes.cs @@ -3,7 +3,7 @@ using System; using System.Collections.Generic; using Common; - using TestClasses; + using Common.TestClasses; #if !NET35 using Xunit; #else diff --git a/AgileMapper.UnitTests/Dictionaries/WhenMappingFromDictionariesOnToEnumerableMembers.cs b/AgileMapper.UnitTests/Dictionaries/WhenMappingFromDictionariesOnToEnumerableMembers.cs index 890abdc69..be7766916 100644 --- a/AgileMapper.UnitTests/Dictionaries/WhenMappingFromDictionariesOnToEnumerableMembers.cs +++ b/AgileMapper.UnitTests/Dictionaries/WhenMappingFromDictionariesOnToEnumerableMembers.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; using Common; + using Common.TestClasses; using TestClasses; #if !NET35 using Xunit; diff --git a/AgileMapper.UnitTests/Dictionaries/WhenMappingFromDictionariesOverComplexTypes.cs b/AgileMapper.UnitTests/Dictionaries/WhenMappingFromDictionariesOverComplexTypes.cs index 2afd5332f..edae53dda 100644 --- a/AgileMapper.UnitTests/Dictionaries/WhenMappingFromDictionariesOverComplexTypes.cs +++ b/AgileMapper.UnitTests/Dictionaries/WhenMappingFromDictionariesOverComplexTypes.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; using Common; + using Common.TestClasses; using TestClasses; #if !NET35 using Xunit; diff --git a/AgileMapper.UnitTests/Dictionaries/WhenMappingFromDictionariesToNewComplexTypes.cs b/AgileMapper.UnitTests/Dictionaries/WhenMappingFromDictionariesToNewComplexTypes.cs index 841812127..bfcfdf5b3 100644 --- a/AgileMapper.UnitTests/Dictionaries/WhenMappingFromDictionariesToNewComplexTypes.cs +++ b/AgileMapper.UnitTests/Dictionaries/WhenMappingFromDictionariesToNewComplexTypes.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; using Common; + using Common.TestClasses; using TestClasses; #if !NET35 using Xunit; diff --git a/AgileMapper.UnitTests/Dictionaries/WhenMappingFromDictionariesToNewEnumerableMembers.cs b/AgileMapper.UnitTests/Dictionaries/WhenMappingFromDictionariesToNewEnumerableMembers.cs index 8cc49b562..f1ad12300 100644 --- a/AgileMapper.UnitTests/Dictionaries/WhenMappingFromDictionariesToNewEnumerableMembers.cs +++ b/AgileMapper.UnitTests/Dictionaries/WhenMappingFromDictionariesToNewEnumerableMembers.cs @@ -5,6 +5,7 @@ namespace AgileObjects.AgileMapper.UnitTests.Dictionaries using System.Collections.ObjectModel; using System.Linq; using Common; + using Common.TestClasses; #if !NETCOREAPP1_0 && !NET35 using Microsoft.Extensions.Primitives; #endif diff --git a/AgileMapper.UnitTests/Dictionaries/WhenMappingFromDictionariesToNewEnumerables.cs b/AgileMapper.UnitTests/Dictionaries/WhenMappingFromDictionariesToNewEnumerables.cs index c29afe94e..9b7df15a3 100644 --- a/AgileMapper.UnitTests/Dictionaries/WhenMappingFromDictionariesToNewEnumerables.cs +++ b/AgileMapper.UnitTests/Dictionaries/WhenMappingFromDictionariesToNewEnumerables.cs @@ -4,6 +4,7 @@ namespace AgileObjects.AgileMapper.UnitTests.Dictionaries using System.Collections.Generic; using System.Linq; using Common; + using Common.TestClasses; using TestClasses; #if !NET35 using Xunit; diff --git a/AgileMapper.UnitTests/Dictionaries/WhenMappingFromDictionaryMembers.cs b/AgileMapper.UnitTests/Dictionaries/WhenMappingFromDictionaryMembers.cs index a4d2393f7..fadc87cac 100644 --- a/AgileMapper.UnitTests/Dictionaries/WhenMappingFromDictionaryMembers.cs +++ b/AgileMapper.UnitTests/Dictionaries/WhenMappingFromDictionaryMembers.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; using Common; + using Common.TestClasses; using TestClasses; #if !NET35 using Xunit; diff --git a/AgileMapper.UnitTests/Dictionaries/WhenMappingOnToDictionaries.cs b/AgileMapper.UnitTests/Dictionaries/WhenMappingOnToDictionaries.cs index 31e87aff7..db810c95c 100644 --- a/AgileMapper.UnitTests/Dictionaries/WhenMappingOnToDictionaries.cs +++ b/AgileMapper.UnitTests/Dictionaries/WhenMappingOnToDictionaries.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; using Common; + using Common.TestClasses; using TestClasses; #if !NET35 using Xunit; diff --git a/AgileMapper.UnitTests/Dictionaries/WhenMappingOnToDictionaryMembers.cs b/AgileMapper.UnitTests/Dictionaries/WhenMappingOnToDictionaryMembers.cs index 9af6a086f..95b35e524 100644 --- a/AgileMapper.UnitTests/Dictionaries/WhenMappingOnToDictionaryMembers.cs +++ b/AgileMapper.UnitTests/Dictionaries/WhenMappingOnToDictionaryMembers.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; using Common; + using Common.TestClasses; using TestClasses; #if !NET35 using Xunit; diff --git a/AgileMapper.UnitTests/Dictionaries/WhenMappingOverDictionaries.cs b/AgileMapper.UnitTests/Dictionaries/WhenMappingOverDictionaries.cs index f34f9192e..8770ccfc6 100644 --- a/AgileMapper.UnitTests/Dictionaries/WhenMappingOverDictionaries.cs +++ b/AgileMapper.UnitTests/Dictionaries/WhenMappingOverDictionaries.cs @@ -5,6 +5,7 @@ using System.Collections.ObjectModel; using AgileMapper.Extensions.Internal; using Common; + using Common.TestClasses; using TestClasses; #if !NET35 using Xunit; diff --git a/AgileMapper.UnitTests/Dictionaries/WhenMappingOverDictionaryMembers.cs b/AgileMapper.UnitTests/Dictionaries/WhenMappingOverDictionaryMembers.cs index 613970838..cd3332277 100644 --- a/AgileMapper.UnitTests/Dictionaries/WhenMappingOverDictionaryMembers.cs +++ b/AgileMapper.UnitTests/Dictionaries/WhenMappingOverDictionaryMembers.cs @@ -2,6 +2,7 @@ { using System.Collections.Generic; using Common; + using Common.TestClasses; using TestClasses; #if !NET35 using Xunit; diff --git a/AgileMapper.UnitTests/Dictionaries/WhenMappingToNewDictionaries.cs b/AgileMapper.UnitTests/Dictionaries/WhenMappingToNewDictionaries.cs index 373e78ee9..4e047ce84 100644 --- a/AgileMapper.UnitTests/Dictionaries/WhenMappingToNewDictionaries.cs +++ b/AgileMapper.UnitTests/Dictionaries/WhenMappingToNewDictionaries.cs @@ -5,6 +5,7 @@ using System.Collections.ObjectModel; using System.Linq; using Common; + using Common.TestClasses; using TestClasses; #if !NET35 using Xunit; diff --git a/AgileMapper.UnitTests/Dictionaries/WhenUnflatteningFromDictionaries.cs b/AgileMapper.UnitTests/Dictionaries/WhenUnflatteningFromDictionaries.cs index faf31a501..d8416270b 100644 --- a/AgileMapper.UnitTests/Dictionaries/WhenUnflatteningFromDictionaries.cs +++ b/AgileMapper.UnitTests/Dictionaries/WhenUnflatteningFromDictionaries.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; using Common; + using Common.TestClasses; using TestClasses; #if !NET35 using Xunit; diff --git a/AgileMapper.UnitTests/Dynamics/Configuration/WhenConfiguringSourceDynamicMapping.cs b/AgileMapper.UnitTests/Dynamics/Configuration/WhenConfiguringSourceDynamicMapping.cs index fc439da46..a3cae5b40 100644 --- a/AgileMapper.UnitTests/Dynamics/Configuration/WhenConfiguringSourceDynamicMapping.cs +++ b/AgileMapper.UnitTests/Dynamics/Configuration/WhenConfiguringSourceDynamicMapping.cs @@ -7,6 +7,7 @@ namespace AgileObjects.AgileMapper.UnitTests.Dynamics.Configuration using System.Linq.Expressions; using Api.Configuration; using Common; + using Common.TestClasses; using TestClasses; using Xunit; diff --git a/AgileMapper.UnitTests/Dynamics/WhenFlatteningToDynamics.cs b/AgileMapper.UnitTests/Dynamics/WhenFlatteningToDynamics.cs index 61d30b9d3..ff97e62c3 100644 --- a/AgileMapper.UnitTests/Dynamics/WhenFlatteningToDynamics.cs +++ b/AgileMapper.UnitTests/Dynamics/WhenFlatteningToDynamics.cs @@ -2,6 +2,7 @@ { using System.Collections.Generic; using Common; + using Common.TestClasses; using Microsoft.CSharp.RuntimeBinder; using TestClasses; using Xunit; diff --git a/AgileMapper.UnitTests/Dynamics/WhenMappingFromDynamicsOverEnumerableMembers.cs b/AgileMapper.UnitTests/Dynamics/WhenMappingFromDynamicsOverEnumerableMembers.cs index 8d278e178..8431e76f1 100644 --- a/AgileMapper.UnitTests/Dynamics/WhenMappingFromDynamicsOverEnumerableMembers.cs +++ b/AgileMapper.UnitTests/Dynamics/WhenMappingFromDynamicsOverEnumerableMembers.cs @@ -6,6 +6,7 @@ namespace AgileObjects.AgileMapper.UnitTests.Dynamics using System.Dynamic; using System.Linq; using Common; + using Common.TestClasses; using TestClasses; using Xunit; diff --git a/AgileMapper.UnitTests/Dynamics/WhenMappingFromDynamicsOverEnumerables.cs b/AgileMapper.UnitTests/Dynamics/WhenMappingFromDynamicsOverEnumerables.cs index 1ccebfd43..7785eee15 100644 --- a/AgileMapper.UnitTests/Dynamics/WhenMappingFromDynamicsOverEnumerables.cs +++ b/AgileMapper.UnitTests/Dynamics/WhenMappingFromDynamicsOverEnumerables.cs @@ -6,6 +6,7 @@ namespace AgileObjects.AgileMapper.UnitTests.Dynamics using System.Linq; using AgileMapper.Extensions.Internal; using Common; + using Common.TestClasses; using TestClasses; using Xunit; diff --git a/AgileMapper.UnitTests/Dynamics/WhenMappingFromDynamicsToNewComplexTypes.cs b/AgileMapper.UnitTests/Dynamics/WhenMappingFromDynamicsToNewComplexTypes.cs index 7e853afd6..81466805e 100644 --- a/AgileMapper.UnitTests/Dynamics/WhenMappingFromDynamicsToNewComplexTypes.cs +++ b/AgileMapper.UnitTests/Dynamics/WhenMappingFromDynamicsToNewComplexTypes.cs @@ -4,6 +4,7 @@ namespace AgileObjects.AgileMapper.UnitTests.Dynamics using System; using System.Dynamic; using Common; + using Common.TestClasses; using TestClasses; using Xunit; diff --git a/AgileMapper.UnitTests/Dynamics/WhenMappingFromDynamicsToNewEnumerableMembers.cs b/AgileMapper.UnitTests/Dynamics/WhenMappingFromDynamicsToNewEnumerableMembers.cs index afef7d3f0..a26bf359f 100644 --- a/AgileMapper.UnitTests/Dynamics/WhenMappingFromDynamicsToNewEnumerableMembers.cs +++ b/AgileMapper.UnitTests/Dynamics/WhenMappingFromDynamicsToNewEnumerableMembers.cs @@ -3,6 +3,7 @@ namespace AgileObjects.AgileMapper.UnitTests.Dynamics { using System.Collections.Generic; using System.Dynamic; + using Common.TestClasses; using TestClasses; using Xunit; diff --git a/AgileMapper.UnitTests/Dynamics/WhenMappingOnToDynamicMembers.cs b/AgileMapper.UnitTests/Dynamics/WhenMappingOnToDynamicMembers.cs index 155e45a1d..a5555d687 100644 --- a/AgileMapper.UnitTests/Dynamics/WhenMappingOnToDynamicMembers.cs +++ b/AgileMapper.UnitTests/Dynamics/WhenMappingOnToDynamicMembers.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Dynamic; using Common; + using Common.TestClasses; using Microsoft.CSharp.RuntimeBinder; using TestClasses; using Xunit; diff --git a/AgileMapper.UnitTests/Dynamics/WhenMappingOverDynamicMembers.cs b/AgileMapper.UnitTests/Dynamics/WhenMappingOverDynamicMembers.cs index a82e14e51..1b9e752a8 100644 --- a/AgileMapper.UnitTests/Dynamics/WhenMappingOverDynamicMembers.cs +++ b/AgileMapper.UnitTests/Dynamics/WhenMappingOverDynamicMembers.cs @@ -1,6 +1,7 @@ namespace AgileObjects.AgileMapper.UnitTests.Dynamics { using System.Dynamic; + using Common.TestClasses; using TestClasses; using Xunit; diff --git a/AgileMapper.UnitTests/Dynamics/WhenMappingToNewDynamicMembers.cs b/AgileMapper.UnitTests/Dynamics/WhenMappingToNewDynamicMembers.cs index 27d586a60..0a0202c71 100644 --- a/AgileMapper.UnitTests/Dynamics/WhenMappingToNewDynamicMembers.cs +++ b/AgileMapper.UnitTests/Dynamics/WhenMappingToNewDynamicMembers.cs @@ -1,6 +1,7 @@ namespace AgileObjects.AgileMapper.UnitTests.Dynamics { using System.Dynamic; + using Common.TestClasses; using TestClasses; using Xunit; diff --git a/AgileMapper.UnitTests/Dynamics/WhenMappingToNewEnumerablesOfDynamic.cs b/AgileMapper.UnitTests/Dynamics/WhenMappingToNewEnumerablesOfDynamic.cs index ec8e8d8e4..26db42b31 100644 --- a/AgileMapper.UnitTests/Dynamics/WhenMappingToNewEnumerablesOfDynamic.cs +++ b/AgileMapper.UnitTests/Dynamics/WhenMappingToNewEnumerablesOfDynamic.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; using Common; + using Common.TestClasses; using TestClasses; using Xunit; diff --git a/AgileMapper.UnitTests/Extensions/Internal/WhenEquatingExpressions.cs b/AgileMapper.UnitTests/Extensions/Internal/WhenEquatingExpressions.cs index 6d44a0fac..9cf51cc89 100644 --- a/AgileMapper.UnitTests/Extensions/Internal/WhenEquatingExpressions.cs +++ b/AgileMapper.UnitTests/Extensions/Internal/WhenEquatingExpressions.cs @@ -5,6 +5,7 @@ using System.Linq.Expressions; using AgileMapper.Extensions.Internal; using Common; + using Common.TestClasses; using TestClasses; #if !NET35 using Xunit; diff --git a/AgileMapper.UnitTests/Extensions/WhenMappingViaExtensionMethods.cs b/AgileMapper.UnitTests/Extensions/WhenMappingViaExtensionMethods.cs index f3d3869f7..24fcf4607 100644 --- a/AgileMapper.UnitTests/Extensions/WhenMappingViaExtensionMethods.cs +++ b/AgileMapper.UnitTests/Extensions/WhenMappingViaExtensionMethods.cs @@ -4,6 +4,7 @@ using System.Globalization; using AgileMapper.Extensions; using Common; + using Common.TestClasses; using TestClasses; #if !NET35 using Xunit; diff --git a/AgileMapper.UnitTests/Extensions/WhenUnflatteningViaExtensionMethods.cs b/AgileMapper.UnitTests/Extensions/WhenUnflatteningViaExtensionMethods.cs index f47812e15..9928631a3 100644 --- a/AgileMapper.UnitTests/Extensions/WhenUnflatteningViaExtensionMethods.cs +++ b/AgileMapper.UnitTests/Extensions/WhenUnflatteningViaExtensionMethods.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using AgileMapper.Extensions; using Common; + using Common.TestClasses; using TestClasses; #if !NET35 using Xunit; diff --git a/AgileMapper.UnitTests/MapperCloning/WhenCloningConstructorDataSources.cs b/AgileMapper.UnitTests/MapperCloning/WhenCloningConstructorDataSources.cs index 81800f48f..0bb40b5fc 100644 --- a/AgileMapper.UnitTests/MapperCloning/WhenCloningConstructorDataSources.cs +++ b/AgileMapper.UnitTests/MapperCloning/WhenCloningConstructorDataSources.cs @@ -2,6 +2,7 @@ { using System; using Common; + using Common.TestClasses; using TestClasses; #if !NET35 using Xunit; diff --git a/AgileMapper.UnitTests/MapperCloning/WhenCloningDataSources.cs b/AgileMapper.UnitTests/MapperCloning/WhenCloningDataSources.cs index f26f695f3..d76eb8355 100644 --- a/AgileMapper.UnitTests/MapperCloning/WhenCloningDataSources.cs +++ b/AgileMapper.UnitTests/MapperCloning/WhenCloningDataSources.cs @@ -2,6 +2,7 @@ { using AgileMapper.Configuration; using Common; + using Common.TestClasses; using TestClasses; #if !NET35 using Xunit; diff --git a/AgileMapper.UnitTests/MapperCloning/WhenCloningStringFormatting.cs b/AgileMapper.UnitTests/MapperCloning/WhenCloningStringFormatting.cs index 3937caa8c..55b821a66 100644 --- a/AgileMapper.UnitTests/MapperCloning/WhenCloningStringFormatting.cs +++ b/AgileMapper.UnitTests/MapperCloning/WhenCloningStringFormatting.cs @@ -1,6 +1,7 @@ namespace AgileObjects.AgileMapper.UnitTests.MapperCloning { using Common; + using Common.TestClasses; using TestClasses; #if !NET35 using Xunit; diff --git a/AgileMapper.UnitTests/Members/WhenCreatingTargetMembersFromExpressions.cs b/AgileMapper.UnitTests/Members/WhenCreatingTargetMembersFromExpressions.cs index 652bcbf7f..0e2561f55 100644 --- a/AgileMapper.UnitTests/Members/WhenCreatingTargetMembersFromExpressions.cs +++ b/AgileMapper.UnitTests/Members/WhenCreatingTargetMembersFromExpressions.cs @@ -6,6 +6,7 @@ using System.Linq.Expressions; using AgileMapper.Members; using Common; + using Common.TestClasses; using TestClasses; #if !NET35 using Xunit; diff --git a/AgileMapper.UnitTests/Members/WhenDeterminingRecursion.cs b/AgileMapper.UnitTests/Members/WhenDeterminingRecursion.cs index db4a234fc..98d863829 100644 --- a/AgileMapper.UnitTests/Members/WhenDeterminingRecursion.cs +++ b/AgileMapper.UnitTests/Members/WhenDeterminingRecursion.cs @@ -2,6 +2,7 @@ { using AgileMapper.Members; using Common; + using Common.TestClasses; using TestClasses; #if !NET35 using Xunit; diff --git a/AgileMapper.UnitTests/Members/WhenFindingDataSources.cs b/AgileMapper.UnitTests/Members/WhenFindingDataSources.cs index c2f20b8d1..71cc1f114 100644 --- a/AgileMapper.UnitTests/Members/WhenFindingDataSources.cs +++ b/AgileMapper.UnitTests/Members/WhenFindingDataSources.cs @@ -4,8 +4,8 @@ namespace AgileObjects.AgileMapper.UnitTests.Members using System.Linq.Expressions; using AgileMapper.Members; using Common; + using Common.TestClasses; using ObjectPopulation; - using TestClasses; #if !NET35 using Xunit; #else diff --git a/AgileMapper.UnitTests/Members/WhenFindingSourceMembers.cs b/AgileMapper.UnitTests/Members/WhenFindingSourceMembers.cs index d315fbf88..246a0ffb7 100644 --- a/AgileMapper.UnitTests/Members/WhenFindingSourceMembers.cs +++ b/AgileMapper.UnitTests/Members/WhenFindingSourceMembers.cs @@ -3,6 +3,7 @@ using System; using System.Linq; using Common; + using Common.TestClasses; using TestClasses; #if !NET35 using Xunit; diff --git a/AgileMapper.UnitTests/Members/WhenFindingTargetMembers.cs b/AgileMapper.UnitTests/Members/WhenFindingTargetMembers.cs index 6b98aac10..00608d1b0 100644 --- a/AgileMapper.UnitTests/Members/WhenFindingTargetMembers.cs +++ b/AgileMapper.UnitTests/Members/WhenFindingTargetMembers.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Linq; using Common; + using Common.TestClasses; using TestClasses; #if !NET35 using Xunit; diff --git a/AgileMapper.UnitTests/Members/WhenMatchingSourceToTargetMembers.cs b/AgileMapper.UnitTests/Members/WhenMatchingSourceToTargetMembers.cs index dc346cb9d..668865771 100644 --- a/AgileMapper.UnitTests/Members/WhenMatchingSourceToTargetMembers.cs +++ b/AgileMapper.UnitTests/Members/WhenMatchingSourceToTargetMembers.cs @@ -4,6 +4,7 @@ using System.Linq.Expressions; using AgileMapper.Members; using Common; + using Common.TestClasses; using TestClasses; #if !NET35 using Xunit; diff --git a/AgileMapper.UnitTests/SimpleTypeConversion/WhenConvertingToBools.cs b/AgileMapper.UnitTests/SimpleTypeConversion/WhenConvertingToBools.cs index 523b26343..a41118f30 100644 --- a/AgileMapper.UnitTests/SimpleTypeConversion/WhenConvertingToBools.cs +++ b/AgileMapper.UnitTests/SimpleTypeConversion/WhenConvertingToBools.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Collections.ObjectModel; using Common; + using Common.TestClasses; using TestClasses; #if !NET35 using Xunit; diff --git a/AgileMapper.UnitTests/SimpleTypeConversion/WhenConvertingToDateTimeOffsets.cs b/AgileMapper.UnitTests/SimpleTypeConversion/WhenConvertingToDateTimeOffsets.cs index dbb27bc89..0e36a3d49 100644 --- a/AgileMapper.UnitTests/SimpleTypeConversion/WhenConvertingToDateTimeOffsets.cs +++ b/AgileMapper.UnitTests/SimpleTypeConversion/WhenConvertingToDateTimeOffsets.cs @@ -2,7 +2,7 @@ { using System; using Common; - using TestClasses; + using Common.TestClasses; #if !NET35 using Xunit; #else diff --git a/AgileMapper.UnitTests/SimpleTypeConversion/WhenConvertingToDateTimes.cs b/AgileMapper.UnitTests/SimpleTypeConversion/WhenConvertingToDateTimes.cs index aeee5f028..750b316b4 100644 --- a/AgileMapper.UnitTests/SimpleTypeConversion/WhenConvertingToDateTimes.cs +++ b/AgileMapper.UnitTests/SimpleTypeConversion/WhenConvertingToDateTimes.cs @@ -2,7 +2,7 @@ { using System; using Common; - using TestClasses; + using Common.TestClasses; #if !NET35 using Xunit; #else diff --git a/AgileMapper.UnitTests/SimpleTypeConversion/WhenConvertingToFlagsEnums.cs b/AgileMapper.UnitTests/SimpleTypeConversion/WhenConvertingToFlagsEnums.cs index b88e391a0..d07c8fe91 100644 --- a/AgileMapper.UnitTests/SimpleTypeConversion/WhenConvertingToFlagsEnums.cs +++ b/AgileMapper.UnitTests/SimpleTypeConversion/WhenConvertingToFlagsEnums.cs @@ -1,6 +1,7 @@ namespace AgileObjects.AgileMapper.UnitTests.SimpleTypeConversion { using Common; + using Common.TestClasses; using TestClasses; using static TestClasses.Status; #if !NET35 diff --git a/AgileMapper.UnitTests/SimpleTypeConversion/WhenConvertingToGuids.cs b/AgileMapper.UnitTests/SimpleTypeConversion/WhenConvertingToGuids.cs index 472dd4616..088070bb4 100644 --- a/AgileMapper.UnitTests/SimpleTypeConversion/WhenConvertingToGuids.cs +++ b/AgileMapper.UnitTests/SimpleTypeConversion/WhenConvertingToGuids.cs @@ -2,6 +2,7 @@ { using System; using Common; + using Common.TestClasses; using TestClasses; #if !NET35 using Xunit; diff --git a/AgileMapper.UnitTests/SimpleTypeConversion/WhenConvertingToTimeSpans.cs b/AgileMapper.UnitTests/SimpleTypeConversion/WhenConvertingToTimeSpans.cs index 20350f587..537feeb59 100644 --- a/AgileMapper.UnitTests/SimpleTypeConversion/WhenConvertingToTimeSpans.cs +++ b/AgileMapper.UnitTests/SimpleTypeConversion/WhenConvertingToTimeSpans.cs @@ -2,7 +2,7 @@ { using System; using Common; - using TestClasses; + using Common.TestClasses; #if !NET35 using Xunit; #else diff --git a/AgileMapper.UnitTests/Structs/Configuration/WhenConfiguringStructCreationCallbacks.cs b/AgileMapper.UnitTests/Structs/Configuration/WhenConfiguringStructCreationCallbacks.cs index 6a5e66458..d717ac025 100644 --- a/AgileMapper.UnitTests/Structs/Configuration/WhenConfiguringStructCreationCallbacks.cs +++ b/AgileMapper.UnitTests/Structs/Configuration/WhenConfiguringStructCreationCallbacks.cs @@ -1,6 +1,7 @@ namespace AgileObjects.AgileMapper.UnitTests.Structs.Configuration { using Common; + using Common.TestClasses; using TestClasses; #if !NET35 using Xunit; diff --git a/AgileMapper.UnitTests/Structs/Configuration/WhenConfiguringStructDataSources.cs b/AgileMapper.UnitTests/Structs/Configuration/WhenConfiguringStructDataSources.cs index eb71e3758..4d4c0f503 100644 --- a/AgileMapper.UnitTests/Structs/Configuration/WhenConfiguringStructDataSources.cs +++ b/AgileMapper.UnitTests/Structs/Configuration/WhenConfiguringStructDataSources.cs @@ -3,6 +3,7 @@ using System; using AgileMapper.Extensions.Internal; using Common; + using Common.TestClasses; using TestClasses; #if !NET35 using Xunit; diff --git a/AgileMapper.UnitTests/Structs/Dictionaries/WhenMappingFromDictionariesToStructs.cs b/AgileMapper.UnitTests/Structs/Dictionaries/WhenMappingFromDictionariesToStructs.cs index 5129756be..ea3ee1174 100644 --- a/AgileMapper.UnitTests/Structs/Dictionaries/WhenMappingFromDictionariesToStructs.cs +++ b/AgileMapper.UnitTests/Structs/Dictionaries/WhenMappingFromDictionariesToStructs.cs @@ -4,6 +4,7 @@ using System.Collections.ObjectModel; using System.Linq; using Common; + using Common.TestClasses; using TestClasses; #if !NET35 using Xunit; diff --git a/AgileMapper.UnitTests/Structs/WhenMappingOnToStructs.cs b/AgileMapper.UnitTests/Structs/WhenMappingOnToStructs.cs index 90461a5af..8b5f231c6 100644 --- a/AgileMapper.UnitTests/Structs/WhenMappingOnToStructs.cs +++ b/AgileMapper.UnitTests/Structs/WhenMappingOnToStructs.cs @@ -1,6 +1,7 @@ namespace AgileObjects.AgileMapper.UnitTests.Structs { using Common; + using Common.TestClasses; using TestClasses; #if !NET35 using Xunit; diff --git a/AgileMapper.UnitTests/Structs/WhenMappingOverStructMembers.cs b/AgileMapper.UnitTests/Structs/WhenMappingOverStructMembers.cs index b373453c2..6bc5259c3 100644 --- a/AgileMapper.UnitTests/Structs/WhenMappingOverStructMembers.cs +++ b/AgileMapper.UnitTests/Structs/WhenMappingOverStructMembers.cs @@ -1,6 +1,7 @@ namespace AgileObjects.AgileMapper.UnitTests.Structs { using Common; + using Common.TestClasses; using TestClasses; #if !NET35 using Xunit; diff --git a/AgileMapper.UnitTests/Structs/WhenMappingOverStructs.cs b/AgileMapper.UnitTests/Structs/WhenMappingOverStructs.cs index 4d936d582..245ce6f75 100644 --- a/AgileMapper.UnitTests/Structs/WhenMappingOverStructs.cs +++ b/AgileMapper.UnitTests/Structs/WhenMappingOverStructs.cs @@ -2,6 +2,7 @@ { using System; using Common; + using Common.TestClasses; using TestClasses; #if !NET35 using Xunit; diff --git a/AgileMapper.UnitTests/Structs/WhenMappingToNewStructMembers.cs b/AgileMapper.UnitTests/Structs/WhenMappingToNewStructMembers.cs index 682a9db13..5595f10d5 100644 --- a/AgileMapper.UnitTests/Structs/WhenMappingToNewStructMembers.cs +++ b/AgileMapper.UnitTests/Structs/WhenMappingToNewStructMembers.cs @@ -1,6 +1,7 @@ namespace AgileObjects.AgileMapper.UnitTests.Structs { using Common; + using Common.TestClasses; using TestClasses; #if !NET35 using Xunit; diff --git a/AgileMapper.UnitTests/Structs/WhenMappingToNewStructs.cs b/AgileMapper.UnitTests/Structs/WhenMappingToNewStructs.cs index 3ba5524b7..f0cfb93b0 100644 --- a/AgileMapper.UnitTests/Structs/WhenMappingToNewStructs.cs +++ b/AgileMapper.UnitTests/Structs/WhenMappingToNewStructs.cs @@ -1,6 +1,7 @@ namespace AgileObjects.AgileMapper.UnitTests.Structs { using Common; + using Common.TestClasses; using TestClasses; #if !NET35 using Xunit; diff --git a/AgileMapper.UnitTests/Structs/WhenMappingToStructEnumerables.cs b/AgileMapper.UnitTests/Structs/WhenMappingToStructEnumerables.cs index f6d98d8f4..5b0c23564 100644 --- a/AgileMapper.UnitTests/Structs/WhenMappingToStructEnumerables.cs +++ b/AgileMapper.UnitTests/Structs/WhenMappingToStructEnumerables.cs @@ -4,6 +4,7 @@ using System.Collections.ObjectModel; using System.Linq; using Common; + using Common.TestClasses; using TestClasses; using static System.Decimal; #if !NET35 diff --git a/AgileMapper.UnitTests/WhenMappingCircularReferences.cs b/AgileMapper.UnitTests/WhenMappingCircularReferences.cs index 6258b92b1..e64acebed 100644 --- a/AgileMapper.UnitTests/WhenMappingCircularReferences.cs +++ b/AgileMapper.UnitTests/WhenMappingCircularReferences.cs @@ -7,6 +7,7 @@ using AgileMapper.Extensions; using AgileMapper.Extensions.Internal; using Common; + using Common.TestClasses; using TestClasses; #if !NET35 using Xunit; diff --git a/AgileMapper.UnitTests/WhenMappingDerivedTypes.cs b/AgileMapper.UnitTests/WhenMappingDerivedTypes.cs index 0f66bde67..754e2498c 100644 --- a/AgileMapper.UnitTests/WhenMappingDerivedTypes.cs +++ b/AgileMapper.UnitTests/WhenMappingDerivedTypes.cs @@ -6,6 +6,7 @@ using System.Collections.ObjectModel; using System.Linq; using Common; + using Common.TestClasses; using TestClasses; #if !NET35 using Xunit; diff --git a/AgileMapper.UnitTests/WhenMappingOnToComplexTypeMembers.cs b/AgileMapper.UnitTests/WhenMappingOnToComplexTypeMembers.cs index a1a7ba351..8627b9f83 100644 --- a/AgileMapper.UnitTests/WhenMappingOnToComplexTypeMembers.cs +++ b/AgileMapper.UnitTests/WhenMappingOnToComplexTypeMembers.cs @@ -1,6 +1,7 @@ namespace AgileObjects.AgileMapper.UnitTests { using Common; + using Common.TestClasses; using TestClasses; #if !NET35 using Xunit; diff --git a/AgileMapper.UnitTests/WhenMappingOnToComplexTypes.cs b/AgileMapper.UnitTests/WhenMappingOnToComplexTypes.cs index 336fa78e0..464d90556 100644 --- a/AgileMapper.UnitTests/WhenMappingOnToComplexTypes.cs +++ b/AgileMapper.UnitTests/WhenMappingOnToComplexTypes.cs @@ -1,6 +1,7 @@ namespace AgileObjects.AgileMapper.UnitTests { using Common; + using Common.TestClasses; using TestClasses; #if !NET35 using Xunit; diff --git a/AgileMapper.UnitTests/WhenMappingOnToEnumerableMembers.cs b/AgileMapper.UnitTests/WhenMappingOnToEnumerableMembers.cs index ba8825696..a573dce66 100644 --- a/AgileMapper.UnitTests/WhenMappingOnToEnumerableMembers.cs +++ b/AgileMapper.UnitTests/WhenMappingOnToEnumerableMembers.cs @@ -5,6 +5,7 @@ using System.Collections.ObjectModel; using System.Linq; using Common; + using Common.TestClasses; using TestClasses; #if !NET35 using Xunit; diff --git a/AgileMapper.UnitTests/WhenMappingOverComplexTypeMembers.cs b/AgileMapper.UnitTests/WhenMappingOverComplexTypeMembers.cs index aa67cf228..75c4eca0c 100644 --- a/AgileMapper.UnitTests/WhenMappingOverComplexTypeMembers.cs +++ b/AgileMapper.UnitTests/WhenMappingOverComplexTypeMembers.cs @@ -1,6 +1,7 @@ namespace AgileObjects.AgileMapper.UnitTests { using Common; + using Common.TestClasses; using TestClasses; #if !NET35 using Xunit; diff --git a/AgileMapper.UnitTests/WhenMappingOverComplexTypes.cs b/AgileMapper.UnitTests/WhenMappingOverComplexTypes.cs index 49f78de65..e6188201a 100644 --- a/AgileMapper.UnitTests/WhenMappingOverComplexTypes.cs +++ b/AgileMapper.UnitTests/WhenMappingOverComplexTypes.cs @@ -2,6 +2,7 @@ { using System; using Common; + using Common.TestClasses; using TestClasses; #if !NET35 using Xunit; diff --git a/AgileMapper.UnitTests/WhenMappingOverEnumerableMembers.cs b/AgileMapper.UnitTests/WhenMappingOverEnumerableMembers.cs index dbf896642..9ee98b8f6 100644 --- a/AgileMapper.UnitTests/WhenMappingOverEnumerableMembers.cs +++ b/AgileMapper.UnitTests/WhenMappingOverEnumerableMembers.cs @@ -5,6 +5,7 @@ using System.Collections.ObjectModel; using System.Linq; using Common; + using Common.TestClasses; using TestClasses; using static System.Decimal; #if !NET35 diff --git a/AgileMapper.UnitTests/WhenMappingOverEnumerables.cs b/AgileMapper.UnitTests/WhenMappingOverEnumerables.cs index d95c31dfd..696d32b7b 100644 --- a/AgileMapper.UnitTests/WhenMappingOverEnumerables.cs +++ b/AgileMapper.UnitTests/WhenMappingOverEnumerables.cs @@ -5,6 +5,7 @@ using System.Collections.ObjectModel; using System.Linq; using Common; + using Common.TestClasses; using TestClasses; #if !NET35 using Xunit; diff --git a/AgileMapper.UnitTests/WhenMappingToConstructors.cs b/AgileMapper.UnitTests/WhenMappingToConstructors.cs index c060b4c17..b406c2594 100644 --- a/AgileMapper.UnitTests/WhenMappingToConstructors.cs +++ b/AgileMapper.UnitTests/WhenMappingToConstructors.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; using Common; + using Common.TestClasses; using TestClasses; #if !NET35 using Xunit; diff --git a/AgileMapper.UnitTests/WhenMappingToMetaMembers.cs b/AgileMapper.UnitTests/WhenMappingToMetaMembers.cs index 8ef332a8a..abdd553f8 100644 --- a/AgileMapper.UnitTests/WhenMappingToMetaMembers.cs +++ b/AgileMapper.UnitTests/WhenMappingToMetaMembers.cs @@ -6,6 +6,7 @@ using System.Linq; using AgileMapper.Extensions; using Common; + using Common.TestClasses; using TestClasses; #if !NET35 using Xunit; diff --git a/AgileMapper.UnitTests/WhenMappingToNewComplexTypeMembers.cs b/AgileMapper.UnitTests/WhenMappingToNewComplexTypeMembers.cs index 16345d7c5..8a1a1082b 100644 --- a/AgileMapper.UnitTests/WhenMappingToNewComplexTypeMembers.cs +++ b/AgileMapper.UnitTests/WhenMappingToNewComplexTypeMembers.cs @@ -4,6 +4,7 @@ using AgileMapper.Extensions; using AgileMapper.Extensions.Internal; using Common; + using Common.TestClasses; using TestClasses; #if !NET35 using Xunit; diff --git a/AgileMapper.UnitTests/WhenMappingToNewComplexTypes.cs b/AgileMapper.UnitTests/WhenMappingToNewComplexTypes.cs index d766809df..5041029f7 100644 --- a/AgileMapper.UnitTests/WhenMappingToNewComplexTypes.cs +++ b/AgileMapper.UnitTests/WhenMappingToNewComplexTypes.cs @@ -3,6 +3,7 @@ using System; using AgileMapper.Extensions; using Common; + using Common.TestClasses; using TestClasses; #if !NET35 using Xunit; diff --git a/AgileMapper.UnitTests/WhenMappingToNewEnumerableMembers.cs b/AgileMapper.UnitTests/WhenMappingToNewEnumerableMembers.cs index 8400194bf..de947f006 100644 --- a/AgileMapper.UnitTests/WhenMappingToNewEnumerableMembers.cs +++ b/AgileMapper.UnitTests/WhenMappingToNewEnumerableMembers.cs @@ -7,6 +7,7 @@ #endif using System.Linq; using Common; + using Common.TestClasses; using TestClasses; #if !NET35 using Xunit; diff --git a/AgileMapper.UnitTests/WhenMappingToNewEnumerables.cs b/AgileMapper.UnitTests/WhenMappingToNewEnumerables.cs index 0e1a0450e..e47c2ab66 100644 --- a/AgileMapper.UnitTests/WhenMappingToNewEnumerables.cs +++ b/AgileMapper.UnitTests/WhenMappingToNewEnumerables.cs @@ -5,6 +5,7 @@ using System.Collections.ObjectModel; using System.Linq; using Common; + using Common.TestClasses; using TestClasses; #if !NET35 using Xunit; diff --git a/AgileMapper.UnitTests/WhenUnflatteningFromQueryStrings.cs b/AgileMapper.UnitTests/WhenUnflatteningFromQueryStrings.cs index 88b8d3866..b0e0abf45 100644 --- a/AgileMapper.UnitTests/WhenUnflatteningFromQueryStrings.cs +++ b/AgileMapper.UnitTests/WhenUnflatteningFromQueryStrings.cs @@ -4,6 +4,7 @@ using System.Collections.ObjectModel; using AgileMapper.Extensions; using Common; + using Common.TestClasses; using TestClasses; #if !NET35 using Xunit; diff --git a/AgileMapper.UnitTests/WhenUsingFactoryMethods.cs b/AgileMapper.UnitTests/WhenUsingFactoryMethods.cs index f6c9b2d3d..3e9fc6df1 100644 --- a/AgileMapper.UnitTests/WhenUsingFactoryMethods.cs +++ b/AgileMapper.UnitTests/WhenUsingFactoryMethods.cs @@ -2,6 +2,7 @@ { using AgileMapper.Configuration; using Common; + using Common.TestClasses; using TestClasses; #if !NET35 using Xunit; diff --git a/AgileMapper.UnitTests/WhenViewingMappingPlans.cs b/AgileMapper.UnitTests/WhenViewingMappingPlans.cs index 1de12cc7d..9889e3a83 100644 --- a/AgileMapper.UnitTests/WhenViewingMappingPlans.cs +++ b/AgileMapper.UnitTests/WhenViewingMappingPlans.cs @@ -10,6 +10,7 @@ #endif using System.Text.RegularExpressions; using Common; + using Common.TestClasses; using TestClasses; #if !NET35 using Xunit; @@ -456,9 +457,9 @@ public void ShouldShowAllCachedMappingPlanExpressions() mapper.GetPlanFor().ToANew(); mapper.GetPlansFor(new MegaProduct()).To(); - var plan = mapper.GetPlanExpressionsInCache(); + var expressions = mapper.GetPlanExpressionsInCache(); - plan.ShouldNotBeNull(); + expressions.ShouldNotBeNull().Any().ShouldBeTrue(); } } diff --git a/AgileMapper.sln b/AgileMapper.sln index d7a3d105a..affdb7fa7 100644 --- a/AgileMapper.sln +++ b/AgileMapper.sln @@ -65,6 +65,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Buildable", "Buildable", "{ EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AgileMapper.Buildable", "AgileMapper.Buildable\AgileMapper.Buildable.csproj", "{F55FFD5B-EF1F-4937-B2D2-B3324F9962D6}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AgileMapper.Buildable.UnitTests", "AgileMapper.Buildable.UnitTests\AgileMapper.Buildable.UnitTests.csproj", "{B305D680-C129-4785-B02B-10F293E19A75}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -163,6 +165,10 @@ Global {F55FFD5B-EF1F-4937-B2D2-B3324F9962D6}.Debug|Any CPU.Build.0 = Debug|Any CPU {F55FFD5B-EF1F-4937-B2D2-B3324F9962D6}.Release|Any CPU.ActiveCfg = Release|Any CPU {F55FFD5B-EF1F-4937-B2D2-B3324F9962D6}.Release|Any CPU.Build.0 = Release|Any CPU + {B305D680-C129-4785-B02B-10F293E19A75}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B305D680-C129-4785-B02B-10F293E19A75}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B305D680-C129-4785-B02B-10F293E19A75}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B305D680-C129-4785-B02B-10F293E19A75}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -192,6 +198,7 @@ Global {5085AA3F-A5B7-40E2-9309-7E0B81FBE113} = {88D48136-E176-4AF7-ACA4-A2954F3BD55B} {A4D51412-D8C0-4462-B7C9-1ABDA4AE13B2} = {88D48136-E176-4AF7-ACA4-A2954F3BD55B} {F55FFD5B-EF1F-4937-B2D2-B3324F9962D6} = {C0C3194B-D7C8-470F-8B7D-47BA75E0EDAE} + {B305D680-C129-4785-B02B-10F293E19A75} = {C0C3194B-D7C8-470F-8B7D-47BA75E0EDAE} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {C39E9BDB-0077-445C-8F09-801CAE1AF8AB} diff --git a/AgileMapper/AgileMapper.csproj b/AgileMapper/AgileMapper.csproj index 9340bc920..e46861afd 100644 --- a/AgileMapper/AgileMapper.csproj +++ b/AgileMapper/AgileMapper.csproj @@ -33,7 +33,7 @@ - + diff --git a/AgileMapper/IMapper.cs b/AgileMapper/IMapper.cs index a806d0ec8..77080fb6f 100644 --- a/AgileMapper/IMapper.cs +++ b/AgileMapper/IMapper.cs @@ -2,6 +2,7 @@ { using System; using System.Collections.Generic; + using System.Collections.ObjectModel; using System.Linq; using System.Linq.Expressions; using Api; @@ -101,7 +102,7 @@ public interface IMapper : IDisposable /// Returns mapping plan Expressions for all mapping functions currently cached by the . /// /// An Expression containing the currently-cached functions to be executed during mappings. - Expr GetPlanExpressionsInCache(); + ReadOnlyCollection GetPlanExpressionsInCache(); /// /// Configure callbacks to be executed before a particular type of event occurs for all source diff --git a/AgileMapper/Mapper.cs b/AgileMapper/Mapper.cs index 672361d64..e2d120124 100644 --- a/AgileMapper/Mapper.cs +++ b/AgileMapper/Mapper.cs @@ -2,6 +2,7 @@ { using System; using System.Collections.Generic; + using System.Collections.ObjectModel; using System.Linq; using System.Linq.Expressions; using Api; @@ -124,7 +125,7 @@ public static IProjectionPlanTargetSelector GetPlanForProjecting /// Returns mapping plan Expressions for all mapping functions currently cached by the default . /// /// An Expression containing the currently-cached functions to be executed during mappings. - public static Expr GetPlanExpressionsInCache() => Default.GetPlanExpressionsInCache(); + public static ReadOnlyCollection GetPlanExpressionsInCache() => Default.GetPlanExpressionsInCache(); /// /// Configure callbacks to be executed before a particular type of event occurs for all source @@ -248,7 +249,7 @@ IProjectionPlanTargetSelector IMapper.GetPlanForProjecting MappingPlanSet.For(Context); - Expr IMapper.GetPlanExpressionsInCache() => MappingPlanSet.For(Context); + ReadOnlyCollection IMapper.GetPlanExpressionsInCache() => MappingPlanSet.For(Context); private PlanTargetSelector GetPlan() => new PlanTargetSelector(Context); diff --git a/AgileMapper/Plans/MappingPlanSet.cs b/AgileMapper/Plans/MappingPlanSet.cs index 32696569e..06d413c90 100644 --- a/AgileMapper/Plans/MappingPlanSet.cs +++ b/AgileMapper/Plans/MappingPlanSet.cs @@ -1,6 +1,7 @@ namespace AgileObjects.AgileMapper.Plans { using System.Collections.Generic; + using System.Collections.ObjectModel; using System.Linq; using Extensions; using Extensions.Internal; @@ -50,18 +51,19 @@ public static implicit operator string(MappingPlanSet mappingPlans) } /// - /// Converts the given MappingPlanSet to its Expression - /// representation. + /// Converts the given MappingPlanSet to a collection + /// of Expressions. /// /// The to convert. /// - /// The Expression representation of the MappingPlanSet. + /// A collection of Expressions representing this MappingPlanSet. /// - public static implicit operator Expr(MappingPlanSet mappingPlans) + public static implicit operator ReadOnlyCollection(MappingPlanSet mappingPlans) { - return Expr.Block(mappingPlans + return new ReadOnlyCollection(mappingPlans ._mappingPlans - .Project(plan => plan.GetExpression())); + .Project(plan => plan.GetExpression()) + .ToList()); } /// From d18157984db08e9cb426594bddd2548cf5515c37 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Tue, 15 Sep 2020 06:33:28 +0100 Subject: [PATCH 03/65] Continued --- .../BuildableMapperExtensions.cs | 36 ++++++++++++++---- .../WhenViewingMappingPlans.cs | 2 +- .../WhenViewingProjectionPlans.cs | 2 +- .../WhenViewingMappingPlans.cs | 2 +- AgileMapper/Api/PlanTargetSelector.cs | 1 + AgileMapper/IMapper.cs | 11 ++++-- AgileMapper/IMappingContext.cs | 2 + AgileMapper/Mapper.cs | 14 ++++--- AgileMapper/MappingExecutor.cs | 2 + .../MappingExpressionFactoryBase.cs | 10 +++-- .../ObjectPopulation/ObjectMapperFactory.cs | 8 ++-- AgileMapper/Plans/IMappingPlan.cs | 20 +++++----- AgileMapper/Plans/IMappingPlanFunction.cs | 36 ++++++++++++++++-- AgileMapper/Plans/MappingPlan.cs | 15 ++++---- AgileMapper/Plans/MappingPlanSet.cs | 23 ++++++++--- .../RepeatedMappingMappingPlanFunction.cs | 38 +++++++++---------- .../Plans/RootMapperMappingPlanFunction.cs | 38 ++++++++++--------- AgileMapper/SimpleMappingContext.cs | 2 + 18 files changed, 172 insertions(+), 90 deletions(-) diff --git a/AgileMapper.Buildable/BuildableMapperExtensions.cs b/AgileMapper.Buildable/BuildableMapperExtensions.cs index bf330acc0..beddb0f14 100644 --- a/AgileMapper.Buildable/BuildableMapperExtensions.cs +++ b/AgileMapper.Buildable/BuildableMapperExtensions.cs @@ -2,9 +2,10 @@ { using System.Collections.Generic; using System.Linq; - using System.Linq.Expressions; using BuildableExpressions; using BuildableExpressions.SourceCode; + using Plans; + using ReadableExpressions.Extensions; /// /// Provides extension methods for building AgileMapper mapper source files. @@ -25,20 +26,39 @@ public static IEnumerable BuildSourceCode( this IMapper mapper) { return mapper - .GetPlanExpressionsInCache() + .GetPlansInCache() .ToMapperSourceCodeExpressions(); } /// - /// Converts these into s. + /// Converts these into s. /// - /// The mapping Expressions to convert. - /// A for each of these . + /// The containing the mapping plans to convert. + /// A for each of these . public static IEnumerable ToMapperSourceCodeExpressions( - this IEnumerable mappingExpressions) + this IEnumerable mappingPlans) { - return mappingExpressions - .Select(exp => SourceCodeFactory.SourceCode(exp)) + return mappingPlans + .Select(mappingPlan => + { + return SourceCodeFactory.SourceCode(sc => + { + foreach (var mappingFunction in mappingPlan) + { + var sourceTypeName = mappingFunction.SourceType.GetVariableNameInPascalCase(); + var targetTypeName = mappingFunction.TargetType.GetVariableNameInPascalCase(); + var className = $"{sourceTypeName}_To_{targetTypeName}_Mapper"; + + sc.WithClass(className, cls => cls + .WithMethod( + "Map", + mappingFunction.Summary, + mappingFunction.Mapping)); + } + + return sc; + }); + }) .ToList(); } } diff --git a/AgileMapper.UnitTests.NonParallel/WhenViewingMappingPlans.cs b/AgileMapper.UnitTests.NonParallel/WhenViewingMappingPlans.cs index 190e23cfa..c63383d46 100644 --- a/AgileMapper.UnitTests.NonParallel/WhenViewingMappingPlans.cs +++ b/AgileMapper.UnitTests.NonParallel/WhenViewingMappingPlans.cs @@ -28,7 +28,7 @@ public void ShouldShowAllCachedMappingPlansViaTheStaticApi() Mapper.GetPlanFor().ToANew(); Mapper.GetPlansFor(new MegaProduct()).To(); - var plan = Mapper.GetPlansInCache(); + string plan = Mapper.GetPlansInCache(); plan.ShouldContain("MysteryCustomer -> MysteryCustomerViewModel"); plan.ShouldContain("MegaProduct -> ProductDtoMega"); diff --git a/AgileMapper.UnitTests.Orms/WhenViewingProjectionPlans.cs b/AgileMapper.UnitTests.Orms/WhenViewingProjectionPlans.cs index 5ab5ef85c..b752c7ec5 100644 --- a/AgileMapper.UnitTests.Orms/WhenViewingProjectionPlans.cs +++ b/AgileMapper.UnitTests.Orms/WhenViewingProjectionPlans.cs @@ -56,7 +56,7 @@ public Task ShouldReturnCachedQueryProjectionPlansInAllCachedPlans() Mapper.GetPlanForProjecting(Context.StringItems).To(); Mapper.GetPlanForProjecting(Context.Persons).To(); - var allPlans = Mapper.GetPlansInCache(); + string allPlans = Mapper.GetPlansInCache(); allPlans.ShouldContain("IQueryable -> IQueryable"); allPlans.ShouldContain("IQueryable -> IQueryable"); diff --git a/AgileMapper.UnitTests/WhenViewingMappingPlans.cs b/AgileMapper.UnitTests/WhenViewingMappingPlans.cs index 9889e3a83..219eed3e9 100644 --- a/AgileMapper.UnitTests/WhenViewingMappingPlans.cs +++ b/AgileMapper.UnitTests/WhenViewingMappingPlans.cs @@ -435,7 +435,7 @@ public void ShouldShowAllCachedMappingPlans() mapper.GetPlanFor().ToANew(); mapper.GetPlansFor(new MegaProduct()).To(); - var plan = mapper.GetPlansInCache(); + string plan = mapper.GetPlansInCache(); plan.ShouldContain("PublicField -> PublicProperty"); plan.ShouldContain("Customer -> CustomerViewModel"); diff --git a/AgileMapper/Api/PlanTargetSelector.cs b/AgileMapper/Api/PlanTargetSelector.cs index 18b304b73..071de2ead 100644 --- a/AgileMapper/Api/PlanTargetSelector.cs +++ b/AgileMapper/Api/PlanTargetSelector.cs @@ -70,6 +70,7 @@ private MappingPlan GetMappingPlan( { var planContext = new SimpleMappingContext(ruleSet, _mapperContext) { + IncludeCodeComments = true, IgnoreUnsuccessfulMemberPopulations = false }; diff --git a/AgileMapper/IMapper.cs b/AgileMapper/IMapper.cs index 77080fb6f..69f0f2162 100644 --- a/AgileMapper/IMapper.cs +++ b/AgileMapper/IMapper.cs @@ -7,6 +7,7 @@ using System.Linq.Expressions; using Api; using Api.Configuration; + using Plans; using Queryables.Api; #if NET35 using Expr = Microsoft.Scripting.Ast.Expression; @@ -93,10 +94,14 @@ public interface IMapper : IDisposable IPlanTargetSelector GetPlansFor(); /// - /// Returns mapping plans for all mapping functions currently cached by the . + /// Returns a containing plans for all mapping functions currently + /// cached by the . /// - /// A string containing the currently-cached functions to be executed during mappings. - string GetPlansInCache(); + /// + /// A containing the currently-cached functions to be executed + /// during mappings. + /// + MappingPlanSet GetPlansInCache(); /// /// Returns mapping plan Expressions for all mapping functions currently cached by the . diff --git a/AgileMapper/IMappingContext.cs b/AgileMapper/IMappingContext.cs index 065f5a294..ece919728 100644 --- a/AgileMapper/IMappingContext.cs +++ b/AgileMapper/IMappingContext.cs @@ -2,6 +2,8 @@ { internal interface IMappingContext : IMapperContextOwner, IRuleSetOwner { + bool IncludeCodeComments { get; } + bool IgnoreUnsuccessfulMemberPopulations { get; } bool LazyLoadRepeatMappingFuncs { get; } diff --git a/AgileMapper/Mapper.cs b/AgileMapper/Mapper.cs index e2d120124..eb715f8bd 100644 --- a/AgileMapper/Mapper.cs +++ b/AgileMapper/Mapper.cs @@ -116,10 +116,14 @@ public static IProjectionPlanTargetSelector GetPlanForProjecting } /// - /// Returns mapping plans for all mapping functions currently cached by the default . + /// Returns a containing plans for all mapping functions currently + /// cached by the default . /// - /// A string containing the currently-cached functions to be executed during mappings. - public static string GetPlansInCache() => Default.GetPlansInCache(); + /// + /// A containing the currently-cached functions to be executed + /// during mappings. + /// + public static MappingPlanSet GetPlansInCache() => Default.GetPlansInCache(); /// /// Returns mapping plan Expressions for all mapping functions currently cached by the default . @@ -247,8 +251,8 @@ IProjectionPlanTargetSelector IMapper.GetPlanForProjecting IMapper.GetPlansFor() => GetPlan(); - string IMapper.GetPlansInCache() => MappingPlanSet.For(Context); - + MappingPlanSet IMapper.GetPlansInCache() => MappingPlanSet.For(Context); + ReadOnlyCollection IMapper.GetPlanExpressionsInCache() => MappingPlanSet.For(Context); private PlanTargetSelector GetPlan() diff --git a/AgileMapper/MappingExecutor.cs b/AgileMapper/MappingExecutor.cs index 512dda1c0..8c6fd8da5 100644 --- a/AgileMapper/MappingExecutor.cs +++ b/AgileMapper/MappingExecutor.cs @@ -30,6 +30,8 @@ public MappingExecutor(TSource source, MapperContext mapperContext) public MappingRuleSet RuleSet { get; private set; } + public bool IncludeCodeComments => false; + public bool IgnoreUnsuccessfulMemberPopulations => true; public bool LazyLoadRepeatMappingFuncs => true; diff --git a/AgileMapper/ObjectPopulation/MappingExpressionFactoryBase.cs b/AgileMapper/ObjectPopulation/MappingExpressionFactoryBase.cs index 1428322e8..b568303e7 100644 --- a/AgileMapper/ObjectPopulation/MappingExpressionFactoryBase.cs +++ b/AgileMapper/ObjectPopulation/MappingExpressionFactoryBase.cs @@ -14,13 +14,13 @@ namespace AgileObjects.AgileMapper.ObjectPopulation using Extensions.Internal; using Members; using Members.MemberExtensions; - using ReadableExpressions; using ReadableExpressions.Extensions; #if NET35 using static Microsoft.Scripting.Ast.ExpressionType; #else using static System.Linq.Expressions.ExpressionType; #endif + using static ReadableExpressions.ReadableExpression; internal abstract class MappingExpressionFactoryBase { @@ -30,9 +30,11 @@ public Expression Create(IObjectMappingData mappingData) if (TargetCannotBeMapped(mappingData, out var reason)) { - return Expression.Block( - ReadableExpression.Comment(reason), - GetNullMappingFallbackValue(mapperData)); + var fallbackValue = GetNullMappingFallbackValue(mapperData); + + return mappingData.MappingContext.IncludeCodeComments + ? Expression.Block(Comment(reason), fallbackValue) + : fallbackValue; } var context = new MappingCreationContext(mappingData); diff --git a/AgileMapper/ObjectPopulation/ObjectMapperFactory.cs b/AgileMapper/ObjectPopulation/ObjectMapperFactory.cs index 341642854..b64558410 100644 --- a/AgileMapper/ObjectPopulation/ObjectMapperFactory.cs +++ b/AgileMapper/ObjectPopulation/ObjectMapperFactory.cs @@ -21,11 +21,9 @@ public ObjectMapperFactory(CacheSet mapperScopedCacheSet) public void RegisterCreationCallback(MapperCreationCallbackKey creationCallbackKey, Action callback) { - if (_creationCallbacksByKey == null) - { - _creationCallbacksByKey = - new ExpandableSimpleDictionary>(3, default(MapperCreationCallbackKey.Comparer)); - } + _creationCallbacksByKey ??= + new ExpandableSimpleDictionary>(3, + default(MapperCreationCallbackKey.Comparer)); _creationCallbacksByKey.Add(creationCallbackKey, callback); } diff --git a/AgileMapper/Plans/IMappingPlan.cs b/AgileMapper/Plans/IMappingPlan.cs index 7e15ca6e6..7fcbeea03 100644 --- a/AgileMapper/Plans/IMappingPlan.cs +++ b/AgileMapper/Plans/IMappingPlan.cs @@ -1,15 +1,17 @@ namespace AgileObjects.AgileMapper.Plans { -#if NET35 - using Microsoft.Scripting.Ast; -#else - using System.Linq.Expressions; -#endif + using System.Collections.Generic; - internal interface IMappingPlan + /// + /// Implementing classes will describe a plan for mapping from one type to another with a + /// particular rule set. + /// + public interface IMappingPlan : IEnumerable { - string GetDescription(); - - Expression GetExpression(); + /// + /// Gets a source-code string translation of this . + /// + /// A source-code string translation of this . + string ToSourceCode(); } } \ No newline at end of file diff --git a/AgileMapper/Plans/IMappingPlanFunction.cs b/AgileMapper/Plans/IMappingPlanFunction.cs index 00d5d7976..9bd0950f2 100644 --- a/AgileMapper/Plans/IMappingPlanFunction.cs +++ b/AgileMapper/Plans/IMappingPlanFunction.cs @@ -1,15 +1,45 @@ namespace AgileObjects.AgileMapper.Plans { + using System; #if NET35 using Microsoft.Scripting.Ast; #else using System.Linq.Expressions; #endif + using ReadableExpressions; - internal interface IMappingPlanFunction + /// + /// Implementing classes will describe a mapping function used to map from one type to another + /// with a particular rule set. + /// + public interface IMappingPlanFunction { - Expression GetExpression(); + /// + /// Gets the source type from which this will perform a + /// mapping. + /// + Type SourceType { get; } - string GetDescription(); + /// + /// Gets the target type to which this will perform a + /// mapping. + /// + Type TargetType { get; } + + /// + /// Gets an Expression summarising the . + /// + CommentExpression Summary { get; } + + /// + /// Gets an Expression describing the 's mapping. + /// + Expression Mapping { get; } + + /// + /// Gets a source-code string translation of this . + /// + /// A source-code string translation of this . + string ToSourceCode(); } } \ No newline at end of file diff --git a/AgileMapper/Plans/MappingPlan.cs b/AgileMapper/Plans/MappingPlan.cs index 76d412418..12395c4de 100644 --- a/AgileMapper/Plans/MappingPlan.cs +++ b/AgileMapper/Plans/MappingPlan.cs @@ -1,7 +1,9 @@ namespace AgileObjects.AgileMapper.Plans { using System; + using System.Collections; using System.Collections.Generic; + using System.Linq; #if NET35 using Microsoft.Scripting.Ast; #else @@ -46,7 +48,7 @@ public static implicit operator string(MappingPlan mappingPlan) { return mappingPlan ._mappingPlanFunctions - .ProjectToArray(pd => pd.GetDescription()) + .ProjectToArray(pf => pf.ToSourceCode()) .Join(Environment.NewLine + Environment.NewLine); } @@ -59,16 +61,15 @@ public static implicit operator Expression(MappingPlan mappingPlan) { return Expression.Block(mappingPlan ._mappingPlanFunctions - .ProjectToArray(pd => pd.GetExpression())); + .SelectMany(mpf => new[] { mpf.Summary, mpf.Mapping })); } - #region IMappingPlan Members + string IMappingPlan.ToSourceCode() => this; - string IMappingPlan.GetDescription() => this; - - Expression IMappingPlan.GetExpression() => this; + IEnumerator IEnumerable.GetEnumerator() => _mappingPlanFunctions.GetEnumerator(); - #endregion + IEnumerator IEnumerable.GetEnumerator() + => _mappingPlanFunctions.GetEnumerator(); /// /// Returns the string representation of the . diff --git a/AgileMapper/Plans/MappingPlanSet.cs b/AgileMapper/Plans/MappingPlanSet.cs index 06d413c90..63551723d 100644 --- a/AgileMapper/Plans/MappingPlanSet.cs +++ b/AgileMapper/Plans/MappingPlanSet.cs @@ -1,5 +1,6 @@ namespace AgileObjects.AgileMapper.Plans { + using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; @@ -16,7 +17,7 @@ /// Contains sets of details of mapping plans for mappings between a particular source and target types, /// for particular mapping types (create new, merge, overwrite). /// - public class MappingPlanSet + public class MappingPlanSet : IEnumerable { private readonly IEnumerable _mappingPlans; @@ -45,8 +46,7 @@ internal static MappingPlanSet For(MapperContext mapperContext) public static implicit operator string(MappingPlanSet mappingPlans) { return mappingPlans - ._mappingPlans - .Project(plan => plan.GetDescription()) + .Project(plan => plan.ToSourceCode()) .Join(NewLine + NewLine); } @@ -61,11 +61,24 @@ public static implicit operator string(MappingPlanSet mappingPlans) public static implicit operator ReadOnlyCollection(MappingPlanSet mappingPlans) { return new ReadOnlyCollection(mappingPlans - ._mappingPlans - .Project(plan => plan.GetExpression()) + .Select(mp => + { + var functionBlocks = mp + .Select(mpf => (Expr)Expr.Block(mpf.Summary, mpf.Mapping)) + .ToList(); + + return functionBlocks.HasOne() + ? functionBlocks.First() + : Expr.Block(functionBlocks); + }) .ToList()); } + IEnumerator IEnumerable.GetEnumerator() => _mappingPlans.GetEnumerator(); + + IEnumerator IEnumerable.GetEnumerator() + => _mappingPlans.GetEnumerator(); + /// /// Returns the string representation of the . /// diff --git a/AgileMapper/Plans/RepeatedMappingMappingPlanFunction.cs b/AgileMapper/Plans/RepeatedMappingMappingPlanFunction.cs index a8237825d..1963add8b 100644 --- a/AgileMapper/Plans/RepeatedMappingMappingPlanFunction.cs +++ b/AgileMapper/Plans/RepeatedMappingMappingPlanFunction.cs @@ -12,44 +12,42 @@ internal class RepeatedMappingMappingPlanFunction : IMappingPlanFunction { - private readonly Type _sourceType; - private readonly Type _targetType; - private readonly Expression _mapping; + private CommentExpression _summary; public RepeatedMappingMappingPlanFunction(IRepeatedMapperFunc mapperFunc) { - _sourceType = mapperFunc.SourceType; - _targetType = mapperFunc.TargetType; - _mapping = mapperFunc.Mapping; + SourceType = mapperFunc.SourceType; + TargetType = mapperFunc.TargetType; + Mapping = mapperFunc.Mapping; } - public Expression GetExpression() - { - var description = GetMappingDescription(); - - return Expression.Block( - ReadableExpression.Comment(description), - _mapping); - } + public Type SourceType { get; } - public string GetDescription() - { - var description = GetMappingDescription(linePrefix: "// "); + public Type TargetType { get; } - return description + _mapping.ToReadableString(); - } + public CommentExpression Summary + => _summary ??= ReadableExpression.Comment(GetMappingDescription()); private string GetMappingDescription(string linePrefix = null) { return $@" {linePrefix}- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - {linePrefix}- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -{linePrefix}Map {_sourceType.GetFriendlyName()} -> {_targetType.GetFriendlyName()} +{linePrefix}Map {SourceType.GetFriendlyName()} -> {TargetType.GetFriendlyName()} {linePrefix}Repeated Mapping Mapper {linePrefix}- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - {linePrefix}- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - "; } + + public Expression Mapping { get; } + + public string ToSourceCode() + { + var description = GetMappingDescription(linePrefix: "// "); + + return description + Mapping.ToReadableString(); + } } } \ No newline at end of file diff --git a/AgileMapper/Plans/RootMapperMappingPlanFunction.cs b/AgileMapper/Plans/RootMapperMappingPlanFunction.cs index 6159b6dfd..d9c420fd5 100644 --- a/AgileMapper/Plans/RootMapperMappingPlanFunction.cs +++ b/AgileMapper/Plans/RootMapperMappingPlanFunction.cs @@ -1,5 +1,6 @@ namespace AgileObjects.AgileMapper.Plans { + using System; #if NET35 using Microsoft.Scripting.Ast; #else @@ -14,6 +15,8 @@ internal class RootMapperMappingPlanFunction : IMappingPlanFunction { private readonly ObjectMapperData _mapperData; private readonly Expression _mapping; + private CommentExpression _summary; + private Expression _finalMapping; public RootMapperMappingPlanFunction(IObjectMapper mapper) { @@ -21,33 +24,22 @@ public RootMapperMappingPlanFunction(IObjectMapper mapper) _mapping = mapper.GetMappingLambda(); } - public Expression GetExpression() - { - var description = GetMappingDescription(); - var mapping = GetFinalMappingExpression(); - - return Expression.Block( - ReadableExpression.Comment(description), - mapping); - } + public Type SourceType => _mapperData.SourceType; - public string GetDescription() - { - var description = GetMappingDescription(linePrefix: "// "); - var mapping = GetFinalMappingExpression(); + public Type TargetType => _mapperData.TargetType; - return description + mapping.ToReadableString(); - } + public CommentExpression Summary + => _summary ??= ReadableExpression.Comment(GetMappingDescription()); private string GetMappingDescription(string linePrefix = null) { - var sourceType = _mapperData.SourceType.GetFriendlyName(); - var targetType = _mapperData.TargetType.GetFriendlyName(); + var sourceTypeName = SourceType.GetFriendlyName(); + var targetTypeName = TargetType.GetFriendlyName(); return $@" {linePrefix}- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - {linePrefix}- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -{linePrefix}Map {sourceType} -> {targetType} +{linePrefix}Map {sourceTypeName} -> {targetTypeName} {linePrefix}Rule Set: {_mapperData.RuleSet.Name} {linePrefix}- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - {linePrefix}- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -55,6 +47,16 @@ private string GetMappingDescription(string linePrefix = null) "; } + public Expression Mapping + => _finalMapping ??= GetFinalMappingExpression(); + + public string ToSourceCode() + { + var description = GetMappingDescription(linePrefix: "// "); + + return description + Mapping.ToReadableString(); + } + private Expression GetFinalMappingExpression() { var mappingWithEnumMismatches = EnumMappingMismatchFinder.Process(_mapping, _mapperData); diff --git a/AgileMapper/SimpleMappingContext.cs b/AgileMapper/SimpleMappingContext.cs index fc045a455..60919016e 100644 --- a/AgileMapper/SimpleMappingContext.cs +++ b/AgileMapper/SimpleMappingContext.cs @@ -13,6 +13,8 @@ public SimpleMappingContext(MappingRuleSet ruleSet, MapperContext mapperContext) public MappingRuleSet RuleSet { get; } + public bool IncludeCodeComments { get; set; } + public bool IgnoreUnsuccessfulMemberPopulations { get; set; } public bool LazyLoadRepeatMappingFuncs => false; From 6d3ffcc5695ac41ea15dc43677d78d25822a7b60 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Fri, 9 Oct 2020 08:15:50 +0100 Subject: [PATCH 04/65] Removing EnumerableIndex / Surfacing mapping Expressions as LambdaExpressions --- AgileMapper/Members/IMappingData.cs | 22 +------------------ AgileMapper/Members/IMappingExceptionData.cs | 7 ------ AgileMapper/Members/MappingInstanceData.cs | 4 ---- .../ObjectPopulation/IObjectMapperFunc.cs | 9 +++++--- .../ObjectMappingExceptionData.cs | 2 -- .../RepeatedMappings/IRepeatedMapperFunc.cs | 9 +++++++- .../RepeatedMappings/RepeatedMapperFunc.cs | 2 +- AgileMapper/Plans/IMappingPlan.cs | 6 +++++ AgileMapper/Plans/IMappingPlanFunction.cs | 2 +- AgileMapper/Plans/MappingPlan.cs | 12 +++++----- .../RepeatedMappingMappingPlanFunction.cs | 15 +++++-------- .../Plans/RootMapperMappingPlanFunction.cs | 12 ++++------ .../Validation/EnumMappingMismatchFinder.cs | 2 +- 13 files changed, 40 insertions(+), 64 deletions(-) diff --git a/AgileMapper/Members/IMappingData.cs b/AgileMapper/Members/IMappingData.cs index bf2dade70..603bee32f 100644 --- a/AgileMapper/Members/IMappingData.cs +++ b/AgileMapper/Members/IMappingData.cs @@ -1,7 +1,5 @@ namespace AgileObjects.AgileMapper.Members { - using System; - /// /// Provides the data being used at a particular point during a mapping. /// @@ -29,17 +27,6 @@ public interface IMappingData /// The target object for the mapping context. TTarget GetTarget(); - /// - /// Gets the index of the current enumerable element being mapped in the mapping context - /// described by this , if applicable. - /// - /// - /// The index of the current enumerable element being mapped in the mapping context described - /// by this , otherwise null. - /// - [Obsolete("Use GetElementIndex() instead. This method will be removed in a future release.")] - int? GetEnumerableIndex(); - /// /// Gets the index of the current enumerable element being mapped in the mapping context /// described by this , if applicable. @@ -95,19 +82,12 @@ public interface IMappingData : IServiceProviderAccess /// TTarget Target { get; } - /// - /// Gets the index of the current enumerable element being mapped in the mapping context - /// described by this , if applicable. - /// - [Obsolete("Use ElementIndex instead. This property will be removed in a future release.")] - int? EnumerableIndex { get; } - /// /// Gets the index of the current enumerable element being mapped in the mapping context /// described by this , if applicable. /// int? ElementIndex { get; } - + /// /// Gets the key of the current Dictionary KeyValuePair being mapped in the mapping context /// described by this , if applicable. diff --git a/AgileMapper/Members/IMappingExceptionData.cs b/AgileMapper/Members/IMappingExceptionData.cs index 1eb81cfe3..e686351f3 100644 --- a/AgileMapper/Members/IMappingExceptionData.cs +++ b/AgileMapper/Members/IMappingExceptionData.cs @@ -20,13 +20,6 @@ public interface IMappingExceptionData : IServiceProviderAccessor /// object Target { get; } - /// - /// Gets the index of the current enumerable element being mapped when the mapping Exception - /// occurred, if applicable. - /// - [Obsolete("Use ElementIndex instead. This property will be removed in a future release.")] - int? EnumerableIndex { get; } - /// /// Gets the index of the current enumerable element being mapped when the mapping Exception /// occurred, if applicable. diff --git a/AgileMapper/Members/MappingInstanceData.cs b/AgileMapper/Members/MappingInstanceData.cs index abe5939d9..7ef64d180 100644 --- a/AgileMapper/Members/MappingInstanceData.cs +++ b/AgileMapper/Members/MappingInstanceData.cs @@ -42,8 +42,6 @@ protected MappingInstanceData( public TTarget Target { get; set; } - int? IMappingData.EnumerableIndex => ElementIndex; - public int? ElementIndex { get; } public object ElementKey { get; } @@ -68,8 +66,6 @@ T IMappingData.GetTarget() return default(T); } - int? IMappingData.GetEnumerableIndex() => GetElementIndex(); - public int? GetElementIndex() => ElementIndex ?? _parent?.GetElementIndex(); public object GetElementKey() => ElementKey ?? _parent?.GetElementKey(); diff --git a/AgileMapper/ObjectPopulation/IObjectMapperFunc.cs b/AgileMapper/ObjectPopulation/IObjectMapperFunc.cs index 6044fe97b..3ad8de250 100644 --- a/AgileMapper/ObjectPopulation/IObjectMapperFunc.cs +++ b/AgileMapper/ObjectPopulation/IObjectMapperFunc.cs @@ -6,10 +6,13 @@ namespace AgileObjects.AgileMapper.ObjectPopulation using System.Linq.Expressions; #endif - internal interface IObjectMapperFunc + internal interface IObjectMapperFuncBase { - Expression Mapping { get; } - object Map(IObjectMappingData mappingData); } + + internal interface IObjectMapperFunc : IObjectMapperFuncBase + { + Expression Mapping { get; } + } } \ No newline at end of file diff --git a/AgileMapper/ObjectPopulation/ObjectMappingExceptionData.cs b/AgileMapper/ObjectPopulation/ObjectMappingExceptionData.cs index d3ca54174..b95962262 100644 --- a/AgileMapper/ObjectPopulation/ObjectMappingExceptionData.cs +++ b/AgileMapper/ObjectPopulation/ObjectMappingExceptionData.cs @@ -32,7 +32,5 @@ public ObjectMappingExceptionData(IMappingData mappingData, Ex object IMappingExceptionData.Target => Target; public Exception Exception { get; } - - int? IMappingExceptionData.EnumerableIndex => ElementIndex; } } \ No newline at end of file diff --git a/AgileMapper/ObjectPopulation/RepeatedMappings/IRepeatedMapperFunc.cs b/AgileMapper/ObjectPopulation/RepeatedMappings/IRepeatedMapperFunc.cs index bacd814f4..abb0ce2a0 100644 --- a/AgileMapper/ObjectPopulation/RepeatedMappings/IRepeatedMapperFunc.cs +++ b/AgileMapper/ObjectPopulation/RepeatedMappings/IRepeatedMapperFunc.cs @@ -1,12 +1,19 @@ namespace AgileObjects.AgileMapper.ObjectPopulation.RepeatedMappings { using System; +#if NET35 + using Microsoft.Scripting.Ast; +#else + using System.Linq.Expressions; +#endif - internal interface IRepeatedMapperFunc : IObjectMapperFunc + internal interface IRepeatedMapperFunc : IObjectMapperFuncBase { Type SourceType { get; } Type TargetType { get; } + LambdaExpression Mapping { get; } + } } \ No newline at end of file diff --git a/AgileMapper/ObjectPopulation/RepeatedMappings/RepeatedMapperFunc.cs b/AgileMapper/ObjectPopulation/RepeatedMappings/RepeatedMapperFunc.cs index 939133cf4..88499c115 100644 --- a/AgileMapper/ObjectPopulation/RepeatedMappings/RepeatedMapperFunc.cs +++ b/AgileMapper/ObjectPopulation/RepeatedMappings/RepeatedMapperFunc.cs @@ -27,7 +27,7 @@ public RepeatedMapperFunc(IObjectMappingData mappingData, bool lazyLoadFuncs) CreateMapperFunc(mappingData); } - public Expression Mapping { get; private set; } + public LambdaExpression Mapping { get; private set; } public Type SourceType => typeof(TChildSource); diff --git a/AgileMapper/Plans/IMappingPlan.cs b/AgileMapper/Plans/IMappingPlan.cs index 7fcbeea03..8cf6d1d03 100644 --- a/AgileMapper/Plans/IMappingPlan.cs +++ b/AgileMapper/Plans/IMappingPlan.cs @@ -8,6 +8,12 @@ /// public interface IMappingPlan : IEnumerable { + /// + /// Gets the root describing the plan to map the root source + /// and target objects. + /// + IMappingPlanFunction Root { get; } + /// /// Gets a source-code string translation of this . /// diff --git a/AgileMapper/Plans/IMappingPlanFunction.cs b/AgileMapper/Plans/IMappingPlanFunction.cs index 9bd0950f2..f74f27175 100644 --- a/AgileMapper/Plans/IMappingPlanFunction.cs +++ b/AgileMapper/Plans/IMappingPlanFunction.cs @@ -34,7 +34,7 @@ public interface IMappingPlanFunction /// /// Gets an Expression describing the 's mapping. /// - Expression Mapping { get; } + LambdaExpression Mapping { get; } /// /// Gets a source-code string translation of this . diff --git a/AgileMapper/Plans/MappingPlan.cs b/AgileMapper/Plans/MappingPlan.cs index 12395c4de..6e65615f2 100644 --- a/AgileMapper/Plans/MappingPlan.cs +++ b/AgileMapper/Plans/MappingPlan.cs @@ -23,10 +23,9 @@ public class MappingPlan : IMappingPlan internal MappingPlan(IObjectMapper cachedMapper) { - _mappingPlanFunctions = new List - { - new RootMapperMappingPlanFunction(cachedMapper) - }; + Root = new RootMapperMappingPlanFunction(cachedMapper); + + _mappingPlanFunctions = new List { Root }; if (cachedMapper.MapperData.HasRepeatedMapperFuncs) { @@ -61,9 +60,12 @@ public static implicit operator Expression(MappingPlan mappingPlan) { return Expression.Block(mappingPlan ._mappingPlanFunctions - .SelectMany(mpf => new[] { mpf.Summary, mpf.Mapping })); + .SelectMany(mpf => new Expression[] { mpf.Summary, mpf.Mapping })); } + /// + public IMappingPlanFunction Root { get; } + string IMappingPlan.ToSourceCode() => this; IEnumerator IEnumerable.GetEnumerator() => _mappingPlanFunctions.GetEnumerator(); diff --git a/AgileMapper/Plans/RepeatedMappingMappingPlanFunction.cs b/AgileMapper/Plans/RepeatedMappingMappingPlanFunction.cs index 1963add8b..a09a6df92 100644 --- a/AgileMapper/Plans/RepeatedMappingMappingPlanFunction.cs +++ b/AgileMapper/Plans/RepeatedMappingMappingPlanFunction.cs @@ -12,18 +12,17 @@ internal class RepeatedMappingMappingPlanFunction : IMappingPlanFunction { + private readonly IRepeatedMapperFunc _mapperFunc; private CommentExpression _summary; public RepeatedMappingMappingPlanFunction(IRepeatedMapperFunc mapperFunc) { - SourceType = mapperFunc.SourceType; - TargetType = mapperFunc.TargetType; - Mapping = mapperFunc.Mapping; + _mapperFunc = mapperFunc; } - public Type SourceType { get; } + public Type SourceType => _mapperFunc.SourceType; - public Type TargetType { get; } + public Type TargetType => _mapperFunc.TargetType; public CommentExpression Summary => _summary ??= ReadableExpression.Comment(GetMappingDescription()); @@ -31,17 +30,13 @@ public CommentExpression Summary private string GetMappingDescription(string linePrefix = null) { return $@" -{linePrefix}- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -{linePrefix}- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - {linePrefix}Map {SourceType.GetFriendlyName()} -> {TargetType.GetFriendlyName()} {linePrefix}Repeated Mapping Mapper -{linePrefix}- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -{linePrefix}- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - "; } - public Expression Mapping { get; } + public LambdaExpression Mapping => _mapperFunc.Mapping; public string ToSourceCode() { diff --git a/AgileMapper/Plans/RootMapperMappingPlanFunction.cs b/AgileMapper/Plans/RootMapperMappingPlanFunction.cs index d9c420fd5..47addaa2a 100644 --- a/AgileMapper/Plans/RootMapperMappingPlanFunction.cs +++ b/AgileMapper/Plans/RootMapperMappingPlanFunction.cs @@ -14,9 +14,9 @@ internal class RootMapperMappingPlanFunction : IMappingPlanFunction { private readonly ObjectMapperData _mapperData; - private readonly Expression _mapping; + private readonly LambdaExpression _mapping; private CommentExpression _summary; - private Expression _finalMapping; + private LambdaExpression _finalMapping; public RootMapperMappingPlanFunction(IObjectMapper mapper) { @@ -37,17 +37,13 @@ private string GetMappingDescription(string linePrefix = null) var targetTypeName = TargetType.GetFriendlyName(); return $@" -{linePrefix}- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -{linePrefix}- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - {linePrefix}Map {sourceTypeName} -> {targetTypeName} {linePrefix}Rule Set: {_mapperData.RuleSet.Name} -{linePrefix}- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -{linePrefix}- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - "; } - public Expression Mapping + public LambdaExpression Mapping => _finalMapping ??= GetFinalMappingExpression(); public string ToSourceCode() @@ -57,7 +53,7 @@ public string ToSourceCode() return description + Mapping.ToReadableString(); } - private Expression GetFinalMappingExpression() + private LambdaExpression GetFinalMappingExpression() { var mappingWithEnumMismatches = EnumMappingMismatchFinder.Process(_mapping, _mapperData); diff --git a/AgileMapper/Validation/EnumMappingMismatchFinder.cs b/AgileMapper/Validation/EnumMappingMismatchFinder.cs index 668de530b..e0c5683e0 100644 --- a/AgileMapper/Validation/EnumMappingMismatchFinder.cs +++ b/AgileMapper/Validation/EnumMappingMismatchFinder.cs @@ -51,7 +51,7 @@ public static ICollection FindMismatches(ObjectMapperDat return mismatchSets; } - public static Expression Process(Expression mapping, ObjectMapperData mapperData) + public static LambdaExpression Process(LambdaExpression mapping, ObjectMapperData mapperData) { var targetMemberDatas = GetAllTargetMemberDatas(mapperData); From a30cd89b9520895e05996121237c630a12379b72 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Mon, 12 Oct 2020 15:51:03 +0100 Subject: [PATCH 05/65] Tidying --- .../ObjectPopulation/MappingDataFactory.cs | 4 +- .../ObjectPopulation/MemberMapperDataBase.cs | 42 ++++++++++--------- 2 files changed, 25 insertions(+), 21 deletions(-) diff --git a/AgileMapper/ObjectPopulation/MappingDataFactory.cs b/AgileMapper/ObjectPopulation/MappingDataFactory.cs index b940fcfef..99d326bc5 100644 --- a/AgileMapper/ObjectPopulation/MappingDataFactory.cs +++ b/AgileMapper/ObjectPopulation/MappingDataFactory.cs @@ -12,10 +12,10 @@ internal static class MappingDataFactory private static MethodInfo _forElementMethod; public static MethodInfo ForChildMethod => - _forChildMethod ?? (_forChildMethod = typeof(MappingDataFactory).GetPublicStaticMethod(nameof(ForChild))); + _forChildMethod ??= typeof(MappingDataFactory).GetPublicStaticMethod(nameof(ForChild)); public static MethodInfo ForElementMethod - => _forElementMethod ?? (_forElementMethod = typeof(MappingDataFactory).GetPublicStaticMethod(nameof(ForElement))); + => _forElementMethod ??= typeof(MappingDataFactory).GetPublicStaticMethod(nameof(ForElement)); public static ObjectMappingData ForChild( TSource source, diff --git a/AgileMapper/ObjectPopulation/MemberMapperDataBase.cs b/AgileMapper/ObjectPopulation/MemberMapperDataBase.cs index 561255bf3..5bdf3d445 100644 --- a/AgileMapper/ObjectPopulation/MemberMapperDataBase.cs +++ b/AgileMapper/ObjectPopulation/MemberMapperDataBase.cs @@ -35,15 +35,9 @@ protected MemberMapperDataBase( TargetObject = GetMappingDataProperty(Member.RootTargetMemberName); } - public ObjectMapperData Parent { get; } - - public ParameterExpression MappingDataObject { get; } - - public Expression SourceObject { get; set; } + #region Setup - public Expression TargetObject { get; set; } - - protected ParameterExpression CreateMappingDataObject() + private ParameterExpression CreateMappingDataObject() { var mdType = typeof(IObjectMappingData<,>).MakeGenericType(SourceType, TargetType); @@ -70,7 +64,27 @@ protected ParameterExpression CreateMappingDataObject() return Expression.Parameter(mdType, mappingDataVariableName); } - protected Type MappingDataType { get; } + private Expression GetMappingDataProperty(Type mappingDataType, string propertyName) + { + var property = mappingDataType.GetPublicInstanceProperty(propertyName); + + return Expression.Property(MappingDataObject, property); + } + + protected Expression GetMappingDataProperty(string propertyName) + => Expression.Property(MappingDataObject, propertyName); + + #endregion + + public ObjectMapperData Parent { get; } + + public ParameterExpression MappingDataObject { get; } + + public Expression SourceObject { get; set; } + + public Expression TargetObject { get; set; } + + private Type MappingDataType { get; } protected Expression GetElementIndexAccess() => GetMappingDataProperty(MappingDataType, "ElementIndex"); @@ -80,15 +94,5 @@ protected Expression GetElementKeyAccess() protected Expression GetParentObjectAccess() => GetMappingDataProperty(nameof(Parent)); - - protected Expression GetMappingDataProperty(Type mappingDataType, string propertyName) - { - var property = mappingDataType.GetPublicInstanceProperty(propertyName); - - return Expression.Property(MappingDataObject, property); - } - - protected Expression GetMappingDataProperty(string propertyName) - => Expression.Property(MappingDataObject, propertyName); } } \ No newline at end of file From 85131220ba5fc7cb60f21969df728acb7d63b927 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Mon, 12 Oct 2020 15:51:24 +0100 Subject: [PATCH 06/65] Building mapper source code continued --- .../BuildableMapperExtensions.cs | 31 ++++++++++--------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/AgileMapper.Buildable/BuildableMapperExtensions.cs b/AgileMapper.Buildable/BuildableMapperExtensions.cs index beddb0f14..836ddc895 100644 --- a/AgileMapper.Buildable/BuildableMapperExtensions.cs +++ b/AgileMapper.Buildable/BuildableMapperExtensions.cs @@ -41,23 +41,24 @@ public static IEnumerable ToMapperSourceCodeExpressions( return mappingPlans .Select(mappingPlan => { - return SourceCodeFactory.SourceCode(sc => - { - foreach (var mappingFunction in mappingPlan) - { - var sourceTypeName = mappingFunction.SourceType.GetVariableNameInPascalCase(); - var targetTypeName = mappingFunction.TargetType.GetVariableNameInPascalCase(); - var className = $"{sourceTypeName}_To_{targetTypeName}_Mapper"; + var sourceCode = SourceCodeFactory.Default.CreateSourceCode(); - sc.WithClass(className, cls => cls - .WithMethod( - "Map", - mappingFunction.Summary, - mappingFunction.Mapping)); - } + var rootPlan = mappingPlan.Root; - return sc; - }); + var sourceTypeName = rootPlan.SourceType.GetVariableNameInPascalCase(); + var targetTypeName = rootPlan.TargetType.GetVariableNameInPascalCase(); + var className = $"{sourceTypeName}_To_{targetTypeName}_Mapper"; + + var mapperClass = sourceCode.AddClass(cls => cls.Named(className)); + + var coreMapMethod = mapperClass.AddMethod(rootPlan.Mapping, m => m + .WithSummary(rootPlan.Summary) + .WithVisibility(MemberVisibility.Private) + .Named("Map")); + + var coreMapMethodCall = BuildableExpression.Call(coreMapMethod); + + return sourceCode; }) .ToList(); } From 208c4119f1271a18df0c36ee7a93933b9578fc85 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Mon, 3 May 2021 12:23:28 +0100 Subject: [PATCH 07/65] Working towards buildable mappers --- .../WhenBuildingMapperSourceCode.cs | 2 +- .../BuildableMapperExtensions.cs | 108 ++++++++++---- ...gileMapper.PerformanceTester.Net461.csproj | 4 +- .../App.config | 8 +- .../packages.config | 2 +- ...nsions.cs => FluentAssertionExtensions.cs} | 4 +- AgileMapper.sln | 7 +- AgileMapper/Mapper.cs | 8 +- AgileMapper/MappingExecutor.cs | 135 +++++++++++++----- .../ObjectMappingDataFactory.cs | 2 +- AgileMapper/Plans/IMappingPlan.cs | 6 + AgileMapper/Plans/MappingPlan.cs | 4 + 12 files changed, 201 insertions(+), 89 deletions(-) rename AgileMapper.UnitTests.Common/{ShouldExtensions.cs => FluentAssertionExtensions.cs} (99%) diff --git a/AgileMapper.Buildable.UnitTests/WhenBuildingMapperSourceCode.cs b/AgileMapper.Buildable.UnitTests/WhenBuildingMapperSourceCode.cs index ce2b34100..d88168df3 100644 --- a/AgileMapper.Buildable.UnitTests/WhenBuildingMapperSourceCode.cs +++ b/AgileMapper.Buildable.UnitTests/WhenBuildingMapperSourceCode.cs @@ -17,7 +17,7 @@ public void ShouldBuildASingleSimpleSourceCodeFileViaTheInstanceApi() var sourceCode = sourceCodeExpressions .ShouldHaveSingleItem() - .ToSourceCode() + .ToCSharpString() .ShouldNotBeNull(); } } diff --git a/AgileMapper.Buildable/BuildableMapperExtensions.cs b/AgileMapper.Buildable/BuildableMapperExtensions.cs index 836ddc895..6bd121b84 100644 --- a/AgileMapper.Buildable/BuildableMapperExtensions.cs +++ b/AgileMapper.Buildable/BuildableMapperExtensions.cs @@ -1,11 +1,17 @@ namespace AgileObjects.AgileMapper.Buildable { + using System; using System.Collections.Generic; using System.Linq; + using System.Linq.Expressions; using BuildableExpressions; using BuildableExpressions.SourceCode; + using Extensions; + using NetStandardPolyfills; using Plans; using ReadableExpressions.Extensions; + using static System.Linq.Expressions.Expression; + using static BuildableExpressions.SourceCode.MemberVisibility; /// /// Provides extension methods for building AgileMapper mapper source files. @@ -25,42 +31,86 @@ public static class BuildableMapperExtensions public static IEnumerable BuildSourceCode( this IMapper mapper) { - return mapper - .GetPlansInCache() - .ToMapperSourceCodeExpressions(); - } - - /// - /// Converts these into s. - /// - /// The containing the mapping plans to convert. - /// A for each of these . - public static IEnumerable ToMapperSourceCodeExpressions( - this IEnumerable mappingPlans) - { - return mappingPlans - .Select(mappingPlan => + yield return BuildableExpression + .SourceCode(sourceCode => { - var sourceCode = SourceCodeFactory.Default.CreateSourceCode(); + sourceCode.SetNamespace("AgileObjects.AgileMapper.Buildable"); + + //var mapperClasses = new List(); + + var mapperGroups = mapper + .GetPlansInCache() + .GroupBy(plan => plan.Root.SourceType) + .Project(grp => new + { + SourceType = grp.Key, + MapperName = grp.Key.GetVariableNameInPascalCase() + "Mapper", + Plans = grp.ToList() + }) + .OrderBy(_ => _.MapperName); - var rootPlan = mappingPlan.Root; + foreach (var mapperGroup in mapperGroups) + { + var instanceMapperClass = sourceCode.AddClass(mapperGroup.MapperName, mapperClass => + { + var sourceType = mapperGroup.SourceType; + var baseType = typeof(MappingExecutor<>).MakeGenericType(sourceType); + mapperClass.SetBaseType(baseType); - var sourceTypeName = rootPlan.SourceType.GetVariableNameInPascalCase(); - var targetTypeName = rootPlan.TargetType.GetVariableNameInPascalCase(); - var className = $"{sourceTypeName}_To_{targetTypeName}_Mapper"; + mapperClass.AddConstructor(ctor => + { + var sourceParameter = ctor.AddParameter("source", sourceType); + + ctor.SetConstructorCall( + baseType.GetNonPublicInstanceConstructor(sourceType), + sourceParameter); - var mapperClass = sourceCode.AddClass(cls => cls.Named(className)); + ctor.SetBody(Empty()); + }); + + foreach (var plan in mapperGroup.Plans) + { + mapperClass.AddMethod("Map", doMapping => + { + doMapping.SetVisibility(Private); + doMapping.SetStatic(); + doMapping.SetBody(plan.Root.Mapping); + }); + } + + var mapMethodInfosByTargetType = mapperClass.Type + .GetNonPublicStaticMethods("Map") + .ToDictionary(m => m.GetParameters()[0].ParameterType.GetGenericTypeArguments()[1]); + + foreach (var plan in mapperGroup.Plans) + { + mapperClass.AddMethod(GetMapMethodName(plan), mapCaller => + { + mapCaller.SetBody(Call( + mapMethodInfosByTargetType[plan.Root.TargetType])); + }); + } + }); + } + }); + } + + private static string GetMapMethodName(IMappingPlan plan) + { + switch (plan.RuleSetName) + { + case "CreateNew": + return "ToANew"; - var coreMapMethod = mapperClass.AddMethod(rootPlan.Mapping, m => m - .WithSummary(rootPlan.Summary) - .WithVisibility(MemberVisibility.Private) - .Named("Map")); + case "Merge": + return "OnTo"; - var coreMapMethodCall = BuildableExpression.Call(coreMapMethod); + case "Overwrite": + return "Over"; - return sourceCode; - }) - .ToList(); + default: + throw new NotSupportedException($"Unable to map rule set '{plan.RuleSetName}'"); + } } } } diff --git a/AgileMapper.PerformanceTester.Net461/AgileMapper.PerformanceTester.Net461.csproj b/AgileMapper.PerformanceTester.Net461/AgileMapper.PerformanceTester.Net461.csproj index 00dab03ed..2d5e2677c 100644 --- a/AgileMapper.PerformanceTester.Net461/AgileMapper.PerformanceTester.Net461.csproj +++ b/AgileMapper.PerformanceTester.Net461/AgileMapper.PerformanceTester.Net461.csproj @@ -34,8 +34,8 @@ 4 - - ..\packages\AgileObjects.NetStandardPolyfills.1.5.0\lib\net40\AgileObjects.NetStandardPolyfills.dll + + ..\packages\AgileObjects.NetStandardPolyfills.1.6.0\lib\net40\AgileObjects.NetStandardPolyfills.dll ..\packages\AgileObjects.ReadableExpressions.3.0.0-preview1\lib\net40\AgileObjects.ReadableExpressions.dll diff --git a/AgileMapper.PerformanceTester.Net461/App.config b/AgileMapper.PerformanceTester.Net461/App.config index 3f18c82e4..e06a6e682 100644 --- a/AgileMapper.PerformanceTester.Net461/App.config +++ b/AgileMapper.PerformanceTester.Net461/App.config @@ -1,13 +1,13 @@ - + - + - - + + diff --git a/AgileMapper.PerformanceTester.Net461/packages.config b/AgileMapper.PerformanceTester.Net461/packages.config index c395c1bc2..fef25c884 100644 --- a/AgileMapper.PerformanceTester.Net461/packages.config +++ b/AgileMapper.PerformanceTester.Net461/packages.config @@ -1,6 +1,6 @@  - + diff --git a/AgileMapper.UnitTests.Common/ShouldExtensions.cs b/AgileMapper.UnitTests.Common/FluentAssertionExtensions.cs similarity index 99% rename from AgileMapper.UnitTests.Common/ShouldExtensions.cs rename to AgileMapper.UnitTests.Common/FluentAssertionExtensions.cs index 9eb41e0c4..53e511ab9 100644 --- a/AgileMapper.UnitTests.Common/ShouldExtensions.cs +++ b/AgileMapper.UnitTests.Common/FluentAssertionExtensions.cs @@ -9,7 +9,7 @@ using NetStandardPolyfills; using ReadableExpressions.Extensions; - public static class ShouldExtensions + public static class FluentAssertionExtensions { public static void ShouldBeDefault(this T value) => value.ShouldBe(default(T)); @@ -58,7 +58,7 @@ public static void ShouldBe(this TActual value, TExpected ex } } - private static readonly MethodInfo _areEqualMethod = typeof(ShouldExtensions) + private static readonly MethodInfo _areEqualMethod = typeof(FluentAssertionExtensions) .GetNonPublicStaticMethod("AreEqual"); private static bool AreEqual(TExpected expected, TActual actual) diff --git a/AgileMapper.sln b/AgileMapper.sln index 2f94638ed..4a7c736f2 100644 --- a/AgileMapper.sln +++ b/AgileMapper.sln @@ -59,7 +59,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Buildable", "Buildable", "{ EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AgileMapper.Buildable", "AgileMapper.Buildable\AgileMapper.Buildable.csproj", "{F55FFD5B-EF1F-4937-B2D2-B3324F9962D6}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AgileMapper.Buildable.UnitTests", "AgileMapper.Buildable.UnitTests\AgileMapper.Buildable.UnitTests.csproj", "{B305D680-C129-4785-B02B-10F293E19A75}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AgileMapper.Buildable.UnitTests", "AgileMapper.Buildable.UnitTests\AgileMapper.Buildable.UnitTests.csproj", "{B305D680-C129-4785-B02B-10F293E19A75}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -143,10 +143,6 @@ Global {72287439-1634-4164-ABAC-E4A462235C5D}.Debug|Any CPU.Build.0 = Debug|Any CPU {72287439-1634-4164-ABAC-E4A462235C5D}.Release|Any CPU.ActiveCfg = Release|Any CPU {72287439-1634-4164-ABAC-E4A462235C5D}.Release|Any CPU.Build.0 = Release|Any CPU - {A4D51412-D8C0-4462-B7C9-1ABDA4AE13B2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {A4D51412-D8C0-4462-B7C9-1ABDA4AE13B2}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A4D51412-D8C0-4462-B7C9-1ABDA4AE13B2}.Release|Any CPU.ActiveCfg = Release|Any CPU - {A4D51412-D8C0-4462-B7C9-1ABDA4AE13B2}.Release|Any CPU.Build.0 = Release|Any CPU {F55FFD5B-EF1F-4937-B2D2-B3324F9962D6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {F55FFD5B-EF1F-4937-B2D2-B3324F9962D6}.Debug|Any CPU.Build.0 = Debug|Any CPU {F55FFD5B-EF1F-4937-B2D2-B3324F9962D6}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -180,7 +176,6 @@ Global {5085AA3F-A5B7-40E2-9309-7E0B81FBE113} = {88D48136-E176-4AF7-ACA4-A2954F3BD55B} {F9307FDB-0F73-40AB-B7AC-AE79CFA5521C} = {88D48136-E176-4AF7-ACA4-A2954F3BD55B} {72287439-1634-4164-ABAC-E4A462235C5D} = {88D48136-E176-4AF7-ACA4-A2954F3BD55B} - {A4D51412-D8C0-4462-B7C9-1ABDA4AE13B2} = {88D48136-E176-4AF7-ACA4-A2954F3BD55B} {F55FFD5B-EF1F-4937-B2D2-B3324F9962D6} = {C0C3194B-D7C8-470F-8B7D-47BA75E0EDAE} {B305D680-C129-4785-B02B-10F293E19A75} = {C0C3194B-D7C8-470F-8B7D-47BA75E0EDAE} EndGlobalSection diff --git a/AgileMapper/Mapper.cs b/AgileMapper/Mapper.cs index dfae21c4e..1cf8e96e8 100644 --- a/AgileMapper/Mapper.cs +++ b/AgileMapper/Mapper.cs @@ -281,16 +281,16 @@ TSource IMapper.DeepClone( } IFlatteningSelector IMapper.Flatten(TSource source) - => new MappingExecutor(source, Context); + => new MappingExecutor(Context, source); IUnflatteningSelector> IMapper.Unflatten(IDictionary source) - => new MappingExecutor>(source, Context); + => new MappingExecutor>(Context, source); IUnflatteningSelector IMapper.Unflatten(QueryString queryString) - => new MappingExecutor(queryString, Context); + => new MappingExecutor(Context, queryString); ITargetSelector IMapper.Map(TSource source) - => new MappingExecutor(source, Context); + => new MappingExecutor(Context, source); #region IDisposable Members diff --git a/AgileMapper/MappingExecutor.cs b/AgileMapper/MappingExecutor.cs index 86444c07f..e9caa5567 100644 --- a/AgileMapper/MappingExecutor.cs +++ b/AgileMapper/MappingExecutor.cs @@ -12,95 +12,138 @@ using Extensions.Internal; using ObjectPopulation; - internal class MappingExecutor : + /// + /// Provides options to execute a particular mapping from an object of the + /// type. + /// + /// + /// The type of sources object from which this will + /// perform mappings. + /// + public class MappingExecutor : ITargetSelector, IFlatteningSelector, IUnflatteningSelector, IMappingContext { + private MapperContext _mapperContext; private readonly TSource _source; + private MappingRuleSet _ruleSet; + + /// + /// Initializes a new instance of the class with the + /// given . + /// + /// The source object from which the mapping is to be performed. + protected internal MappingExecutor(TSource source) + : this(Mapper.Default.Context, source) + { + } - public MappingExecutor(TSource source, MapperContext mapperContext) + internal MappingExecutor(MapperContext mapperContext, TSource source) { + _mapperContext = mapperContext.ThrowIfDisposed(); _source = source; - MapperContext = mapperContext.ThrowIfDisposed(); } - public MapperContext MapperContext { get; private set; } + MapperContext IMapperContextOwner.MapperContext => _mapperContext; - public MappingRuleSet RuleSet { get; private set; } + MappingRuleSet IRuleSetOwner.RuleSet => _ruleSet; - public bool IncludeCodeComments => false; + bool IMappingContext.IncludeCodeComments => false; - public bool IgnoreUnsuccessfulMemberPopulations => true; + bool IMappingContext.IgnoreUnsuccessfulMemberPopulations => true; - public bool LazyLoadRepeatMappingFuncs => true; + bool IMappingContext.LazyLoadRepeatMappingFuncs => true; #region ToANew Overloads - public object ToANew(Type resultType) => MappingExecutorBridge.CreateNew(resultType, this); + object ITargetSelector.ToANew(Type resultType) => ToANew(resultType); + + private object ToANew(Type resultType) => MappingExecutorBridge.CreateNew(resultType, this); - public TResult ToANew() => PerformMapping(MapperContext.RuleSets.CreateNew, default(TResult)); + TResult ITargetSelector.ToANew() => ToANew(); - public TResult ToANew(Expression>> configuration) + private TResult ToANew() => PerformMapping(CreateNew, default(TResult)); + + TResult ITargetSelector.ToANew( + Expression>> configuration) { return (configuration != null) - ? PerformMapping(MapperContext.RuleSets.CreateNew, default(TResult), new[] { configuration }) - : PerformMapping(MapperContext.RuleSets.CreateNew, default(TResult)); + ? PerformMapping(CreateNew, default(TResult), new[] { configuration }) + : PerformMapping(CreateNew, default(TResult)); } - public TResult ToANew( + TResult ITargetSelector.ToANew( params Expression>>[] configurations) + { + return ToANew(configurations); + } + + private TResult ToANew( + Expression>>[] configurations) { return configurations.Any() - ? PerformMapping(MapperContext.RuleSets.CreateNew, default(TResult), configurations) - : PerformMapping(MapperContext.RuleSets.CreateNew, default(TResult)); + ? PerformMapping(CreateNew, default(TResult), configurations) + : PerformMapping(CreateNew, default(TResult)); } + private MappingRuleSet CreateNew => _mapperContext.RuleSets.CreateNew; + #endregion #region OnTo Overloads - public TTarget OnTo(TTarget existing) => PerformMapping(MapperContext.RuleSets.Merge, existing); + TTarget ITargetSelector.OnTo(TTarget existing) + => PerformMapping(Merge, existing); - public TTarget OnTo(TTarget existing, Expression>> configuration) + TTarget ITargetSelector.OnTo( + TTarget existing, + Expression>> configuration) { return (configuration != null) - ? PerformMapping(MapperContext.RuleSets.Merge, existing, new[] { configuration }) - : PerformMapping(MapperContext.RuleSets.Merge, existing); + ? PerformMapping(Merge, existing, new[] { configuration }) + : PerformMapping(Merge, existing); } - public TTarget OnTo( + TTarget ITargetSelector.OnTo( TTarget existing, params Expression>>[] configurations) { return configurations.Any() - ? PerformMapping(MapperContext.RuleSets.Merge, existing, configurations) - : PerformMapping(MapperContext.RuleSets.Merge, existing); + ? PerformMapping(Merge, existing, configurations) + : PerformMapping(Merge, existing); } + private MappingRuleSet Merge => _mapperContext.RuleSets.Merge; + #endregion #region Over Overloads - public TTarget Over(TTarget existing) => PerformMapping(MapperContext.RuleSets.Overwrite, existing); + TTarget ITargetSelector.Over(TTarget existing) + => PerformMapping(Overwrite, existing); - public TTarget Over(TTarget existing, Expression>> configuration) + TTarget ITargetSelector.Over( + TTarget existing, + Expression>> configuration) { return (configuration != null) - ? PerformMapping(MapperContext.RuleSets.Overwrite, existing, new[] { configuration }) - : PerformMapping(MapperContext.RuleSets.Overwrite, existing); + ? PerformMapping(Overwrite, existing, new[] { configuration }) + : PerformMapping(Overwrite, existing); } - public TTarget Over( + TTarget ITargetSelector.Over( TTarget existing, params Expression>>[] configurations) { return configurations.Any() - ? PerformMapping(MapperContext.RuleSets.Overwrite, existing, configurations) - : PerformMapping(MapperContext.RuleSets.Overwrite, existing); + ? PerformMapping(Overwrite, existing, configurations) + : PerformMapping(Overwrite, existing); } + private MappingRuleSet Overwrite => _mapperContext.RuleSets.Overwrite; + #endregion private TTarget PerformMapping(MappingRuleSet ruleSet, TTarget target) @@ -110,8 +153,7 @@ private TTarget PerformMapping(MappingRuleSet ruleSet, TTarget target) return target; } - RuleSet = ruleSet; - + _ruleSet = ruleSet; return PerformMapping(target); } @@ -125,9 +167,8 @@ private TTarget PerformMapping( return target; } - RuleSet = ruleSet; - MapperContext = MapperContext.InlineContexts.GetContextFor(configurations, this); - + _ruleSet = ruleSet; + _mapperContext = _mapperContext.InlineContexts.GetContextFor(configurations, this); return PerformMapping(target); } @@ -136,10 +177,7 @@ private TTarget PerformMapping(TTarget target) if (MappingTypes.SkipTypesCheck) { // Optimise for the most common scenario: - var typedRootMappingData = ObjectMappingDataFactory - .ForRootFixedTypes(_source, target, this); - - return typedRootMappingData.MapStart(); + return PerformFixedTypesMapping(_source, target); } var rootMappingData = ObjectMappingDataFactory.ForRoot(_source, target, this); @@ -148,6 +186,25 @@ private TTarget PerformMapping(TTarget target) return (TTarget)result; } + /// + /// Maps the given object to the given , + /// without first checking the runtime Types of either object. + /// + /// The Type of object to which the mapping will be performed. + /// The object from which the mapping will be performed. + /// The object to which the mapping will be performed, if applicable. + /// + /// The given object, or a new, mapped + /// instance, as applicable. + /// + protected virtual TTarget PerformFixedTypesMapping(TSource source, TTarget target) + { + var typedRootMappingData = ObjectMappingDataFactory + .ForRootFixedTypes(source, target, this, createMapper: true); + + return typedRootMappingData.MapStart(); + } + #region IFlatteningSelector Members #if FEATURE_DYNAMIC diff --git a/AgileMapper/ObjectPopulation/ObjectMappingDataFactory.cs b/AgileMapper/ObjectPopulation/ObjectMappingDataFactory.cs index f2395bafb..8d410ea9f 100644 --- a/AgileMapper/ObjectPopulation/ObjectMappingDataFactory.cs +++ b/AgileMapper/ObjectPopulation/ObjectMappingDataFactory.cs @@ -48,7 +48,7 @@ public static ObjectMappingData ForRootFixedTypes public interface IMappingPlan : IEnumerable { + /// + /// Gets the name of the rule set (CreateNew, Overwrite, etc) used to create the mapping + /// described by this . + /// + string RuleSetName { get; } + /// /// Gets the root describing the plan to map the root source /// and target objects. diff --git a/AgileMapper/Plans/MappingPlan.cs b/AgileMapper/Plans/MappingPlan.cs index 6e65615f2..c964dc3ce 100644 --- a/AgileMapper/Plans/MappingPlan.cs +++ b/AgileMapper/Plans/MappingPlan.cs @@ -23,6 +23,7 @@ public class MappingPlan : IMappingPlan internal MappingPlan(IObjectMapper cachedMapper) { + RuleSetName = cachedMapper.MapperData.RuleSet.Name; Root = new RootMapperMappingPlanFunction(cachedMapper); _mappingPlanFunctions = new List { Root }; @@ -63,6 +64,9 @@ public static implicit operator Expression(MappingPlan mappingPlan) .SelectMany(mpf => new Expression[] { mpf.Summary, mpf.Mapping })); } + /// + public string RuleSetName { get; } + /// public IMappingPlanFunction Root { get; } From 24f49967744c7769a01bcc7273abe8f3965136ab Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Tue, 4 May 2021 22:33:59 +0100 Subject: [PATCH 08/65] Basic buildable CreateNew static mapper API working --- .../FluentAssertionExtensions.cs | 62 ++++ .../WhenBuildingMapperSourceCode.cs | 80 ++++- .../BuildableMapperExtensions.cs | 279 +++++++++++++++--- AgileMapper/MappingExecutor.cs | 30 +- ...cs => IUntypedObjectMappingDataFactory.cs} | 2 +- .../ObjectMappingDataFactory.cs | 24 +- 6 files changed, 403 insertions(+), 74 deletions(-) create mode 100644 AgileMapper.Buildable.UnitTests/FluentAssertionExtensions.cs rename AgileMapper/ObjectPopulation/{IObjectMappingDataFactoryBridge.cs => IUntypedObjectMappingDataFactory.cs} (98%) diff --git a/AgileMapper.Buildable.UnitTests/FluentAssertionExtensions.cs b/AgileMapper.Buildable.UnitTests/FluentAssertionExtensions.cs new file mode 100644 index 000000000..155198099 --- /dev/null +++ b/AgileMapper.Buildable.UnitTests/FluentAssertionExtensions.cs @@ -0,0 +1,62 @@ +namespace AgileObjects.AgileMapper.Buildable.UnitTests +{ + using System; + using System.Collections.Generic; + using System.Reflection; + using AgileMapper.UnitTests.Common; + using AgileMapper.UnitTests.Common.TestClasses; + using BuildableExpressions.Compilation; + using BuildableExpressions.SourceCode; + using NetStandardPolyfills; + + internal static class FluentAssertionExtensions + { + public static Type ShouldCompileAStaticMapperClass( + this IEnumerable sourceCodeExpressions) + { + var mapperAssembly = sourceCodeExpressions + .ShouldHaveSingleItem() + .Compile() + .CompiledAssembly + .ShouldNotBeNull(); + + var staticMapperClass = mapperAssembly + .GetType("AgileObjects.AgileMapper.Buildable." + nameof(Mapper)) + .ShouldNotBeNull(); + + return staticMapperClass; + } + + public static IEnumerable GetMapMethods(this Type staticMapperClass) + => staticMapperClass.GetPublicStaticMethods("Map"); + + public static MappingExecutor ShouldCreateMappingExecutor( + this MethodInfo staticMapMethod, + TSource source) + { + return staticMapMethod + .Invoke(null, new object[] { source }) + .ShouldNotBeNull() + .ShouldBeOfType>(); + } + + public static MethodInfo ShouldHaveAToANewMethod( + this MappingExecutor executor) + { + return executor.GetType() + .GetPublicInstanceMethods("ToANew") + .ShouldHaveSingleItem(); + } + + public static TResult ShouldExecuteAToANewMapping( + this MethodInfo createNewMethod, + object executor) + { + return createNewMethod + .MakeGenericMethod(typeof(TResult)) + .Invoke(executor, Array.Empty()) + .ShouldNotBeNull() + .ShouldBeOfType(); + } + } +} diff --git a/AgileMapper.Buildable.UnitTests/WhenBuildingMapperSourceCode.cs b/AgileMapper.Buildable.UnitTests/WhenBuildingMapperSourceCode.cs index d88168df3..02177d9bd 100644 --- a/AgileMapper.Buildable.UnitTests/WhenBuildingMapperSourceCode.cs +++ b/AgileMapper.Buildable.UnitTests/WhenBuildingMapperSourceCode.cs @@ -1,13 +1,18 @@ namespace AgileObjects.AgileMapper.Buildable.UnitTests { + using System; + using System.Collections.Generic; + using System.Reflection; using AgileMapper.UnitTests.Common; using AgileObjects.AgileMapper.UnitTests.Common.TestClasses; + using BuildableExpressions.Compilation; + using NetStandardPolyfills; using Xunit; public class WhenBuildingMapperSourceCode { [Fact] - public void ShouldBuildASingleSimpleSourceCodeFileViaTheInstanceApi() + public void ShouldBuildSingleSourceSingleTargetCreateNewMapper() { using (var mapper = Mapper.CreateNew()) { @@ -15,10 +20,75 @@ public void ShouldBuildASingleSimpleSourceCodeFileViaTheInstanceApi() var sourceCodeExpressions = mapper.BuildSourceCode(); - var sourceCode = sourceCodeExpressions - .ShouldHaveSingleItem() - .ToCSharpString() - .ShouldNotBeNull(); + var staticMapperClass = sourceCodeExpressions + .ShouldCompileAStaticMapperClass(); + + var staticMapMethod = staticMapperClass + .GetMapMethods() + .ShouldHaveSingleItem(); + + var source = new PublicField { Value = "123" }; + + var executor = staticMapMethod + .ShouldCreateMappingExecutor(source); + + var result = executor + .ShouldHaveAToANewMethod() + .ShouldExecuteAToANewMapping>(executor); + + result.Value.ShouldBe(123); + } + } + + [Fact] + public void ShouldBuildSingleSourceMultipleTargetCreateNewMapper() + { + using (var mapper = Mapper.CreateNew()) + { + mapper.GetPlanFor>().ToANew>(); + mapper.GetPlanFor>().ToANew>(); + + var sourceCodeExpressions = mapper.BuildSourceCode(); + + var staticMapperClass = sourceCodeExpressions + .ShouldCompileAStaticMapperClass(); + + var staticMapMethod = staticMapperClass + .GetMapMethods() + .ShouldHaveSingleItem(); + + var source = new PublicField { Value = "456" }; + + var executor = staticMapMethod + .ShouldCreateMappingExecutor(source); + + var createNewMethod = executor.ShouldHaveAToANewMethod(); + + var publicFieldResult = createNewMethod + .ShouldExecuteAToANewMapping>(executor); + + publicFieldResult.Value.ShouldBe(456); + + var publicPropertyResult = createNewMethod + .ShouldExecuteAToANewMapping>(executor); + + publicPropertyResult.Value.ShouldBe("456"); + + var configEx = Should.Throw(() => + { + createNewMethod + .ShouldExecuteAToANewMapping>(executor); + }); + + var notSupportedMessage = configEx + .InnerException + .ShouldBeOfType() + .Message; + + notSupportedMessage.ShouldContain("Unable"); + notSupportedMessage.ShouldContain("CreateNew"); + notSupportedMessage.ShouldContain("source type 'PublicField'"); + notSupportedMessage.ShouldContain("target type 'PublicField'"); } } } diff --git a/AgileMapper.Buildable/BuildableMapperExtensions.cs b/AgileMapper.Buildable/BuildableMapperExtensions.cs index 6bd121b84..c9dbbb5f5 100644 --- a/AgileMapper.Buildable/BuildableMapperExtensions.cs +++ b/AgileMapper.Buildable/BuildableMapperExtensions.cs @@ -4,20 +4,31 @@ using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; + using System.Reflection; + using AgileObjects.ReadableExpressions; using BuildableExpressions; using BuildableExpressions.SourceCode; + using BuildableExpressions.SourceCode.Api; using Extensions; using NetStandardPolyfills; - using Plans; using ReadableExpressions.Extensions; using static System.Linq.Expressions.Expression; using static BuildableExpressions.SourceCode.MemberVisibility; + using PublicTypeExtensions = ReadableExpressions.Extensions.PublicTypeExtensions; /// /// Provides extension methods for building AgileMapper mapper source files. /// public static class BuildableMapperExtensions { + private static readonly Expression _doNotCreateMapper = Constant(false, typeof(bool)); + + private static readonly MethodInfo _isAssignableToMethod = typeof(TypeExtensionsPolyfill) + .GetPublicStaticMethod(nameof(TypeExtensionsPolyfill.IsAssignableTo)); + + private static readonly ConstructorInfo _notSupportedCtor = typeof(NotSupportedException) + .GetPublicInstanceConstructor(typeof(string)); + /// /// Builds s for the configured mappers in this /// . @@ -36,9 +47,7 @@ public static IEnumerable BuildSourceCode( { sourceCode.SetNamespace("AgileObjects.AgileMapper.Buildable"); - //var mapperClasses = new List(); - - var mapperGroups = mapper + var mapperClassGroups = mapper .GetPlansInCache() .GroupBy(plan => plan.Root.SourceType) .Project(grp => new @@ -49,68 +58,252 @@ public static IEnumerable BuildSourceCode( }) .OrderBy(_ => _.MapperName); - foreach (var mapperGroup in mapperGroups) + var mapperClassesBySourceType = new Dictionary(); + + foreach (var mapperGroup in mapperClassGroups) { - var instanceMapperClass = sourceCode.AddClass(mapperGroup.MapperName, mapperClass => - { - var sourceType = mapperGroup.SourceType; - var baseType = typeof(MappingExecutor<>).MakeGenericType(sourceType); - mapperClass.SetBaseType(baseType); + var sourceType = mapperGroup.SourceType; - mapperClass.AddConstructor(ctor => + mapperClassesBySourceType.Add(sourceType, sourceCode.AddClass( + mapperGroup.MapperName, + mapperClass => { - var sourceParameter = ctor.AddParameter("source", sourceType); - - ctor.SetConstructorCall( - baseType.GetNonPublicInstanceConstructor(sourceType), - sourceParameter); + var baseType = typeof(MappingExecutor<>).MakeGenericType(sourceType); + mapperClass.SetBaseType(baseType); - ctor.SetBody(Empty()); - }); - - foreach (var plan in mapperGroup.Plans) - { - mapperClass.AddMethod("Map", doMapping => + mapperClass.AddConstructor(ctor => { - doMapping.SetVisibility(Private); - doMapping.SetStatic(); - doMapping.SetBody(plan.Root.Mapping); + var sourceParameter = ctor.AddParameter("source", sourceType); + + ctor.SetConstructorCall( + baseType.GetNonPublicInstanceConstructor(sourceType), + sourceParameter); + + ctor.SetBody(Empty()); }); - } - var mapMethodInfosByTargetType = mapperClass.Type - .GetNonPublicStaticMethods("Map") - .ToDictionary(m => m.GetParameters()[0].ParameterType.GetGenericTypeArguments()[1]); + foreach (var plan in mapperGroup.Plans) + { + mapperClass.AddMethod(plan.RuleSetName, doMapping => + { + doMapping.SetVisibility(Private); + doMapping.SetStatic(); + doMapping.SetBody(plan.Root.Mapping); + }); + } - foreach (var plan in mapperGroup.Plans) - { - mapperClass.AddMethod(GetMapMethodName(plan), mapCaller => + var createMappingDataMethod = baseType + .GetNonPublicInstanceMethod("CreateRootMappingData"); + + var allRuleSetMapMethodInfos = mapperClass.Type + .GetNonPublicStaticMethods() + .Project(m => new MapMethodInfo( + sourceType, + mapperClass, + createMappingDataMethod, + m)) + .GroupBy(m => m.RuleSetName) + .Select(methodGroup => methodGroup.ToList()); + + foreach (var ruleSetMapMethodInfos in allRuleSetMapMethodInfos) { - mapCaller.SetBody(Call( - mapMethodInfosByTargetType[plan.Root.TargetType])); - }); - } - }); + AddMapMethodsFor(mapperClass, ruleSetMapMethodInfos); + } + })); } + + sourceCode.AddClass("Mapper", staticMapperClass => + { + staticMapperClass.SetStatic(); + + foreach (var sourceTypeAndMapper in mapperClassesBySourceType) + { + var sourceType = sourceTypeAndMapper.Key; + var mapperClass = sourceTypeAndMapper.Value; + + staticMapperClass.AddMethod("Map", mapMethod => + { + var sourceParameter = mapMethod + .AddParameter(sourceType, "source"); + + var newMapper = New( + mapperClass.Type.GetPublicInstanceConstructor(sourceType), + sourceParameter); + + mapMethod.SetBody(newMapper); + }); + } + }); }); } - private static string GetMapMethodName(IMappingPlan plan) + private static void AddMapMethodsFor( + IClassMemberConfigurator mapperClass, + IList mapMethodInfos) { - switch (plan.RuleSetName) + var ruleSetName = mapMethodInfos[0].RuleSetName; + + switch (ruleSetName) { case "CreateNew": - return "ToANew"; + AddCreateNewMapMethod(mapperClass, mapMethodInfos); + return; case "Merge": - return "OnTo"; + AddUpdateInstanceMapMethod(mapperClass, mapMethodInfos, "OnTo"); + return; case "Overwrite": - return "Over"; + AddUpdateInstanceMapMethod(mapperClass, mapMethodInfos, "Over"); + return; default: - throw new NotSupportedException($"Unable to map rule set '{plan.RuleSetName}'"); + throw new NotSupportedException($"Unable to map rule set '{ruleSetName}'"); + } + } + + private static void AddCreateNewMapMethod( + IClassMemberConfigurator mapperClass, + IList mapMethodInfos) + { + mapperClass.AddMethod("ToANew", mapNewMethod => + { + var hasSingleTarget = mapMethodInfos.Count == 1; + + var targetGenericParameter = mapNewMethod.AddGenericParameter("TTarget", param => + { + if (hasSingleTarget) + { + param.AddTypeConstraints(mapMethodInfos[0].TargetType); + } + }); + + if (hasSingleTarget) + { + mapNewMethod.SetBody(mapMethodInfos[0].CreateMapCall(Default)); + return; + } + + var targetGenericParameterType = targetGenericParameter.Type; + var typeofTarget = BuildableExpression.TypeOf(targetGenericParameter); + + var mappingExpressions = new List(); + var returnTarget = Label(targetGenericParameterType, "Return"); + + foreach (var mapMethodInfo in mapMethodInfos) + { + var targetType = mapMethodInfo.TargetType; + + var typeofTargetType = BuildableExpression.TypeOf(targetType); + var typesAssignable = Call(_isAssignableToMethod, typeofTarget, typeofTargetType); + + var mapCall = mapMethodInfo.CreateMapCall(Default); + var mapCallResultAsObject = Convert(mapCall, typeof(object)); + var mapCallResultAsTarget = Convert(mapCallResultAsObject, targetGenericParameterType); + var returnMapResult = Return(returnTarget, mapCallResultAsTarget); + + var ifTypesMatchMap = IfThen(typesAssignable, returnMapResult); + mappingExpressions.Add(ifTypesMatchMap); + } + + mappingExpressions.Add(GetThrowTargetNotSupportedException(mapMethodInfos[0], targetGenericParameter)); + mappingExpressions.Add(Label(returnTarget, Default(targetGenericParameterType))); + + mapNewMethod.SetBody(Block(mappingExpressions)); + }); + } + + private static Expression GetThrowTargetNotSupportedException( + MapMethodInfo mapMethodInfo, + TypeExpression targetGenericParameter) + { + var stringConcatMethod = typeof(string).GetPublicStaticMethod( + nameof(string.Concat), + typeof(string), + typeof(string), + typeof(string)); + + var nullConfiguration = Default(typeof(Func)); + + var getFriendlyNameMethod = typeof(PublicTypeExtensions).GetPublicStaticMethod( + nameof(PublicTypeExtensions.GetFriendlyName), + typeof(Type), + nullConfiguration.Type); + + var getErrorMessageCall = Call( + stringConcatMethod, + Constant( + $"Unable to perform a '{mapMethodInfo.RuleSetName}' mapping " + + $"from source type '{mapMethodInfo.SourceType.GetFriendlyName()}' " + + "to target type '", + typeof(string)), + Call( + getFriendlyNameMethod, + BuildableExpression.TypeOf(targetGenericParameter), + nullConfiguration), + Constant("'", typeof(string))); + + return Throw(New(_notSupportedCtor, getErrorMessageCall)); + } + + private static void AddUpdateInstanceMapMethod( + IClassMemberConfigurator mapperClass, + IEnumerable mapMethodInfos, + string apiMethodName) + { + foreach (var mapMethodInfo in mapMethodInfos) + { + mapperClass.AddMethod(apiMethodName, mapMethod => + { + mapMethod.SetBody(mapMethodInfo + .CreateMapCall(t => mapMethod.AddParameter(t, "target"))); + }); } } + + #region Helper Members + + private class MapMethodInfo + { + private readonly IClassExpressionConfigurator _mapperClass; + private readonly MethodInfo _createMappingDataMethod; + private readonly MethodInfo _mapMethod; + + public MapMethodInfo( + Type sourceType, + IClassExpressionConfigurator mapperClass, + MethodInfo createMappingDataMethod, + MethodInfo mapMethod) + { + SourceType = sourceType; + _createMappingDataMethod = createMappingDataMethod; + _mapperClass = mapperClass; + _mapMethod = mapMethod; + + TargetType = mapMethod + .GetParameters()[0] + .ParameterType + .GetGenericTypeArguments()[1]; + } + + public string RuleSetName => _mapMethod.Name; + + public Type SourceType { get; } + + public Type TargetType { get; } + + public Expression CreateMapCall(Func targetFactory) + { + var createMappingDataCall = Call( + _mapperClass.ThisInstanceExpression, + _createMappingDataMethod.MakeGenericMethod(TargetType), + targetFactory.Invoke(TargetType), + _doNotCreateMapper); + + return Call(_mapMethod, createMappingDataCall); + } + } + + #endregion } } diff --git a/AgileMapper/MappingExecutor.cs b/AgileMapper/MappingExecutor.cs index e9caa5567..1d81d5950 100644 --- a/AgileMapper/MappingExecutor.cs +++ b/AgileMapper/MappingExecutor.cs @@ -177,7 +177,10 @@ private TTarget PerformMapping(TTarget target) if (MappingTypes.SkipTypesCheck) { // Optimise for the most common scenario: - return PerformFixedTypesMapping(_source, target); + var typedRootMappingData = ObjectMappingDataFactory + .ForRootFixedTypes(_source, target, this, createMapper: true); + + return typedRootMappingData.MapStart(); } var rootMappingData = ObjectMappingDataFactory.ForRoot(_source, target, this); @@ -187,22 +190,23 @@ private TTarget PerformMapping(TTarget target) } /// - /// Maps the given object to the given , - /// without first checking the runtime Types of either object. + /// Create an object for this + /// 's source object and the given + /// object, optionally building a Mapper for the types. /// - /// The Type of object to which the mapping will be performed. - /// The object from which the mapping will be performed. - /// The object to which the mapping will be performed, if applicable. + /// The type of target object to which the mapping is being performed. + /// The target object to which the mapping is being performed. + /// Whether a Mapper should be created for the types being mapped. /// - /// The given object, or a new, mapped - /// instance, as applicable. + /// An object for this + /// 's source object and the given + /// object. /// - protected virtual TTarget PerformFixedTypesMapping(TSource source, TTarget target) + protected IObjectMappingData CreateRootMappingData( + TTarget target, + bool createMapper) { - var typedRootMappingData = ObjectMappingDataFactory - .ForRootFixedTypes(source, target, this, createMapper: true); - - return typedRootMappingData.MapStart(); + return ObjectMappingDataFactory.ForRootFixedTypes(_source, target, this, createMapper); } #region IFlatteningSelector Members diff --git a/AgileMapper/ObjectPopulation/IObjectMappingDataFactoryBridge.cs b/AgileMapper/ObjectPopulation/IUntypedObjectMappingDataFactory.cs similarity index 98% rename from AgileMapper/ObjectPopulation/IObjectMappingDataFactoryBridge.cs rename to AgileMapper/ObjectPopulation/IUntypedObjectMappingDataFactory.cs index d7b00c807..49553e452 100644 --- a/AgileMapper/ObjectPopulation/IObjectMappingDataFactoryBridge.cs +++ b/AgileMapper/ObjectPopulation/IUntypedObjectMappingDataFactory.cs @@ -4,7 +4,7 @@ namespace AgileObjects.AgileMapper.ObjectPopulation /// Provides bridge methods enabling creation of typed /// instances in partial trust scenarios. This interface is intended for internal use only. /// - public interface IObjectMappingDataFactoryBridge + public interface IUntypedObjectMappingDataFactory { /// /// Creates a child instance. diff --git a/AgileMapper/ObjectPopulation/ObjectMappingDataFactory.cs b/AgileMapper/ObjectPopulation/ObjectMappingDataFactory.cs index 8d410ea9f..dfd4e8d7a 100644 --- a/AgileMapper/ObjectPopulation/ObjectMappingDataFactory.cs +++ b/AgileMapper/ObjectPopulation/ObjectMappingDataFactory.cs @@ -16,9 +16,9 @@ namespace AgileObjects.AgileMapper.ObjectPopulation using Members.Sources; using NetStandardPolyfills; - internal class ObjectMappingDataFactory : IObjectMappingDataFactoryBridge + internal class ObjectMappingDataFactory : IUntypedObjectMappingDataFactory { - private static readonly IObjectMappingDataFactoryBridge _bridge = new ObjectMappingDataFactory(); + private static readonly IUntypedObjectMappingDataFactory _bridge = new ObjectMappingDataFactory(); public static ObjectMappingData ForRootFixedTypes( IMappingContext mappingContext) @@ -123,7 +123,7 @@ public static IObjectMappingData ForChild( var typedForChildCaller = GlobalContext.Instance.Cache.GetOrAddWithHashCodes(key, k => { - var bridgeParameter = Expression.Parameter(typeof(IObjectMappingDataFactoryBridge), "bridge"); + var bridgeParameter = Expression.Parameter(typeof(IUntypedObjectMappingDataFactory), "bridge"); var childMembersSourceParameter = Expression.Parameter(typeof(object), "childMembersSource"); var parentParameter = Expression.Parameter(typeof(object), nameof(parent)); @@ -137,7 +137,7 @@ public static IObjectMappingData ForChild( childMembersSourceParameter, parentParameter); - var typedForChildLambda = Expression.Lambda>( + var typedForChildLambda = Expression.Lambda>( typedForChildCall, bridgeParameter, childMembersSourceParameter, @@ -151,7 +151,7 @@ public static IObjectMappingData ForChild( return (IObjectMappingData)typedForChildCaller.Invoke(_bridge, membersSource, parent); } - object IObjectMappingDataFactoryBridge.ForChild(object childMembersSource, object parent) + object IUntypedObjectMappingDataFactory.ForChild(object childMembersSource, object parent) { var mapperKey = new ChildObjectMapperKey( MappingTypes.For(default(TSource), default(TTarget)), @@ -208,7 +208,7 @@ public static IObjectMappingData ForElement( var typedForElementCaller = GlobalContext.Instance.Cache.GetOrAdd(key, k => { - var bridgeParameter = Expression.Parameter(typeof(IObjectMappingDataFactoryBridge), "bridge"); + var bridgeParameter = Expression.Parameter(typeof(IUntypedObjectMappingDataFactory), "bridge"); var parentParameter = Expression.Parameter(typeof(object), "parent"); var typedForElementMethod = bridgeParameter.Type @@ -220,7 +220,7 @@ public static IObjectMappingData ForElement( typedForElementMethod, parentParameter); - var typedForElementLambda = Expression.Lambda>( + var typedForElementLambda = Expression.Lambda>( typedForElementCall, bridgeParameter, parentParameter); @@ -231,7 +231,7 @@ public static IObjectMappingData ForElement( return (IObjectMappingData)typedForElementCaller.Invoke(_bridge, parent); } - object IObjectMappingDataFactoryBridge.ForElement(object parent) + object IUntypedObjectMappingDataFactory.ForElement(object parent) { var mappingData = (IObjectMappingData)parent; var source = mappingData.GetSource(); @@ -330,14 +330,14 @@ private static IObjectMappingData Create( parent); } - private static Func GetPartialTrustMappingDataCreator( + private static Func GetPartialTrustMappingDataCreator( MappingTypes mappingTypes) { var createCallerKey = DeclaredAndRuntimeTypesKey.For(mappingTypes); var createCallerFunc = GlobalContext.Instance.Cache.GetOrAdd(createCallerKey, k => { - var bridgeParameter = Expression.Parameter(typeof(IObjectMappingDataFactoryBridge), "bridge"); + var bridgeParameter = Expression.Parameter(typeof(IUntypedObjectMappingDataFactory), "bridge"); var sourceParameter = Parameters.Create(k.DeclaredSourceType, "source"); var targetParameter = Parameters.Create(k.DeclaredTargetType, "target"); var elementIndexParameter = Expression.Parameter(typeof(int?), "i"); @@ -366,7 +366,7 @@ private static IObjectMappingData Create( parentParameter); var createLambda = Expression - .Lambda>( + .Lambda>( createCall, bridgeParameter, sourceParameter, @@ -383,7 +383,7 @@ private static IObjectMappingData Create( return createCallerFunc; } - object IObjectMappingDataFactoryBridge.CreateMappingData( + object IUntypedObjectMappingDataFactory.CreateMappingData( TDeclaredSource source, TDeclaredTarget target, int? elementIndex, From 5bc7ea8c71c5edbc265ccaf7ebb85e31d35a7afb Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Wed, 5 May 2021 09:20:29 +0100 Subject: [PATCH 09/65] Extending test coverage --- .../FluentAssertionExtensions.cs | 45 ++++++++++- .../WhenBuildingMapperSourceCode.cs | 77 ++++++++++++++++--- .../FluentAssertionExtensions.cs | 4 +- .../TestClasses/Address.cs | 2 +- .../Inline/WhenConfiguringCallbacksInline.cs | 1 + .../WhenConfiguringObjectCreationInline.cs | 1 + .../Inline/WhenMappingToNullInline.cs | 1 + .../WhenConfiguringExceptionHandling.cs | 1 + ...henConfiguringObjectCreationIncorrectly.cs | 1 + ...WhenConfiguringObjectMappingIncorrectly.cs | 1 + .../WhenViewingDictionaryMappingPlans.cs | 1 + ...tteningToQueryStringViaExtensionMethods.cs | 1 + .../WhenFlatteningViaExtensionMethods.cs | 1 + ...ningFromQueryStringsViaExtensionMethods.cs | 2 +- .../MapperCloning/WhenCloningMemberIgnores.cs | 2 +- .../WhenCloningObjectFactories.cs | 1 + .../Structs/WhenMappingOnToStructMembers.cs | 1 + .../WhenMappingToUnmappableStructMembers.cs | 1 + .../WhenMappingOnToEnumerables.cs | 1 + 19 files changed, 129 insertions(+), 16 deletions(-) rename {AgileMapper.UnitTests => AgileMapper.UnitTests.Common}/TestClasses/Address.cs (75%) diff --git a/AgileMapper.Buildable.UnitTests/FluentAssertionExtensions.cs b/AgileMapper.Buildable.UnitTests/FluentAssertionExtensions.cs index 155198099..f3767b043 100644 --- a/AgileMapper.Buildable.UnitTests/FluentAssertionExtensions.cs +++ b/AgileMapper.Buildable.UnitTests/FluentAssertionExtensions.cs @@ -4,7 +4,6 @@ using System.Collections.Generic; using System.Reflection; using AgileMapper.UnitTests.Common; - using AgileMapper.UnitTests.Common.TestClasses; using BuildableExpressions.Compilation; using BuildableExpressions.SourceCode; using NetStandardPolyfills; @@ -40,7 +39,7 @@ public static MappingExecutor ShouldCreateMappingExecutor( .ShouldBeOfType>(); } - public static MethodInfo ShouldHaveAToANewMethod( + public static MethodInfo ShouldHaveACreateNewMethod( this MappingExecutor executor) { return executor.GetType() @@ -48,7 +47,23 @@ public static MethodInfo ShouldHaveAToANewMethod( .ShouldHaveSingleItem(); } - public static TResult ShouldExecuteAToANewMapping( + public static MethodInfo ShouldHaveAMergeMethod( + this MappingExecutor executor) + { + return executor.GetType() + .GetPublicInstanceMethods("OnTo") + .ShouldHaveSingleItem(); + } + + public static MethodInfo ShouldHaveAnOverwriteMethod( + this MappingExecutor executor) + { + return executor.GetType() + .GetPublicInstanceMethods("Over") + .ShouldHaveSingleItem(); + } + + public static TResult ShouldExecuteACreateNewMapping( this MethodInfo createNewMethod, object executor) { @@ -58,5 +73,29 @@ public static TResult ShouldExecuteAToANewMapping( .ShouldNotBeNull() .ShouldBeOfType(); } + + public static TTarget ShouldExecuteAMergeMapping( + this MethodInfo mergeMethod, + object executor, + TTarget target) + { + return mergeMethod + .Invoke(executor, new object[] { target }) + .ShouldNotBeNull() + .ShouldBeSameAs(target) + .ShouldBeOfType(); + } + + public static TTarget ShouldExecuteAnOverwriteMapping( + this MethodInfo overwriteMethod, + object executor, + TTarget target) + { + return overwriteMethod + .Invoke(executor, new object[] { target }) + .ShouldNotBeNull() + .ShouldBeSameAs(target) + .ShouldBeOfType(); + } } } diff --git a/AgileMapper.Buildable.UnitTests/WhenBuildingMapperSourceCode.cs b/AgileMapper.Buildable.UnitTests/WhenBuildingMapperSourceCode.cs index 02177d9bd..f16bdefeb 100644 --- a/AgileMapper.Buildable.UnitTests/WhenBuildingMapperSourceCode.cs +++ b/AgileMapper.Buildable.UnitTests/WhenBuildingMapperSourceCode.cs @@ -1,12 +1,9 @@ namespace AgileObjects.AgileMapper.Buildable.UnitTests { using System; - using System.Collections.Generic; using System.Reflection; using AgileMapper.UnitTests.Common; using AgileObjects.AgileMapper.UnitTests.Common.TestClasses; - using BuildableExpressions.Compilation; - using NetStandardPolyfills; using Xunit; public class WhenBuildingMapperSourceCode @@ -33,8 +30,8 @@ public void ShouldBuildSingleSourceSingleTargetCreateNewMapper() .ShouldCreateMappingExecutor(source); var result = executor - .ShouldHaveAToANewMethod() - .ShouldExecuteAToANewMapping>(executor); + .ShouldHaveACreateNewMethod() + .ShouldExecuteACreateNewMapping>(executor); result.Value.ShouldBe(123); } @@ -62,22 +59,22 @@ public void ShouldBuildSingleSourceMultipleTargetCreateNewMapper() var executor = staticMapMethod .ShouldCreateMappingExecutor(source); - var createNewMethod = executor.ShouldHaveAToANewMethod(); + var createNewMethod = executor.ShouldHaveACreateNewMethod(); var publicFieldResult = createNewMethod - .ShouldExecuteAToANewMapping>(executor); + .ShouldExecuteACreateNewMapping>(executor); publicFieldResult.Value.ShouldBe(456); var publicPropertyResult = createNewMethod - .ShouldExecuteAToANewMapping>(executor); + .ShouldExecuteACreateNewMapping>(executor); publicPropertyResult.Value.ShouldBe("456"); var configEx = Should.Throw(() => { createNewMethod - .ShouldExecuteAToANewMapping>(executor); + .ShouldExecuteACreateNewMapping>(executor); }); var notSupportedMessage = configEx @@ -91,5 +88,67 @@ public void ShouldBuildSingleSourceMultipleTargetCreateNewMapper() notSupportedMessage.ShouldContain("target type 'PublicField'"); } } + + [Fact] + public void ShouldBuildASingleSourceSingleTargetMergeMapper() + { + using (var mapper = Mapper.CreateNew()) + { + mapper.GetPlanFor
().OnTo
(); + + var sourceCodeExpressions = mapper.BuildSourceCode(); + + var staticMapperClass = sourceCodeExpressions + .ShouldCompileAStaticMapperClass(); + + var staticMapMethod = staticMapperClass + .GetMapMethods() + .ShouldHaveSingleItem(); + + var source = new Address { Line1 = "Line 1!" }; + var target = new Address { Line2 = "Line 2!" }; + + var executor = staticMapMethod + .ShouldCreateMappingExecutor(source); + + executor + .ShouldHaveAMergeMethod() + .ShouldExecuteAMergeMapping(executor, target); + + target.Line1.ShouldBe("Line 1!"); + target.Line2.ShouldBe("Line 2!"); + } + } + + [Fact] + public void ShouldBuildASingleSourceSingleTargetOverwriteMapper() + { + using (var mapper = Mapper.CreateNew()) + { + mapper.GetPlanFor
().Over
(); + + var sourceCodeExpressions = mapper.BuildSourceCode(); + + var staticMapperClass = sourceCodeExpressions + .ShouldCompileAStaticMapperClass(); + + var staticMapMethod = staticMapperClass + .GetMapMethods() + .ShouldHaveSingleItem(); + + var source = new Address { Line1 = "1.1", Line2 = "1.2" }; + var target = new Address { Line1 = "2.1", Line2 = "2.2" }; + + var executor = staticMapMethod + .ShouldCreateMappingExecutor(source); + + executor + .ShouldHaveAnOverwriteMethod() + .ShouldExecuteAnOverwriteMapping(executor, target); + + target.Line1.ShouldBe("1.1"); + target.Line2.ShouldBe("1.2"); + } + } } } diff --git a/AgileMapper.UnitTests.Common/FluentAssertionExtensions.cs b/AgileMapper.UnitTests.Common/FluentAssertionExtensions.cs index 53e511ab9..140057b41 100644 --- a/AgileMapper.UnitTests.Common/FluentAssertionExtensions.cs +++ b/AgileMapper.UnitTests.Common/FluentAssertionExtensions.cs @@ -187,13 +187,15 @@ public static void ShouldNotBeSameAs(this T actualItem, T nonExpectedItem) } } - public static void ShouldBeSameAs(this T actualItem, T expectedItem) + public static T ShouldBeSameAs(this T actualItem, T expectedItem) where T : class { if (!ReferenceEquals(expectedItem, actualItem)) { Asplode(expectedItem.ToString(), actualItem.ToString()); } + + return expectedItem; } public static void ShouldBeTrue(this bool? value) diff --git a/AgileMapper.UnitTests/TestClasses/Address.cs b/AgileMapper.UnitTests.Common/TestClasses/Address.cs similarity index 75% rename from AgileMapper.UnitTests/TestClasses/Address.cs rename to AgileMapper.UnitTests.Common/TestClasses/Address.cs index ab82220f6..b48cc3c74 100644 --- a/AgileMapper.UnitTests/TestClasses/Address.cs +++ b/AgileMapper.UnitTests.Common/TestClasses/Address.cs @@ -1,4 +1,4 @@ -namespace AgileObjects.AgileMapper.UnitTests.TestClasses +namespace AgileObjects.AgileMapper.UnitTests.Common.TestClasses { public sealed class Address { diff --git a/AgileMapper.UnitTests/Configuration/Inline/WhenConfiguringCallbacksInline.cs b/AgileMapper.UnitTests/Configuration/Inline/WhenConfiguringCallbacksInline.cs index 4e1ece31f..bffc03864 100644 --- a/AgileMapper.UnitTests/Configuration/Inline/WhenConfiguringCallbacksInline.cs +++ b/AgileMapper.UnitTests/Configuration/Inline/WhenConfiguringCallbacksInline.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using AgileMapper.Members; using Common; + using Common.TestClasses; using TestClasses; #if !NET35 using Xunit; diff --git a/AgileMapper.UnitTests/Configuration/Inline/WhenConfiguringObjectCreationInline.cs b/AgileMapper.UnitTests/Configuration/Inline/WhenConfiguringObjectCreationInline.cs index 432641cf7..7f1f20cb7 100644 --- a/AgileMapper.UnitTests/Configuration/Inline/WhenConfiguringObjectCreationInline.cs +++ b/AgileMapper.UnitTests/Configuration/Inline/WhenConfiguringObjectCreationInline.cs @@ -3,6 +3,7 @@ using System; using AgileMapper.Configuration; using Common; + using Common.TestClasses; using TestClasses; #if !NET35 using Xunit; diff --git a/AgileMapper.UnitTests/Configuration/Inline/WhenMappingToNullInline.cs b/AgileMapper.UnitTests/Configuration/Inline/WhenMappingToNullInline.cs index 16298a786..6c9086e4b 100644 --- a/AgileMapper.UnitTests/Configuration/Inline/WhenMappingToNullInline.cs +++ b/AgileMapper.UnitTests/Configuration/Inline/WhenMappingToNullInline.cs @@ -3,6 +3,7 @@ using AgileMapper.Configuration; using AgileMapper.Extensions.Internal; using Common; + using Common.TestClasses; using TestClasses; #if !NET35 using Xunit; diff --git a/AgileMapper.UnitTests/Configuration/WhenConfiguringExceptionHandling.cs b/AgileMapper.UnitTests/Configuration/WhenConfiguringExceptionHandling.cs index 039351391..58b1d7589 100644 --- a/AgileMapper.UnitTests/Configuration/WhenConfiguringExceptionHandling.cs +++ b/AgileMapper.UnitTests/Configuration/WhenConfiguringExceptionHandling.cs @@ -2,6 +2,7 @@ { using System; using Common; + using Common.TestClasses; using TestClasses; #if !NET35 using Xunit; diff --git a/AgileMapper.UnitTests/Configuration/WhenConfiguringObjectCreationIncorrectly.cs b/AgileMapper.UnitTests/Configuration/WhenConfiguringObjectCreationIncorrectly.cs index bfcac36ee..b9d951ecc 100644 --- a/AgileMapper.UnitTests/Configuration/WhenConfiguringObjectCreationIncorrectly.cs +++ b/AgileMapper.UnitTests/Configuration/WhenConfiguringObjectCreationIncorrectly.cs @@ -3,6 +3,7 @@ using System; using AgileMapper.Configuration; using Common; + using Common.TestClasses; using TestClasses; #if !NET35 using Xunit; diff --git a/AgileMapper.UnitTests/Configuration/WhenConfiguringObjectMappingIncorrectly.cs b/AgileMapper.UnitTests/Configuration/WhenConfiguringObjectMappingIncorrectly.cs index b581a6b04..6866de062 100644 --- a/AgileMapper.UnitTests/Configuration/WhenConfiguringObjectMappingIncorrectly.cs +++ b/AgileMapper.UnitTests/Configuration/WhenConfiguringObjectMappingIncorrectly.cs @@ -3,6 +3,7 @@ using System; using AgileMapper.Configuration; using Common; + using Common.TestClasses; using TestClasses; #if !NET35 using Xunit; diff --git a/AgileMapper.UnitTests/Dictionaries/WhenViewingDictionaryMappingPlans.cs b/AgileMapper.UnitTests/Dictionaries/WhenViewingDictionaryMappingPlans.cs index 4919a6a05..9c2c13d49 100644 --- a/AgileMapper.UnitTests/Dictionaries/WhenViewingDictionaryMappingPlans.cs +++ b/AgileMapper.UnitTests/Dictionaries/WhenViewingDictionaryMappingPlans.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Collections.ObjectModel; using Common; + using Common.TestClasses; using TestClasses; #if !NET35 using Xunit; diff --git a/AgileMapper.UnitTests/Extensions/WhenFlatteningToQueryStringViaExtensionMethods.cs b/AgileMapper.UnitTests/Extensions/WhenFlatteningToQueryStringViaExtensionMethods.cs index 4ab88c5da..12b0a52fe 100644 --- a/AgileMapper.UnitTests/Extensions/WhenFlatteningToQueryStringViaExtensionMethods.cs +++ b/AgileMapper.UnitTests/Extensions/WhenFlatteningToQueryStringViaExtensionMethods.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using AgileMapper.Extensions; using Common; + using Common.TestClasses; using TestClasses; #if !NET35 using Xunit; diff --git a/AgileMapper.UnitTests/Extensions/WhenFlatteningViaExtensionMethods.cs b/AgileMapper.UnitTests/Extensions/WhenFlatteningViaExtensionMethods.cs index 3e7b796c6..39b49a13a 100644 --- a/AgileMapper.UnitTests/Extensions/WhenFlatteningViaExtensionMethods.cs +++ b/AgileMapper.UnitTests/Extensions/WhenFlatteningViaExtensionMethods.cs @@ -2,6 +2,7 @@ { using AgileMapper.Extensions; using Common; + using Common.TestClasses; using TestClasses; #if !NET35 using Xunit; diff --git a/AgileMapper.UnitTests/Extensions/WhenUnflatteningFromQueryStringsViaExtensionMethods.cs b/AgileMapper.UnitTests/Extensions/WhenUnflatteningFromQueryStringsViaExtensionMethods.cs index cf7dc4fc2..3b23ec1f9 100644 --- a/AgileMapper.UnitTests/Extensions/WhenUnflatteningFromQueryStringsViaExtensionMethods.cs +++ b/AgileMapper.UnitTests/Extensions/WhenUnflatteningFromQueryStringsViaExtensionMethods.cs @@ -3,7 +3,7 @@ using System; using AgileMapper.Extensions; using Common; - using TestClasses; + using Common.TestClasses; #if !NET35 using Xunit; #else diff --git a/AgileMapper.UnitTests/MapperCloning/WhenCloningMemberIgnores.cs b/AgileMapper.UnitTests/MapperCloning/WhenCloningMemberIgnores.cs index 3b58da798..fa37315f1 100644 --- a/AgileMapper.UnitTests/MapperCloning/WhenCloningMemberIgnores.cs +++ b/AgileMapper.UnitTests/MapperCloning/WhenCloningMemberIgnores.cs @@ -2,7 +2,7 @@ { using AgileMapper.Configuration; using Common; - using TestClasses; + using Common.TestClasses; #if !NET35 using Xunit; #else diff --git a/AgileMapper.UnitTests/MapperCloning/WhenCloningObjectFactories.cs b/AgileMapper.UnitTests/MapperCloning/WhenCloningObjectFactories.cs index f1d4e4855..808359afc 100644 --- a/AgileMapper.UnitTests/MapperCloning/WhenCloningObjectFactories.cs +++ b/AgileMapper.UnitTests/MapperCloning/WhenCloningObjectFactories.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using AgileMapper.Configuration; using Common; + using Common.TestClasses; using TestClasses; #if !NET35 using Xunit; diff --git a/AgileMapper.UnitTests/Structs/WhenMappingOnToStructMembers.cs b/AgileMapper.UnitTests/Structs/WhenMappingOnToStructMembers.cs index d2766f14c..066195466 100644 --- a/AgileMapper.UnitTests/Structs/WhenMappingOnToStructMembers.cs +++ b/AgileMapper.UnitTests/Structs/WhenMappingOnToStructMembers.cs @@ -1,6 +1,7 @@ namespace AgileObjects.AgileMapper.UnitTests.Structs { using Common; + using Common.TestClasses; using TestClasses; #if !NET35 using Xunit; diff --git a/AgileMapper.UnitTests/Structs/WhenMappingToUnmappableStructMembers.cs b/AgileMapper.UnitTests/Structs/WhenMappingToUnmappableStructMembers.cs index cf20414f5..26344ea4e 100644 --- a/AgileMapper.UnitTests/Structs/WhenMappingToUnmappableStructMembers.cs +++ b/AgileMapper.UnitTests/Structs/WhenMappingToUnmappableStructMembers.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; using Common; + using Common.TestClasses; using TestClasses; #if !NET35 using Xunit; diff --git a/AgileMapper.UnitTests/WhenMappingOnToEnumerables.cs b/AgileMapper.UnitTests/WhenMappingOnToEnumerables.cs index f84b33ac1..fa5985aec 100644 --- a/AgileMapper.UnitTests/WhenMappingOnToEnumerables.cs +++ b/AgileMapper.UnitTests/WhenMappingOnToEnumerables.cs @@ -5,6 +5,7 @@ using System.Collections.ObjectModel; using System.Linq; using Common; + using Common.TestClasses; using TestClasses; #if !NET35 using Xunit; From 38e5358d3fa26e4ccf25fcaa15a4601c54448430 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Sat, 8 May 2021 12:54:27 +0100 Subject: [PATCH 10/65] Fixing dynamic tests / Uncoupling built mappers from MappingExecutor --- .../FluentAssertionExtensions.cs | 10 +- .../BuildableMapperExtensions.cs | 4 +- ...ppingFromDynamicsOverComplexTypeMembers.cs | 1 + ...WhenMappingFromDynamicsOverComplexTypes.cs | 2 +- ...pingFromDynamicsToNewComplexTypeMembers.cs | 1 + AgileMapper/Caching/DefaultArrayCache.cs | 2 +- AgileMapper/MappingExecutionContextBase.cs | 59 ++++++++ AgileMapper/MappingExecutor.cs | 130 +++++------------- 8 files changed, 104 insertions(+), 105 deletions(-) create mode 100644 AgileMapper/MappingExecutionContextBase.cs diff --git a/AgileMapper.Buildable.UnitTests/FluentAssertionExtensions.cs b/AgileMapper.Buildable.UnitTests/FluentAssertionExtensions.cs index f3767b043..7fbb38dcd 100644 --- a/AgileMapper.Buildable.UnitTests/FluentAssertionExtensions.cs +++ b/AgileMapper.Buildable.UnitTests/FluentAssertionExtensions.cs @@ -29,18 +29,18 @@ public static Type ShouldCompileAStaticMapperClass( public static IEnumerable GetMapMethods(this Type staticMapperClass) => staticMapperClass.GetPublicStaticMethods("Map"); - public static MappingExecutor ShouldCreateMappingExecutor( + public static MappingExecutionContextBase ShouldCreateMappingExecutor( this MethodInfo staticMapMethod, TSource source) { return staticMapMethod .Invoke(null, new object[] { source }) .ShouldNotBeNull() - .ShouldBeOfType>(); + .ShouldBeOfType>(); } public static MethodInfo ShouldHaveACreateNewMethod( - this MappingExecutor executor) + this MappingExecutionContextBase executor) { return executor.GetType() .GetPublicInstanceMethods("ToANew") @@ -48,7 +48,7 @@ public static MethodInfo ShouldHaveACreateNewMethod( } public static MethodInfo ShouldHaveAMergeMethod( - this MappingExecutor executor) + this MappingExecutionContextBase executor) { return executor.GetType() .GetPublicInstanceMethods("OnTo") @@ -56,7 +56,7 @@ public static MethodInfo ShouldHaveAMergeMethod( } public static MethodInfo ShouldHaveAnOverwriteMethod( - this MappingExecutor executor) + this MappingExecutionContextBase executor) { return executor.GetType() .GetPublicInstanceMethods("Over") diff --git a/AgileMapper.Buildable/BuildableMapperExtensions.cs b/AgileMapper.Buildable/BuildableMapperExtensions.cs index c9dbbb5f5..d91f18dd0 100644 --- a/AgileMapper.Buildable/BuildableMapperExtensions.cs +++ b/AgileMapper.Buildable/BuildableMapperExtensions.cs @@ -5,12 +5,12 @@ using System.Linq; using System.Linq.Expressions; using System.Reflection; - using AgileObjects.ReadableExpressions; using BuildableExpressions; using BuildableExpressions.SourceCode; using BuildableExpressions.SourceCode.Api; using Extensions; using NetStandardPolyfills; + using ReadableExpressions; using ReadableExpressions.Extensions; using static System.Linq.Expressions.Expression; using static BuildableExpressions.SourceCode.MemberVisibility; @@ -68,7 +68,7 @@ public static IEnumerable BuildSourceCode( mapperGroup.MapperName, mapperClass => { - var baseType = typeof(MappingExecutor<>).MakeGenericType(sourceType); + var baseType = typeof(MappingExecutionContextBase<>).MakeGenericType(sourceType); mapperClass.SetBaseType(baseType); mapperClass.AddConstructor(ctor => diff --git a/AgileMapper.UnitTests/Dynamics/WhenMappingFromDynamicsOverComplexTypeMembers.cs b/AgileMapper.UnitTests/Dynamics/WhenMappingFromDynamicsOverComplexTypeMembers.cs index f52bc0c6b..b71764045 100644 --- a/AgileMapper.UnitTests/Dynamics/WhenMappingFromDynamicsOverComplexTypeMembers.cs +++ b/AgileMapper.UnitTests/Dynamics/WhenMappingFromDynamicsOverComplexTypeMembers.cs @@ -2,6 +2,7 @@ { using System.Dynamic; using Common; + using Common.TestClasses; using TestClasses; using Xunit; diff --git a/AgileMapper.UnitTests/Dynamics/WhenMappingFromDynamicsOverComplexTypes.cs b/AgileMapper.UnitTests/Dynamics/WhenMappingFromDynamicsOverComplexTypes.cs index 319931fcd..2bcba2606 100644 --- a/AgileMapper.UnitTests/Dynamics/WhenMappingFromDynamicsOverComplexTypes.cs +++ b/AgileMapper.UnitTests/Dynamics/WhenMappingFromDynamicsOverComplexTypes.cs @@ -3,7 +3,7 @@ namespace AgileObjects.AgileMapper.UnitTests.Dynamics { using System.Dynamic; using Common; - using TestClasses; + using Common.TestClasses; using Xunit; public class WhenMappingFromDynamicsOverComplexTypes diff --git a/AgileMapper.UnitTests/Dynamics/WhenMappingFromDynamicsToNewComplexTypeMembers.cs b/AgileMapper.UnitTests/Dynamics/WhenMappingFromDynamicsToNewComplexTypeMembers.cs index bf169c6a4..745779582 100644 --- a/AgileMapper.UnitTests/Dynamics/WhenMappingFromDynamicsToNewComplexTypeMembers.cs +++ b/AgileMapper.UnitTests/Dynamics/WhenMappingFromDynamicsToNewComplexTypeMembers.cs @@ -2,6 +2,7 @@ namespace AgileObjects.AgileMapper.UnitTests.Dynamics { using System.Dynamic; + using Common.TestClasses; using TestClasses; using Xunit; diff --git a/AgileMapper/Caching/DefaultArrayCache.cs b/AgileMapper/Caching/DefaultArrayCache.cs index 8d2811c8c..b4dd4d72f 100644 --- a/AgileMapper/Caching/DefaultArrayCache.cs +++ b/AgileMapper/Caching/DefaultArrayCache.cs @@ -23,7 +23,7 @@ protected override bool TryGetValue(TKey key, int length, out TValue value) { - for (var i = startIndex; i < length; i++) + for (var i = startIndex; i < length; ++i) { var thisKey = _keys[i]; diff --git a/AgileMapper/MappingExecutionContextBase.cs b/AgileMapper/MappingExecutionContextBase.cs new file mode 100644 index 000000000..5ad6f86c8 --- /dev/null +++ b/AgileMapper/MappingExecutionContextBase.cs @@ -0,0 +1,59 @@ +namespace AgileObjects.AgileMapper +{ + using ObjectPopulation; + + /// + /// Base type providing creation for objects + /// of the type. + /// + /// + /// The type of source objects from which this + /// will perform mappings. + /// + public abstract class MappingExecutionContextBase : IMappingContext + { + private readonly MapperContext _mapperContext; + private readonly TSource _source; + + /// + /// Initializes a new instance of the + /// class with a default MapperContext and the given . + /// + /// The source object from which the mapping is to be performed. + protected MappingExecutionContextBase(TSource source) + { + _mapperContext = Mapper.Default.Context.ThrowIfDisposed(); + _source = source; + } + + MapperContext IMapperContextOwner.MapperContext => _mapperContext; + + MappingRuleSet IRuleSetOwner.RuleSet => null; + + bool IMappingContext.IncludeCodeComments => false; + + bool IMappingContext.IgnoreUnsuccessfulMemberPopulations => true; + + bool IMappingContext.LazyLoadRepeatMappingFuncs => true; + + /// + /// Create an object for this + /// 's source object and the given + /// object, optionally building a Mapper for the types. + /// + /// The type of target object to which the mapping is being performed. + /// The target object to which the mapping is being performed. + /// Whether a Mapper should be created for the types being mapped. + /// + /// An object for this + /// 's source object and the given + /// object. + /// + protected IObjectMappingData CreateRootMappingData( + TTarget target, + bool createMapper) + { + return ObjectMappingDataFactory.ForRootFixedTypes(_source, target, this, createMapper); + } + } +} \ No newline at end of file diff --git a/AgileMapper/MappingExecutor.cs b/AgileMapper/MappingExecutor.cs index 1d81d5950..e8d880079 100644 --- a/AgileMapper/MappingExecutor.cs +++ b/AgileMapper/MappingExecutor.cs @@ -12,138 +12,95 @@ using Extensions.Internal; using ObjectPopulation; - /// - /// Provides options to execute a particular mapping from an object of the - /// type. - /// - /// - /// The type of sources object from which this will - /// perform mappings. - /// - public class MappingExecutor : + internal class MappingExecutor : ITargetSelector, IFlatteningSelector, IUnflatteningSelector, IMappingContext { - private MapperContext _mapperContext; private readonly TSource _source; - private MappingRuleSet _ruleSet; - - /// - /// Initializes a new instance of the class with the - /// given . - /// - /// The source object from which the mapping is to be performed. - protected internal MappingExecutor(TSource source) - : this(Mapper.Default.Context, source) - { - } - internal MappingExecutor(MapperContext mapperContext, TSource source) + public MappingExecutor(MapperContext mapperContext, TSource source) { - _mapperContext = mapperContext.ThrowIfDisposed(); + MapperContext = mapperContext.ThrowIfDisposed(); _source = source; } - MapperContext IMapperContextOwner.MapperContext => _mapperContext; + public MapperContext MapperContext { get; private set; } - MappingRuleSet IRuleSetOwner.RuleSet => _ruleSet; + public MappingRuleSet RuleSet { get; private set; } bool IMappingContext.IncludeCodeComments => false; - bool IMappingContext.IgnoreUnsuccessfulMemberPopulations => true; + public bool IgnoreUnsuccessfulMemberPopulations => true; - bool IMappingContext.LazyLoadRepeatMappingFuncs => true; + public bool LazyLoadRepeatMappingFuncs => true; #region ToANew Overloads - object ITargetSelector.ToANew(Type resultType) => ToANew(resultType); - - private object ToANew(Type resultType) => MappingExecutorBridge.CreateNew(resultType, this); + public object ToANew(Type resultType) => MappingExecutorBridge.CreateNew(resultType, this); - TResult ITargetSelector.ToANew() => ToANew(); + public TResult ToANew() => PerformMapping(MapperContext.RuleSets.CreateNew, default(TResult)); - private TResult ToANew() => PerformMapping(CreateNew, default(TResult)); - - TResult ITargetSelector.ToANew( - Expression>> configuration) + public TResult ToANew(Expression>> configuration) { return (configuration != null) - ? PerformMapping(CreateNew, default(TResult), new[] { configuration }) - : PerformMapping(CreateNew, default(TResult)); + ? PerformMapping(MapperContext.RuleSets.CreateNew, default(TResult), new[] { configuration }) + : PerformMapping(MapperContext.RuleSets.CreateNew, default(TResult)); } - TResult ITargetSelector.ToANew( + public TResult ToANew( params Expression>>[] configurations) - { - return ToANew(configurations); - } - - private TResult ToANew( - Expression>>[] configurations) { return configurations.Any() - ? PerformMapping(CreateNew, default(TResult), configurations) - : PerformMapping(CreateNew, default(TResult)); + ? PerformMapping(MapperContext.RuleSets.CreateNew, default(TResult), configurations) + : PerformMapping(MapperContext.RuleSets.CreateNew, default(TResult)); } - private MappingRuleSet CreateNew => _mapperContext.RuleSets.CreateNew; - #endregion #region OnTo Overloads - TTarget ITargetSelector.OnTo(TTarget existing) - => PerformMapping(Merge, existing); + public TTarget OnTo(TTarget existing) => PerformMapping(MapperContext.RuleSets.Merge, existing); - TTarget ITargetSelector.OnTo( - TTarget existing, - Expression>> configuration) + public TTarget OnTo(TTarget existing, Expression>> configuration) { return (configuration != null) - ? PerformMapping(Merge, existing, new[] { configuration }) - : PerformMapping(Merge, existing); + ? PerformMapping(MapperContext.RuleSets.Merge, existing, new[] { configuration }) + : PerformMapping(MapperContext.RuleSets.Merge, existing); } - TTarget ITargetSelector.OnTo( + public TTarget OnTo( TTarget existing, params Expression>>[] configurations) { return configurations.Any() - ? PerformMapping(Merge, existing, configurations) - : PerformMapping(Merge, existing); + ? PerformMapping(MapperContext.RuleSets.Merge, existing, configurations) + : PerformMapping(MapperContext.RuleSets.Merge, existing); } - private MappingRuleSet Merge => _mapperContext.RuleSets.Merge; - #endregion #region Over Overloads - TTarget ITargetSelector.Over(TTarget existing) - => PerformMapping(Overwrite, existing); + public TTarget Over(TTarget existing) => PerformMapping(MapperContext.RuleSets.Overwrite, existing); - TTarget ITargetSelector.Over( - TTarget existing, - Expression>> configuration) + public TTarget Over(TTarget existing, Expression>> configuration) { return (configuration != null) - ? PerformMapping(Overwrite, existing, new[] { configuration }) - : PerformMapping(Overwrite, existing); + ? PerformMapping(MapperContext.RuleSets.Overwrite, existing, new[] { configuration }) + : PerformMapping(MapperContext.RuleSets.Overwrite, existing); } - TTarget ITargetSelector.Over( + public TTarget Over( TTarget existing, params Expression>>[] configurations) { return configurations.Any() - ? PerformMapping(Overwrite, existing, configurations) - : PerformMapping(Overwrite, existing); + ? PerformMapping(MapperContext.RuleSets.Overwrite, existing, configurations) + : PerformMapping(MapperContext.RuleSets.Overwrite, existing); } - private MappingRuleSet Overwrite => _mapperContext.RuleSets.Overwrite; - #endregion private TTarget PerformMapping(MappingRuleSet ruleSet, TTarget target) @@ -153,7 +110,8 @@ private TTarget PerformMapping(MappingRuleSet ruleSet, TTarget target) return target; } - _ruleSet = ruleSet; + RuleSet = ruleSet; + return PerformMapping(target); } @@ -167,8 +125,9 @@ private TTarget PerformMapping( return target; } - _ruleSet = ruleSet; - _mapperContext = _mapperContext.InlineContexts.GetContextFor(configurations, this); + RuleSet = ruleSet; + MapperContext = MapperContext.InlineContexts.GetContextFor(configurations, this); + return PerformMapping(target); } @@ -189,26 +148,6 @@ private TTarget PerformMapping(TTarget target) return (TTarget)result; } - /// - /// Create an object for this - /// 's source object and the given - /// object, optionally building a Mapper for the types. - /// - /// The type of target object to which the mapping is being performed. - /// The target object to which the mapping is being performed. - /// Whether a Mapper should be created for the types being mapped. - /// - /// An object for this - /// 's source object and the given - /// object. - /// - protected IObjectMappingData CreateRootMappingData( - TTarget target, - bool createMapper) - { - return ObjectMappingDataFactory.ForRootFixedTypes(_source, target, this, createMapper); - } - #region IFlatteningSelector Members #if FEATURE_DYNAMIC @@ -218,7 +157,6 @@ dynamic IFlatteningSelector.ToDynamic( return configurations.Any() ? ToANew(configurations) : ToANew(); } #endif - Dictionary IFlatteningSelector.ToDictionary( params Expression>>>[] configurations) { From 36fddb920fa255caba0839ea88e03393ad27e713 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Tue, 11 May 2021 12:21:56 +0100 Subject: [PATCH 11/65] Handling circular references in built mapper source code --- .../WhenBuildingMapperSourceCode.cs | 14 ++ .../BuildableMapperConstants.cs | 17 ++ .../BuildableMapperExtensions.cs | 115 +++++------- AgileMapper.Buildable/BuildableMapperGroup.cs | 51 ++++++ .../BuildableMapperHelperExtensions.cs | 12 ++ .../RepeatMappingCallReplacer.cs | 171 ++++++++++++++++++ .../TestClasses/Child.cs | 4 +- .../TestClasses/Parent.cs | 4 +- AgileMapper/MappingExecutionContextBase.cs | 43 ++++- .../ObjectPopulation/IObjectMappingData.cs | 2 +- AgileMapper/Plans/IMappingPlanFunction.cs | 6 + .../RepeatedMappingMappingPlanFunction.cs | 2 + .../Plans/RootMapperMappingPlanFunction.cs | 2 + 13 files changed, 364 insertions(+), 79 deletions(-) create mode 100644 AgileMapper.Buildable/BuildableMapperConstants.cs create mode 100644 AgileMapper.Buildable/BuildableMapperGroup.cs create mode 100644 AgileMapper.Buildable/BuildableMapperHelperExtensions.cs create mode 100644 AgileMapper.Buildable/RepeatMappingCallReplacer.cs rename {AgileMapper.UnitTests => AgileMapper.UnitTests.Common}/TestClasses/Child.cs (54%) rename {AgileMapper.UnitTests => AgileMapper.UnitTests.Common}/TestClasses/Parent.cs (53%) diff --git a/AgileMapper.Buildable.UnitTests/WhenBuildingMapperSourceCode.cs b/AgileMapper.Buildable.UnitTests/WhenBuildingMapperSourceCode.cs index f16bdefeb..7246f6e6a 100644 --- a/AgileMapper.Buildable.UnitTests/WhenBuildingMapperSourceCode.cs +++ b/AgileMapper.Buildable.UnitTests/WhenBuildingMapperSourceCode.cs @@ -150,5 +150,19 @@ public void ShouldBuildASingleSourceSingleTargetOverwriteMapper() target.Line2.ShouldBe("1.2"); } } + + [Fact] + public void ShouldBuildACircularReferenceMapping() + { + using (var mapper = Mapper.CreateNew()) + { + mapper.GetPlanFor().ToANew(); + + var sourceCodeExpressions = mapper.BuildSourceCode(); + + var staticMapperClass = sourceCodeExpressions + .ShouldCompileAStaticMapperClass(); + } + } } } diff --git a/AgileMapper.Buildable/BuildableMapperConstants.cs b/AgileMapper.Buildable/BuildableMapperConstants.cs new file mode 100644 index 000000000..efd53d720 --- /dev/null +++ b/AgileMapper.Buildable/BuildableMapperConstants.cs @@ -0,0 +1,17 @@ +namespace AgileObjects.AgileMapper.Buildable +{ + using System; + using System.Reflection; + using NetStandardPolyfills; + + internal static class BuildableMapperConstants + { + public static readonly MethodInfo IsAssignableToMethod = typeof(TypeExtensionsPolyfill) + .GetPublicStaticMethod(nameof(TypeExtensionsPolyfill.IsAssignableTo)); + + public static readonly ConstructorInfo NotSupportedCtor = typeof(NotSupportedException) + .GetPublicInstanceConstructor(typeof(string)); + + public const string MapRepeated = nameof(MapRepeated); + } +} \ No newline at end of file diff --git a/AgileMapper.Buildable/BuildableMapperExtensions.cs b/AgileMapper.Buildable/BuildableMapperExtensions.cs index d91f18dd0..58a7f9fdd 100644 --- a/AgileMapper.Buildable/BuildableMapperExtensions.cs +++ b/AgileMapper.Buildable/BuildableMapperExtensions.cs @@ -4,7 +4,6 @@ using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; - using System.Reflection; using BuildableExpressions; using BuildableExpressions.SourceCode; using BuildableExpressions.SourceCode.Api; @@ -15,20 +14,13 @@ using static System.Linq.Expressions.Expression; using static BuildableExpressions.SourceCode.MemberVisibility; using PublicTypeExtensions = ReadableExpressions.Extensions.PublicTypeExtensions; + using static BuildableMapperConstants; /// /// Provides extension methods for building AgileMapper mapper source files. /// public static class BuildableMapperExtensions { - private static readonly Expression _doNotCreateMapper = Constant(false, typeof(bool)); - - private static readonly MethodInfo _isAssignableToMethod = typeof(TypeExtensionsPolyfill) - .GetPublicStaticMethod(nameof(TypeExtensionsPolyfill.IsAssignableTo)); - - private static readonly ConstructorInfo _notSupportedCtor = typeof(NotSupportedException) - .GetPublicInstanceConstructor(typeof(string)); - /// /// Builds s for the configured mappers in this /// . @@ -50,58 +42,59 @@ public static IEnumerable BuildSourceCode( var mapperClassGroups = mapper .GetPlansInCache() .GroupBy(plan => plan.Root.SourceType) - .Project(grp => new - { - SourceType = grp.Key, - MapperName = grp.Key.GetVariableNameInPascalCase() + "Mapper", - Plans = grp.ToList() - }) - .OrderBy(_ => _.MapperName); - - var mapperClassesBySourceType = new Dictionary(); + .Project(grp => new BuildableMapperGroup(grp.Key, grp.AsEnumerable())) + .OrderBy(grp => grp.MapperName) + .ToList(); foreach (var mapperGroup in mapperClassGroups) { - var sourceType = mapperGroup.SourceType; - - mapperClassesBySourceType.Add(sourceType, sourceCode.AddClass( + mapperGroup.MapperClass = sourceCode.AddClass( mapperGroup.MapperName, mapperClass => { - var baseType = typeof(MappingExecutionContextBase<>).MakeGenericType(sourceType); - mapperClass.SetBaseType(baseType); + mapperGroup.MapperInstance = mapperClass.ThisInstanceExpression; + + mapperClass.SetBaseType(mapperGroup.MapperBaseType); mapperClass.AddConstructor(ctor => { - var sourceParameter = ctor.AddParameter("source", sourceType); - ctor.SetConstructorCall( - baseType.GetNonPublicInstanceConstructor(sourceType), - sourceParameter); + mapperGroup.MapperBaseTypeConstructor, + ctor.AddParameter("source", mapperGroup.SourceType)); ctor.SetBody(Empty()); }); - foreach (var plan in mapperGroup.Plans) + foreach (var planAndMappingMethods in mapperGroup.MappingMethodsByPlan) { - mapperClass.AddMethod(plan.RuleSetName, doMapping => + var plan = planAndMappingMethods.Key; + var mappingMethods = planAndMappingMethods.Value; + + foreach (var repeatPlan in plan.Skip(1)) + { + mappingMethods.Add(mapperClass.AddMethod(MapRepeated, doMapping => + { + doMapping.SetVisibility(Private); + doMapping.SetStatic(); + doMapping.SetBody(repeatPlan.Mapping); + })); + } + + mappingMethods.Insert(0, mapperClass.AddMethod(plan.RuleSetName, doMapping => { doMapping.SetVisibility(Private); doMapping.SetStatic(); doMapping.SetBody(plan.Root.Mapping); - }); + })); } - var createMappingDataMethod = baseType - .GetNonPublicInstanceMethod("CreateRootMappingData"); + RepeatMappingCallReplacer.Replace(mapperGroup); - var allRuleSetMapMethodInfos = mapperClass.Type - .GetNonPublicStaticMethods() - .Project(m => new MapMethodInfo( - sourceType, - mapperClass, - createMappingDataMethod, - m)) + var allRuleSetMapMethodInfos = mapperGroup + .MappingMethodsByPlan.Values + .SelectMany(methods => methods) + .Filter(method => method.Name != MapRepeated) + .Project(method => new MapMethodInfo(mapperGroup, method)) .GroupBy(m => m.RuleSetName) .Select(methodGroup => methodGroup.ToList()); @@ -109,17 +102,17 @@ public static IEnumerable BuildSourceCode( { AddMapMethodsFor(mapperClass, ruleSetMapMethodInfos); } - })); + }); } sourceCode.AddClass("Mapper", staticMapperClass => { staticMapperClass.SetStatic(); - foreach (var sourceTypeAndMapper in mapperClassesBySourceType) + foreach (var mapperClassGroup in mapperClassGroups) { - var sourceType = sourceTypeAndMapper.Key; - var mapperClass = sourceTypeAndMapper.Value; + var sourceType = mapperClassGroup.SourceType; + var mapperClass = mapperClassGroup.MapperClass; staticMapperClass.AddMethod("Map", mapMethod => { @@ -195,7 +188,7 @@ private static void AddCreateNewMapMethod( var targetType = mapMethodInfo.TargetType; var typeofTargetType = BuildableExpression.TypeOf(targetType); - var typesAssignable = Call(_isAssignableToMethod, typeofTarget, typeofTargetType); + var typesAssignable = Call(IsAssignableToMethod, typeofTarget, typeofTargetType); var mapCall = mapMethodInfo.CreateMapCall(Default); var mapCallResultAsObject = Convert(mapCall, typeof(object)); @@ -243,7 +236,7 @@ private static Expression GetThrowTargetNotSupportedException( nullConfiguration), Constant("'", typeof(string))); - return Throw(New(_notSupportedCtor, getErrorMessageCall)); + return Throw(New(NotSupportedCtor, getErrorMessageCall)); } private static void AddUpdateInstanceMapMethod( @@ -265,42 +258,32 @@ private static void AddUpdateInstanceMapMethod( private class MapMethodInfo { - private readonly IClassExpressionConfigurator _mapperClass; - private readonly MethodInfo _createMappingDataMethod; - private readonly MethodInfo _mapMethod; + private readonly BuildableMapperGroup _mapperGroup; + private readonly MethodExpression _mapMethod; public MapMethodInfo( - Type sourceType, - IClassExpressionConfigurator mapperClass, - MethodInfo createMappingDataMethod, - MethodInfo mapMethod) + BuildableMapperGroup mapperGroup, + MethodExpression mapMethod) { - SourceType = sourceType; - _createMappingDataMethod = createMappingDataMethod; - _mapperClass = mapperClass; + _mapperGroup = mapperGroup; _mapMethod = mapMethod; - - TargetType = mapMethod - .GetParameters()[0] - .ParameterType - .GetGenericTypeArguments()[1]; + TargetType = mapMethod.GetTargetType(); } public string RuleSetName => _mapMethod.Name; - public Type SourceType { get; } + public Type SourceType => _mapperGroup.SourceType; public Type TargetType { get; } public Expression CreateMapCall(Func targetFactory) { var createMappingDataCall = Call( - _mapperClass.ThisInstanceExpression, - _createMappingDataMethod.MakeGenericMethod(TargetType), - targetFactory.Invoke(TargetType), - _doNotCreateMapper); + _mapperGroup.MapperInstance, + _mapperGroup.CreateRootMappingDataMethod.MakeGenericMethod(TargetType), + targetFactory.Invoke(TargetType)); - return Call(_mapMethod, createMappingDataCall); + return Call(_mapMethod.MethodInfo, createMappingDataCall); } } diff --git a/AgileMapper.Buildable/BuildableMapperGroup.cs b/AgileMapper.Buildable/BuildableMapperGroup.cs new file mode 100644 index 000000000..60a4a48fb --- /dev/null +++ b/AgileMapper.Buildable/BuildableMapperGroup.cs @@ -0,0 +1,51 @@ +namespace AgileObjects.AgileMapper.Buildable +{ + using System; + using System.Collections.Generic; + using System.Linq; + using System.Linq.Expressions; + using System.Reflection; + using BuildableExpressions.SourceCode; + using NetStandardPolyfills; + using Plans; + using ReadableExpressions.Extensions; + + internal class BuildableMapperGroup + { + private MethodInfo _createChildMappingDataMethod; + + public BuildableMapperGroup( + Type sourceType, + IEnumerable plans) + { + SourceType = sourceType; + MapperBaseType = typeof(MappingExecutionContextBase<>).MakeGenericType(sourceType); + MapperBaseTypeConstructor = MapperBaseType.GetNonPublicInstanceConstructor(sourceType); + CreateRootMappingDataMethod = MapperBaseType.GetNonPublicInstanceMethod("CreateRootMappingData"); + MapperName = sourceType.GetVariableNameInPascalCase() + "Mapper"; + MappingMethodsByPlan = plans.ToDictionary(p => p, p => new List()); + } + + public Type SourceType { get; } + + public Type MapperBaseType { get; } + + public ConstructorInfo MapperBaseTypeConstructor { get; } + + public MethodInfo CreateRootMappingDataMethod { get; } + + public MethodInfo CreateChildMappingDataMethod + => _createChildMappingDataMethod ??= MapperBaseType + .GetNonPublicInstanceMethod("CreateChildMappingData"); + + public string MapperName { get; } + + public Expression MapperInstance { get; set; } + + public ClassExpression MapperClass { get; set; } + + public ICollection Plans => MappingMethodsByPlan.Keys; + + public IDictionary> MappingMethodsByPlan { get; } + } +} \ No newline at end of file diff --git a/AgileMapper.Buildable/BuildableMapperHelperExtensions.cs b/AgileMapper.Buildable/BuildableMapperHelperExtensions.cs new file mode 100644 index 000000000..47724cdf1 --- /dev/null +++ b/AgileMapper.Buildable/BuildableMapperHelperExtensions.cs @@ -0,0 +1,12 @@ +namespace AgileObjects.AgileMapper.Buildable +{ + using System; + using BuildableExpressions.SourceCode; + using NetStandardPolyfills; + + internal static class BuildableMapperHelperExtensions + { + public static Type GetTargetType(this MethodExpression mapMethod) + => mapMethod.Parameters[0].Type.GetGenericTypeArguments()[1]; + } +} \ No newline at end of file diff --git a/AgileMapper.Buildable/RepeatMappingCallReplacer.cs b/AgileMapper.Buildable/RepeatMappingCallReplacer.cs new file mode 100644 index 000000000..38c689ca0 --- /dev/null +++ b/AgileMapper.Buildable/RepeatMappingCallReplacer.cs @@ -0,0 +1,171 @@ +namespace AgileObjects.AgileMapper.Buildable +{ + using System; + using System.Collections.Generic; + using System.Linq; + using System.Linq.Expressions; + using BuildableExpressions.SourceCode; + using Extensions; + using NetStandardPolyfills; + using static System.Linq.Expressions.Expression; + using static BuildableMapperConstants; + + internal class RepeatMappingCallReplacer : ExpressionVisitor + { + private readonly IDictionary _repeatMappingCallReplacements; + + private RepeatMappingCallReplacer( + IDictionary repeatMappingCallReplacements) + { + _repeatMappingCallReplacements = repeatMappingCallReplacements; + } + + public static void Replace(BuildableMapperGroup mapperGroup) + { + var mapMethodGroups = mapperGroup + .MappingMethodsByPlan.Values + .Filter(mapMethods => mapMethods.Count > 1) + .ToList(); + + if (mapMethodGroups.Count == 0) + { + return; + } + + foreach (var mapMethodGroup in mapMethodGroups) + { + var repeatMappingCallReplacements = MappingCallFinder + .GetRepeatMappingCallReplacements(mapperGroup, mapMethodGroup); + + var replacer = new RepeatMappingCallReplacer(repeatMappingCallReplacements); + + foreach (var mapMethod in mapMethodGroup) + { + replacer.ReplaceIn(mapMethod); + } + } + } + + private void ReplaceIn(MethodExpressionBase mapMethod) + { + mapMethod.Update(VisitAndConvert( + mapMethod.Body, + nameof(RepeatMappingCallReplacer))); + } + + protected override Expression VisitMethodCall(MethodCallExpression methodCall) + { + return _repeatMappingCallReplacements.TryGetValue(methodCall, out var replacement) + ? replacement + : methodCall; + } + + #region Helper Classes + + private class MappingCallFinder : ExpressionVisitor + { + private readonly BuildableMapperGroup _mapperGroup; + private readonly IDictionary _mapMethodsByMappingTypes; + private readonly IDictionary _mapRepeatedCallsByMappingTypes; + + private MappingCallFinder( + BuildableMapperGroup mapperGroup, + IEnumerable mapMethods) + { + _mapperGroup = mapperGroup; + + _mapRepeatedCallsByMappingTypes = new Dictionary(); + + _mapMethodsByMappingTypes = mapMethods + .Skip(1) + .ToDictionary(m => new MappingTypePair(m), MappingTypePair.Comparer); + } + + public static IDictionary GetRepeatMappingCallReplacements( + BuildableMapperGroup mapperGroup, + ICollection mapMethods) + { + var finder = new MappingCallFinder(mapperGroup, mapMethods); + + foreach (var mapMethod in mapMethods) + { + finder.Visit(mapMethod.Body); + } + + return finder._mapRepeatedCallsByMappingTypes; + } + + protected override Expression VisitMethodCall(MethodCallExpression methodCall) + { + if (methodCall.Method.Name != MapRepeated) + { + return methodCall; + } + + var sourceObject = methodCall.Arguments[0]; + var targetObject = methodCall.Arguments[1]; + var typePair = new MappingTypePair(sourceObject.Type, targetObject.Type); + + var mapRepeatedMethod = _mapMethodsByMappingTypes[typePair]; + + var createChildMappingDataMethod = _mapperGroup + .CreateChildMappingDataMethod + .MakeGenericMethod(typePair.SourceType, typePair.TargetType); + + var createMappingDataCall = Call( + _mapperGroup.MapperInstance, + createChildMappingDataMethod, + sourceObject, + targetObject, + methodCall.Object!); + + _mapRepeatedCallsByMappingTypes.Add( + methodCall, + Call(mapRepeatedMethod.MethodInfo, createMappingDataCall)); + + return methodCall; + } + } + + private class MappingTypePair + { + public static readonly IEqualityComparer Comparer = + new MappingTypePairComparer(); + + public MappingTypePair(MethodExpressionBase mapMethod) + { + var mappingTypes = mapMethod + .Parameters[0].Type + .GetGenericTypeArguments(); + + SourceType = mappingTypes[0]; + TargetType = mappingTypes[1]; + } + + public MappingTypePair(Type sourceType, Type targetType) + { + SourceType = sourceType; + TargetType = targetType; + } + + public Type SourceType { get; } + + public Type TargetType { get; } + + private class MappingTypePairComparer : IEqualityComparer + { + public bool Equals(MappingTypePair x, MappingTypePair y) + { + // ReSharper disable PossibleNullReferenceException + return x.SourceType == y.SourceType && + x.TargetType == y.TargetType; + // ReSharper restore PossibleNullReferenceException + } + + public int GetHashCode(MappingTypePair obj) => 0; + } + } + + #endregion + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests/TestClasses/Child.cs b/AgileMapper.UnitTests.Common/TestClasses/Child.cs similarity index 54% rename from AgileMapper.UnitTests/TestClasses/Child.cs rename to AgileMapper.UnitTests.Common/TestClasses/Child.cs index a48a58364..8f2f01fe0 100644 --- a/AgileMapper.UnitTests/TestClasses/Child.cs +++ b/AgileMapper.UnitTests.Common/TestClasses/Child.cs @@ -1,6 +1,6 @@ -namespace AgileObjects.AgileMapper.UnitTests.TestClasses +namespace AgileObjects.AgileMapper.UnitTests.Common.TestClasses { - internal class Child + public class Child { public string Name { get; set; } diff --git a/AgileMapper.UnitTests/TestClasses/Parent.cs b/AgileMapper.UnitTests.Common/TestClasses/Parent.cs similarity index 53% rename from AgileMapper.UnitTests/TestClasses/Parent.cs rename to AgileMapper.UnitTests.Common/TestClasses/Parent.cs index 1b59838c8..10824e794 100644 --- a/AgileMapper.UnitTests/TestClasses/Parent.cs +++ b/AgileMapper.UnitTests.Common/TestClasses/Parent.cs @@ -1,6 +1,6 @@ -namespace AgileObjects.AgileMapper.UnitTests.TestClasses +namespace AgileObjects.AgileMapper.UnitTests.Common.TestClasses { - internal class Parent + public class Parent { public string Name { get; set; } diff --git a/AgileMapper/MappingExecutionContextBase.cs b/AgileMapper/MappingExecutionContextBase.cs index 5ad6f86c8..3dc7e9f64 100644 --- a/AgileMapper/MappingExecutionContextBase.cs +++ b/AgileMapper/MappingExecutionContextBase.cs @@ -37,23 +37,50 @@ protected MappingExecutionContextBase(TSource source) bool IMappingContext.LazyLoadRepeatMappingFuncs => true; /// - /// Create an object for this + /// Creates a root object for this /// 's source object and the given - /// object, optionally building a Mapper for the types. + /// object. /// /// The type of target object to which the mapping is being performed. /// The target object to which the mapping is being performed. - /// Whether a Mapper should be created for the types being mapped. /// - /// An object for this + /// A root object for this /// 's source object and the given /// object. /// - protected IObjectMappingData CreateRootMappingData( - TTarget target, - bool createMapper) + protected IObjectMappingData CreateRootMappingData(TTarget target) + => ObjectMappingDataFactory.ForRootFixedTypes(_source, target, this, createMapper: false); + + /// + /// Creates an object for the given + /// and child objects. + /// + /// The type of source object from which the child mapping is being performed. + /// The type of target object to which the child mapping is being performed. + /// The child source object from which the mapping is being performed. + /// The child target object to which the mapping is being performed. + /// The mapping data parent object of the child object to create. + /// + /// A child object for the given + /// and objects. + /// + protected IObjectMappingData CreateChildMappingData( + TChildSource source, + TChildTarget target, + IObjectMappingDataUntyped parent) { - return ObjectMappingDataFactory.ForRootFixedTypes(_source, target, this, createMapper); + var parentMappingData = (IObjectMappingData)parent; + + var childMappingData = ObjectMappingDataFactory.ForChild( + source, + target, + parentMappingData.GetElementIndex(), + parentMappingData.GetElementKey(), + targetMemberRegistrationName: string.Empty, + dataSourceIndex: 0, + parentMappingData); + + return (IObjectMappingData)childMappingData; } } } \ No newline at end of file diff --git a/AgileMapper/ObjectPopulation/IObjectMappingData.cs b/AgileMapper/ObjectPopulation/IObjectMappingData.cs index 3389e251f..732d35766 100644 --- a/AgileMapper/ObjectPopulation/IObjectMappingData.cs +++ b/AgileMapper/ObjectPopulation/IObjectMappingData.cs @@ -101,7 +101,7 @@ TDeclaredTarget Map( /// applicable. /// /// - /// The key of the current Dicionary KeyValuePair being mapped in the mapping context described + /// The key of the current Dictionary KeyValuePair being mapped in the mapping context described /// by this , if applicable. /// /// The element mapping result. diff --git a/AgileMapper/Plans/IMappingPlanFunction.cs b/AgileMapper/Plans/IMappingPlanFunction.cs index f74f27175..97b3e25d9 100644 --- a/AgileMapper/Plans/IMappingPlanFunction.cs +++ b/AgileMapper/Plans/IMappingPlanFunction.cs @@ -14,6 +14,12 @@ /// public interface IMappingPlanFunction { + /// + /// Gets a value indicating whether this describes the + /// root mapping of its . + /// + bool IsRoot { get; } + /// /// Gets the source type from which this will perform a /// mapping. diff --git a/AgileMapper/Plans/RepeatedMappingMappingPlanFunction.cs b/AgileMapper/Plans/RepeatedMappingMappingPlanFunction.cs index a09a6df92..3f6a0de0d 100644 --- a/AgileMapper/Plans/RepeatedMappingMappingPlanFunction.cs +++ b/AgileMapper/Plans/RepeatedMappingMappingPlanFunction.cs @@ -20,6 +20,8 @@ public RepeatedMappingMappingPlanFunction(IRepeatedMapperFunc mapperFunc) _mapperFunc = mapperFunc; } + public bool IsRoot => false; + public Type SourceType => _mapperFunc.SourceType; public Type TargetType => _mapperFunc.TargetType; diff --git a/AgileMapper/Plans/RootMapperMappingPlanFunction.cs b/AgileMapper/Plans/RootMapperMappingPlanFunction.cs index 47addaa2a..d768cd865 100644 --- a/AgileMapper/Plans/RootMapperMappingPlanFunction.cs +++ b/AgileMapper/Plans/RootMapperMappingPlanFunction.cs @@ -24,6 +24,8 @@ public RootMapperMappingPlanFunction(IObjectMapper mapper) _mapping = mapper.GetMappingLambda(); } + public bool IsRoot => true; + public Type SourceType => _mapperData.SourceType; public Type TargetType => _mapperData.TargetType; From dfa2b980e11f8789fb72ac23a0d34bbcd852d033 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Tue, 11 May 2021 20:05:47 +0100 Subject: [PATCH 12/65] Support for built circular reference mappers --- .../WhenBuildingMapperSourceCode.cs | 44 +++++++++++++++++++ AgileMapper.Buildable/BuildableMapperGroup.cs | 2 +- .../RepeatMappingCallReplacer.cs | 1 - AgileMapper/MappingExecutionContextBase.cs | 2 +- 4 files changed, 46 insertions(+), 3 deletions(-) diff --git a/AgileMapper.Buildable.UnitTests/WhenBuildingMapperSourceCode.cs b/AgileMapper.Buildable.UnitTests/WhenBuildingMapperSourceCode.cs index 7246f6e6a..1d3d8ef5a 100644 --- a/AgileMapper.Buildable.UnitTests/WhenBuildingMapperSourceCode.cs +++ b/AgileMapper.Buildable.UnitTests/WhenBuildingMapperSourceCode.cs @@ -162,6 +162,50 @@ public void ShouldBuildACircularReferenceMapping() var staticMapperClass = sourceCodeExpressions .ShouldCompileAStaticMapperClass(); + + var staticMapMethod = staticMapperClass + .GetMapMethods() + .ShouldHaveSingleItem(); + + var source = new Child + { + Name = "Fred", + EldestParent = new Parent + { + Name = "Bonnie", + EldestChild = new Child + { + Name = "Samson", + EldestParent = new Parent + { + Name = "Franklin" + } + } + } + }; + + source.EldestParent.EldestChild.EldestParent.EldestChild = source; + + var executor = staticMapMethod + .ShouldCreateMappingExecutor(source); + + var result = executor + .ShouldHaveACreateNewMethod() + .ShouldExecuteACreateNewMapping(executor); + + result.ShouldNotBeNull().ShouldNotBeSameAs(source); + + result.Name.ShouldBe("Fred"); + result.EldestParent.ShouldNotBeNull(); + + result.EldestParent.Name.ShouldBe("Bonnie"); + result.EldestParent.EldestChild.ShouldNotBeNull(); + + result.EldestParent.EldestChild.Name.ShouldBe("Samson"); + result.EldestParent.EldestChild.EldestParent.ShouldNotBeNull(); + + result.EldestParent.EldestChild.EldestParent.Name.ShouldBe("Franklin"); + result.EldestParent.EldestChild.EldestParent.EldestChild.ShouldBeSameAs(result); } } } diff --git a/AgileMapper.Buildable/BuildableMapperGroup.cs b/AgileMapper.Buildable/BuildableMapperGroup.cs index 60a4a48fb..b6c4191f5 100644 --- a/AgileMapper.Buildable/BuildableMapperGroup.cs +++ b/AgileMapper.Buildable/BuildableMapperGroup.cs @@ -36,7 +36,7 @@ public BuildableMapperGroup( public MethodInfo CreateChildMappingDataMethod => _createChildMappingDataMethod ??= MapperBaseType - .GetNonPublicInstanceMethod("CreateChildMappingData"); + .GetNonPublicStaticMethod("CreateChildMappingData"); public string MapperName { get; } diff --git a/AgileMapper.Buildable/RepeatMappingCallReplacer.cs b/AgileMapper.Buildable/RepeatMappingCallReplacer.cs index 38c689ca0..fe0906ccb 100644 --- a/AgileMapper.Buildable/RepeatMappingCallReplacer.cs +++ b/AgileMapper.Buildable/RepeatMappingCallReplacer.cs @@ -113,7 +113,6 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCall) .MakeGenericMethod(typePair.SourceType, typePair.TargetType); var createMappingDataCall = Call( - _mapperGroup.MapperInstance, createChildMappingDataMethod, sourceObject, targetObject, diff --git a/AgileMapper/MappingExecutionContextBase.cs b/AgileMapper/MappingExecutionContextBase.cs index 3dc7e9f64..45d466d38 100644 --- a/AgileMapper/MappingExecutionContextBase.cs +++ b/AgileMapper/MappingExecutionContextBase.cs @@ -64,7 +64,7 @@ protected IObjectMappingData CreateRootMappingData(TT /// A child object for the given /// and objects. /// - protected IObjectMappingData CreateChildMappingData( + protected static IObjectMappingData CreateChildMappingData( TChildSource source, TChildTarget target, IObjectMappingDataUntyped parent) From 6e02732265be87e8ee39a4f939b60d9e781acded Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Wed, 12 May 2021 10:09:16 +0100 Subject: [PATCH 13/65] Moving reflection objects to constants class --- .../BuildableMapperConstants.cs | 18 ++++++++++++++++ .../BuildableMapperExtensions.cs | 21 +++---------------- 2 files changed, 21 insertions(+), 18 deletions(-) diff --git a/AgileMapper.Buildable/BuildableMapperConstants.cs b/AgileMapper.Buildable/BuildableMapperConstants.cs index efd53d720..f496b4a53 100644 --- a/AgileMapper.Buildable/BuildableMapperConstants.cs +++ b/AgileMapper.Buildable/BuildableMapperConstants.cs @@ -1,8 +1,11 @@ namespace AgileObjects.AgileMapper.Buildable { using System; + using System.Linq.Expressions; using System.Reflection; using NetStandardPolyfills; + using ReadableExpressions; + using ReadableExpressions.Extensions; internal static class BuildableMapperConstants { @@ -12,6 +15,21 @@ internal static class BuildableMapperConstants public static readonly ConstructorInfo NotSupportedCtor = typeof(NotSupportedException) .GetPublicInstanceConstructor(typeof(string)); + public static readonly MethodInfo StringConcatMethod = typeof(string).GetPublicStaticMethod( + nameof(string.Concat), + typeof(string), + typeof(string), + typeof(string)); + + public static readonly Expression NullConfiguration = + Expression.Default(typeof(Func)); + + public static readonly MethodInfo GetFriendlyNameMethod = typeof(PublicTypeExtensions) + .GetPublicStaticMethod( + nameof(PublicTypeExtensions.GetFriendlyName), + typeof(Type), + NullConfiguration.Type); + public const string MapRepeated = nameof(MapRepeated); } } \ No newline at end of file diff --git a/AgileMapper.Buildable/BuildableMapperExtensions.cs b/AgileMapper.Buildable/BuildableMapperExtensions.cs index 58a7f9fdd..dbb3ca982 100644 --- a/AgileMapper.Buildable/BuildableMapperExtensions.cs +++ b/AgileMapper.Buildable/BuildableMapperExtensions.cs @@ -9,11 +9,9 @@ using BuildableExpressions.SourceCode.Api; using Extensions; using NetStandardPolyfills; - using ReadableExpressions; using ReadableExpressions.Extensions; using static System.Linq.Expressions.Expression; using static BuildableExpressions.SourceCode.MemberVisibility; - using PublicTypeExtensions = ReadableExpressions.Extensions.PublicTypeExtensions; using static BuildableMapperConstants; /// @@ -210,30 +208,17 @@ private static Expression GetThrowTargetNotSupportedException( MapMethodInfo mapMethodInfo, TypeExpression targetGenericParameter) { - var stringConcatMethod = typeof(string).GetPublicStaticMethod( - nameof(string.Concat), - typeof(string), - typeof(string), - typeof(string)); - - var nullConfiguration = Default(typeof(Func)); - - var getFriendlyNameMethod = typeof(PublicTypeExtensions).GetPublicStaticMethod( - nameof(PublicTypeExtensions.GetFriendlyName), - typeof(Type), - nullConfiguration.Type); - var getErrorMessageCall = Call( - stringConcatMethod, + StringConcatMethod, Constant( $"Unable to perform a '{mapMethodInfo.RuleSetName}' mapping " + $"from source type '{mapMethodInfo.SourceType.GetFriendlyName()}' " + "to target type '", typeof(string)), Call( - getFriendlyNameMethod, + GetFriendlyNameMethod, BuildableExpression.TypeOf(targetGenericParameter), - nullConfiguration), + NullConfiguration), Constant("'", typeof(string))); return Throw(New(NotSupportedCtor, getErrorMessageCall)); From e97dce0bb62c1dc98b102df784a530dcb1018431 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Wed, 12 May 2021 10:21:30 +0100 Subject: [PATCH 14/65] Organising buildable test classes --- .../WhenBuildingCircularReferenceMappers.cs | 67 ++++++ ...WhenBuildingComplexTypeCreateNewMappers.cs | 92 ++++++++ .../WhenBuildingComplexTypeMergeMappers.cs | 40 ++++ ...WhenBuildingComplexTypeOverwriteMappers.cs | 40 ++++ .../WhenBuildingMapperSourceCode.cs | 212 ------------------ 5 files changed, 239 insertions(+), 212 deletions(-) create mode 100644 AgileMapper.Buildable.UnitTests/WhenBuildingCircularReferenceMappers.cs create mode 100644 AgileMapper.Buildable.UnitTests/WhenBuildingComplexTypeCreateNewMappers.cs create mode 100644 AgileMapper.Buildable.UnitTests/WhenBuildingComplexTypeMergeMappers.cs create mode 100644 AgileMapper.Buildable.UnitTests/WhenBuildingComplexTypeOverwriteMappers.cs delete mode 100644 AgileMapper.Buildable.UnitTests/WhenBuildingMapperSourceCode.cs diff --git a/AgileMapper.Buildable.UnitTests/WhenBuildingCircularReferenceMappers.cs b/AgileMapper.Buildable.UnitTests/WhenBuildingCircularReferenceMappers.cs new file mode 100644 index 000000000..14c65f136 --- /dev/null +++ b/AgileMapper.Buildable.UnitTests/WhenBuildingCircularReferenceMappers.cs @@ -0,0 +1,67 @@ +namespace AgileObjects.AgileMapper.Buildable.UnitTests +{ + using AgileMapper.UnitTests.Common; + using AgileMapper.UnitTests.Common.TestClasses; + using Xunit; + + public class WhenBuildingCircularReferenceMappers + { + [Fact] + public void ShouldBuildACircularReferenceMapper() + { + using (var mapper = Mapper.CreateNew()) + { + mapper.GetPlanFor().ToANew(); + + var sourceCodeExpressions = mapper.BuildSourceCode(); + + var staticMapperClass = sourceCodeExpressions + .ShouldCompileAStaticMapperClass(); + + var staticMapMethod = staticMapperClass + .GetMapMethods() + .ShouldHaveSingleItem(); + + var source = new Child + { + Name = "Fred", + EldestParent = new Parent + { + Name = "Bonnie", + EldestChild = new Child + { + Name = "Samson", + EldestParent = new Parent + { + Name = "Franklin" + } + } + } + }; + + source.EldestParent.EldestChild.EldestParent.EldestChild = source; + + var executor = staticMapMethod + .ShouldCreateMappingExecutor(source); + + var result = executor + .ShouldHaveACreateNewMethod() + .ShouldExecuteACreateNewMapping(executor); + + result.ShouldNotBeNull().ShouldNotBeSameAs(source); + + result.Name.ShouldBe("Fred"); + result.EldestParent.ShouldNotBeNull(); + + result.EldestParent.Name.ShouldBe("Bonnie"); + result.EldestParent.EldestChild.ShouldNotBeNull(); + + result.EldestParent.EldestChild.Name.ShouldBe("Samson"); + result.EldestParent.EldestChild.EldestParent.ShouldNotBeNull(); + + result.EldestParent.EldestChild.EldestParent.Name.ShouldBe("Franklin"); + result.EldestParent.EldestChild.EldestParent.EldestChild.ShouldBeSameAs(result); + } + } + } +} \ No newline at end of file diff --git a/AgileMapper.Buildable.UnitTests/WhenBuildingComplexTypeCreateNewMappers.cs b/AgileMapper.Buildable.UnitTests/WhenBuildingComplexTypeCreateNewMappers.cs new file mode 100644 index 000000000..f80ccb49a --- /dev/null +++ b/AgileMapper.Buildable.UnitTests/WhenBuildingComplexTypeCreateNewMappers.cs @@ -0,0 +1,92 @@ +namespace AgileObjects.AgileMapper.Buildable.UnitTests +{ + using System; + using System.Reflection; + using AgileMapper.UnitTests.Common; + using AgileMapper.UnitTests.Common.TestClasses; + using Xunit; + + public class WhenBuildingComplexTypeCreateNewMappers + { + [Fact] + public void ShouldBuildSingleSourceSingleTargetMapper() + { + using (var mapper = Mapper.CreateNew()) + { + mapper.GetPlanFor>().ToANew>(); + + var sourceCodeExpressions = mapper.BuildSourceCode(); + + var staticMapperClass = sourceCodeExpressions + .ShouldCompileAStaticMapperClass(); + + var staticMapMethod = staticMapperClass + .GetMapMethods() + .ShouldHaveSingleItem(); + + var source = new PublicField { Value = "123" }; + + var executor = staticMapMethod + .ShouldCreateMappingExecutor(source); + + var result = executor + .ShouldHaveACreateNewMethod() + .ShouldExecuteACreateNewMapping>(executor); + + result.Value.ShouldBe(123); + } + } + + [Fact] + public void ShouldBuildSingleSourceMultipleTargetMapper() + { + using (var mapper = Mapper.CreateNew()) + { + mapper.GetPlanFor>().ToANew>(); + mapper.GetPlanFor>().ToANew>(); + + var sourceCodeExpressions = mapper.BuildSourceCode(); + + var staticMapperClass = sourceCodeExpressions + .ShouldCompileAStaticMapperClass(); + + var staticMapMethod = staticMapperClass + .GetMapMethods() + .ShouldHaveSingleItem(); + + var source = new PublicField { Value = "456" }; + + var executor = staticMapMethod + .ShouldCreateMappingExecutor(source); + + var createNewMethod = executor.ShouldHaveACreateNewMethod(); + + var publicFieldResult = createNewMethod + .ShouldExecuteACreateNewMapping>(executor); + + publicFieldResult.Value.ShouldBe(456); + + var publicPropertyResult = createNewMethod + .ShouldExecuteACreateNewMapping>(executor); + + publicPropertyResult.Value.ShouldBe("456"); + + var configEx = Should.Throw(() => + { + createNewMethod + .ShouldExecuteACreateNewMapping>(executor); + }); + + var notSupportedMessage = configEx + .InnerException + .ShouldBeOfType() + .Message; + + notSupportedMessage.ShouldContain("Unable"); + notSupportedMessage.ShouldContain("CreateNew"); + notSupportedMessage.ShouldContain("source type 'PublicField'"); + notSupportedMessage.ShouldContain("target type 'PublicField'"); + } + } + } +} \ No newline at end of file diff --git a/AgileMapper.Buildable.UnitTests/WhenBuildingComplexTypeMergeMappers.cs b/AgileMapper.Buildable.UnitTests/WhenBuildingComplexTypeMergeMappers.cs new file mode 100644 index 000000000..84ba04c39 --- /dev/null +++ b/AgileMapper.Buildable.UnitTests/WhenBuildingComplexTypeMergeMappers.cs @@ -0,0 +1,40 @@ +namespace AgileObjects.AgileMapper.Buildable.UnitTests +{ + using AgileMapper.UnitTests.Common; + using AgileObjects.AgileMapper.UnitTests.Common.TestClasses; + using Xunit; + + public class WhenBuildingComplexTypeMergeMappers + { + [Fact] + public void ShouldBuildASingleSourceSingleTargetMapper() + { + using (var mapper = Mapper.CreateNew()) + { + mapper.GetPlanFor
().OnTo
(); + + var sourceCodeExpressions = mapper.BuildSourceCode(); + + var staticMapperClass = sourceCodeExpressions + .ShouldCompileAStaticMapperClass(); + + var staticMapMethod = staticMapperClass + .GetMapMethods() + .ShouldHaveSingleItem(); + + var source = new Address { Line1 = "Line 1!" }; + var target = new Address { Line2 = "Line 2!" }; + + var executor = staticMapMethod + .ShouldCreateMappingExecutor(source); + + executor + .ShouldHaveAMergeMethod() + .ShouldExecuteAMergeMapping(executor, target); + + target.Line1.ShouldBe("Line 1!"); + target.Line2.ShouldBe("Line 2!"); + } + } + } +} diff --git a/AgileMapper.Buildable.UnitTests/WhenBuildingComplexTypeOverwriteMappers.cs b/AgileMapper.Buildable.UnitTests/WhenBuildingComplexTypeOverwriteMappers.cs new file mode 100644 index 000000000..24a430b43 --- /dev/null +++ b/AgileMapper.Buildable.UnitTests/WhenBuildingComplexTypeOverwriteMappers.cs @@ -0,0 +1,40 @@ +namespace AgileObjects.AgileMapper.Buildable.UnitTests +{ + using AgileMapper.UnitTests.Common; + using AgileMapper.UnitTests.Common.TestClasses; + using Xunit; + + public class WhenBuildingComplexTypeOverwriteMappers + { + [Fact] + public void ShouldBuildASingleSourceSingleTargetMapper() + { + using (var mapper = Mapper.CreateNew()) + { + mapper.GetPlanFor
().Over
(); + + var sourceCodeExpressions = mapper.BuildSourceCode(); + + var staticMapperClass = sourceCodeExpressions + .ShouldCompileAStaticMapperClass(); + + var staticMapMethod = staticMapperClass + .GetMapMethods() + .ShouldHaveSingleItem(); + + var source = new Address { Line1 = "1.1", Line2 = "1.2" }; + var target = new Address { Line1 = "2.1", Line2 = "2.2" }; + + var executor = staticMapMethod + .ShouldCreateMappingExecutor(source); + + executor + .ShouldHaveAnOverwriteMethod() + .ShouldExecuteAnOverwriteMapping(executor, target); + + target.Line1.ShouldBe("1.1"); + target.Line2.ShouldBe("1.2"); + } + } + } +} \ No newline at end of file diff --git a/AgileMapper.Buildable.UnitTests/WhenBuildingMapperSourceCode.cs b/AgileMapper.Buildable.UnitTests/WhenBuildingMapperSourceCode.cs deleted file mode 100644 index 1d3d8ef5a..000000000 --- a/AgileMapper.Buildable.UnitTests/WhenBuildingMapperSourceCode.cs +++ /dev/null @@ -1,212 +0,0 @@ -namespace AgileObjects.AgileMapper.Buildable.UnitTests -{ - using System; - using System.Reflection; - using AgileMapper.UnitTests.Common; - using AgileObjects.AgileMapper.UnitTests.Common.TestClasses; - using Xunit; - - public class WhenBuildingMapperSourceCode - { - [Fact] - public void ShouldBuildSingleSourceSingleTargetCreateNewMapper() - { - using (var mapper = Mapper.CreateNew()) - { - mapper.GetPlanFor>().ToANew>(); - - var sourceCodeExpressions = mapper.BuildSourceCode(); - - var staticMapperClass = sourceCodeExpressions - .ShouldCompileAStaticMapperClass(); - - var staticMapMethod = staticMapperClass - .GetMapMethods() - .ShouldHaveSingleItem(); - - var source = new PublicField { Value = "123" }; - - var executor = staticMapMethod - .ShouldCreateMappingExecutor(source); - - var result = executor - .ShouldHaveACreateNewMethod() - .ShouldExecuteACreateNewMapping>(executor); - - result.Value.ShouldBe(123); - } - } - - [Fact] - public void ShouldBuildSingleSourceMultipleTargetCreateNewMapper() - { - using (var mapper = Mapper.CreateNew()) - { - mapper.GetPlanFor>().ToANew>(); - mapper.GetPlanFor>().ToANew>(); - - var sourceCodeExpressions = mapper.BuildSourceCode(); - - var staticMapperClass = sourceCodeExpressions - .ShouldCompileAStaticMapperClass(); - - var staticMapMethod = staticMapperClass - .GetMapMethods() - .ShouldHaveSingleItem(); - - var source = new PublicField { Value = "456" }; - - var executor = staticMapMethod - .ShouldCreateMappingExecutor(source); - - var createNewMethod = executor.ShouldHaveACreateNewMethod(); - - var publicFieldResult = createNewMethod - .ShouldExecuteACreateNewMapping>(executor); - - publicFieldResult.Value.ShouldBe(456); - - var publicPropertyResult = createNewMethod - .ShouldExecuteACreateNewMapping>(executor); - - publicPropertyResult.Value.ShouldBe("456"); - - var configEx = Should.Throw(() => - { - createNewMethod - .ShouldExecuteACreateNewMapping>(executor); - }); - - var notSupportedMessage = configEx - .InnerException - .ShouldBeOfType() - .Message; - - notSupportedMessage.ShouldContain("Unable"); - notSupportedMessage.ShouldContain("CreateNew"); - notSupportedMessage.ShouldContain("source type 'PublicField'"); - notSupportedMessage.ShouldContain("target type 'PublicField'"); - } - } - - [Fact] - public void ShouldBuildASingleSourceSingleTargetMergeMapper() - { - using (var mapper = Mapper.CreateNew()) - { - mapper.GetPlanFor
().OnTo
(); - - var sourceCodeExpressions = mapper.BuildSourceCode(); - - var staticMapperClass = sourceCodeExpressions - .ShouldCompileAStaticMapperClass(); - - var staticMapMethod = staticMapperClass - .GetMapMethods() - .ShouldHaveSingleItem(); - - var source = new Address { Line1 = "Line 1!" }; - var target = new Address { Line2 = "Line 2!" }; - - var executor = staticMapMethod - .ShouldCreateMappingExecutor(source); - - executor - .ShouldHaveAMergeMethod() - .ShouldExecuteAMergeMapping(executor, target); - - target.Line1.ShouldBe("Line 1!"); - target.Line2.ShouldBe("Line 2!"); - } - } - - [Fact] - public void ShouldBuildASingleSourceSingleTargetOverwriteMapper() - { - using (var mapper = Mapper.CreateNew()) - { - mapper.GetPlanFor
().Over
(); - - var sourceCodeExpressions = mapper.BuildSourceCode(); - - var staticMapperClass = sourceCodeExpressions - .ShouldCompileAStaticMapperClass(); - - var staticMapMethod = staticMapperClass - .GetMapMethods() - .ShouldHaveSingleItem(); - - var source = new Address { Line1 = "1.1", Line2 = "1.2" }; - var target = new Address { Line1 = "2.1", Line2 = "2.2" }; - - var executor = staticMapMethod - .ShouldCreateMappingExecutor(source); - - executor - .ShouldHaveAnOverwriteMethod() - .ShouldExecuteAnOverwriteMapping(executor, target); - - target.Line1.ShouldBe("1.1"); - target.Line2.ShouldBe("1.2"); - } - } - - [Fact] - public void ShouldBuildACircularReferenceMapping() - { - using (var mapper = Mapper.CreateNew()) - { - mapper.GetPlanFor().ToANew(); - - var sourceCodeExpressions = mapper.BuildSourceCode(); - - var staticMapperClass = sourceCodeExpressions - .ShouldCompileAStaticMapperClass(); - - var staticMapMethod = staticMapperClass - .GetMapMethods() - .ShouldHaveSingleItem(); - - var source = new Child - { - Name = "Fred", - EldestParent = new Parent - { - Name = "Bonnie", - EldestChild = new Child - { - Name = "Samson", - EldestParent = new Parent - { - Name = "Franklin" - } - } - } - }; - - source.EldestParent.EldestChild.EldestParent.EldestChild = source; - - var executor = staticMapMethod - .ShouldCreateMappingExecutor(source); - - var result = executor - .ShouldHaveACreateNewMethod() - .ShouldExecuteACreateNewMapping(executor); - - result.ShouldNotBeNull().ShouldNotBeSameAs(source); - - result.Name.ShouldBe("Fred"); - result.EldestParent.ShouldNotBeNull(); - - result.EldestParent.Name.ShouldBe("Bonnie"); - result.EldestParent.EldestChild.ShouldNotBeNull(); - - result.EldestParent.EldestChild.Name.ShouldBe("Samson"); - result.EldestParent.EldestChild.EldestParent.ShouldNotBeNull(); - - result.EldestParent.EldestChild.EldestParent.Name.ShouldBe("Franklin"); - result.EldestParent.EldestChild.EldestParent.EldestChild.ShouldBeSameAs(result); - } - } - } -} From 75499a913fba7ab8e3816f4fda91c203dcf2ee54 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Wed, 12 May 2021 11:36:43 +0100 Subject: [PATCH 15/65] Moving more test classes to Common / Skipping object resolution and derived target type existing value checks on non-readable target members / Buildable test coverage for simple derived type mapping --- .../WhenBuildingDerivedTypeMappers.cs | 64 ++++++++ .../TestClasses/MegaProduct.cs | 7 + .../TestClasses/Product.cs | 4 +- .../TestClasses/ProductDto.cs | 4 +- .../TestClasses/ProductDtoMega.cs | 7 + .../WhenViewingMappingPlans.cs | 1 + .../WhenConfiguringDerivedTypesInline.cs | 1 + .../WhenConfiguringObjectTrackingInline.cs | 29 ++-- .../Inline/WhenIgnoringMembersInline.cs | 1 + .../WhenConfiguringDerivedTypesIncorrectly.cs | 1 + .../WhenConfiguringEntityMapping.cs | 1 + .../WhenConfiguringObjectTracking.cs | 1 + ...henConfiguringObjectTrackingIncorrectly.cs | 142 +++++++++--------- ...WhenMappingFromDynamicsToNewEnumerables.cs | 1 + .../WhenCloningDictionarySettings.cs | 2 +- .../TestClasses/MegaProduct.cs | 7 - .../TestClasses/OrderItemDto.cs | 2 + .../TestClasses/ProductDtoMega.cs | 7 - .../WhenAnalysingCollections.cs | 1 + .../WhenMappingDerivedTypes.cs | 20 ++- .../DerivedComplexTypeDataSourcesFactory.cs | 24 ++- .../TargetObjectResolutionFactory.cs | 16 +- 22 files changed, 212 insertions(+), 131 deletions(-) create mode 100644 AgileMapper.Buildable.UnitTests/WhenBuildingDerivedTypeMappers.cs create mode 100644 AgileMapper.UnitTests.Common/TestClasses/MegaProduct.cs rename {AgileMapper.UnitTests => AgileMapper.UnitTests.Common}/TestClasses/Product.cs (67%) rename {AgileMapper.UnitTests => AgileMapper.UnitTests.Common}/TestClasses/ProductDto.cs (52%) create mode 100644 AgileMapper.UnitTests.Common/TestClasses/ProductDtoMega.cs delete mode 100644 AgileMapper.UnitTests/TestClasses/MegaProduct.cs delete mode 100644 AgileMapper.UnitTests/TestClasses/ProductDtoMega.cs diff --git a/AgileMapper.Buildable.UnitTests/WhenBuildingDerivedTypeMappers.cs b/AgileMapper.Buildable.UnitTests/WhenBuildingDerivedTypeMappers.cs new file mode 100644 index 000000000..b17970d4b --- /dev/null +++ b/AgileMapper.Buildable.UnitTests/WhenBuildingDerivedTypeMappers.cs @@ -0,0 +1,64 @@ +namespace AgileObjects.AgileMapper.Buildable.UnitTests +{ + using AgileMapper.UnitTests.Common; + using AgileMapper.UnitTests.Common.TestClasses; + using Xunit; + + public class WhenBuildingDerivedTypeMappers + { + [Fact] + public void ShouldBuildADerivedTypeCreateNewMapper() + { + using (var mapper = Mapper.CreateNew()) + { + mapper.WhenMapping + .From() + .To() + .Map() + .To(); + + string plan = mapper.GetPlanFor().ToANew(); + + var sourceCodeExpressions = mapper.BuildSourceCode(); + + var staticMapperClass = sourceCodeExpressions + .ShouldCompileAStaticMapperClass(); + + var staticMapMethod = staticMapperClass + .GetMapMethods() + .ShouldHaveSingleItem(); + + var baseTypeSource = new Product { ProductId = "111", Price = 19.99 }; + + var baseTypeExecutor = staticMapMethod + .ShouldCreateMappingExecutor(baseTypeSource); + + var baseTypeResult = baseTypeExecutor + .ShouldHaveACreateNewMethod() + .ShouldExecuteACreateNewMapping(baseTypeExecutor); + + baseTypeResult.ProductId.ShouldBe("111"); + baseTypeResult.Price.ShouldBe(19.99m); + + var derivedTypeSource = new MegaProduct + { + ProductId = "222", + Price = 119.99, + HowMega = 1.0m + }; + + var derivedTypeExecutor = staticMapMethod + .ShouldCreateMappingExecutor(derivedTypeSource); + + var derivedTypeResult = derivedTypeExecutor + .ShouldHaveACreateNewMethod() + .ShouldExecuteACreateNewMapping(derivedTypeExecutor) + .ShouldBeOfType(); + + derivedTypeResult.ProductId.ShouldBe("222"); + derivedTypeResult.Price.ShouldBe(119.99m); + derivedTypeResult.HowMega.ShouldBe("1.0"); + } + } + } +} diff --git a/AgileMapper.UnitTests.Common/TestClasses/MegaProduct.cs b/AgileMapper.UnitTests.Common/TestClasses/MegaProduct.cs new file mode 100644 index 000000000..4e6ac04b4 --- /dev/null +++ b/AgileMapper.UnitTests.Common/TestClasses/MegaProduct.cs @@ -0,0 +1,7 @@ +namespace AgileObjects.AgileMapper.UnitTests.Common.TestClasses +{ + public class MegaProduct : Product + { + public decimal HowMega { get; set; } + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests/TestClasses/Product.cs b/AgileMapper.UnitTests.Common/TestClasses/Product.cs similarity index 67% rename from AgileMapper.UnitTests/TestClasses/Product.cs rename to AgileMapper.UnitTests.Common/TestClasses/Product.cs index fd839862d..3ee92ab11 100644 --- a/AgileMapper.UnitTests/TestClasses/Product.cs +++ b/AgileMapper.UnitTests.Common/TestClasses/Product.cs @@ -1,6 +1,6 @@ -namespace AgileObjects.AgileMapper.UnitTests.TestClasses +namespace AgileObjects.AgileMapper.UnitTests.Common.TestClasses { - internal class Product + public class Product { public string ProductId { diff --git a/AgileMapper.UnitTests/TestClasses/ProductDto.cs b/AgileMapper.UnitTests.Common/TestClasses/ProductDto.cs similarity index 52% rename from AgileMapper.UnitTests/TestClasses/ProductDto.cs rename to AgileMapper.UnitTests.Common/TestClasses/ProductDto.cs index 4759b9384..503759a45 100644 --- a/AgileMapper.UnitTests/TestClasses/ProductDto.cs +++ b/AgileMapper.UnitTests.Common/TestClasses/ProductDto.cs @@ -1,6 +1,6 @@ -namespace AgileObjects.AgileMapper.UnitTests.TestClasses +namespace AgileObjects.AgileMapper.UnitTests.Common.TestClasses { - internal class ProductDto + public class ProductDto { public string ProductId { get; set; } diff --git a/AgileMapper.UnitTests.Common/TestClasses/ProductDtoMega.cs b/AgileMapper.UnitTests.Common/TestClasses/ProductDtoMega.cs new file mode 100644 index 000000000..0ab231a78 --- /dev/null +++ b/AgileMapper.UnitTests.Common/TestClasses/ProductDtoMega.cs @@ -0,0 +1,7 @@ +namespace AgileObjects.AgileMapper.UnitTests.Common.TestClasses +{ + public class ProductDtoMega : ProductDto + { + public string HowMega { get; set; } + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.NonParallel/WhenViewingMappingPlans.cs b/AgileMapper.UnitTests.NonParallel/WhenViewingMappingPlans.cs index c63383d46..887551531 100644 --- a/AgileMapper.UnitTests.NonParallel/WhenViewingMappingPlans.cs +++ b/AgileMapper.UnitTests.NonParallel/WhenViewingMappingPlans.cs @@ -2,6 +2,7 @@ { using AgileMapper.Extensions.Internal; using Common; + using Common.TestClasses; using TestClasses; using Xunit; diff --git a/AgileMapper.UnitTests/Configuration/Inline/WhenConfiguringDerivedTypesInline.cs b/AgileMapper.UnitTests/Configuration/Inline/WhenConfiguringDerivedTypesInline.cs index 405697830..e4eb54db1 100644 --- a/AgileMapper.UnitTests/Configuration/Inline/WhenConfiguringDerivedTypesInline.cs +++ b/AgileMapper.UnitTests/Configuration/Inline/WhenConfiguringDerivedTypesInline.cs @@ -2,6 +2,7 @@ { using System.Linq; using Common; + using Common.TestClasses; using TestClasses; #if !NET35 using Xunit; diff --git a/AgileMapper.UnitTests/Configuration/Inline/WhenConfiguringObjectTrackingInline.cs b/AgileMapper.UnitTests/Configuration/Inline/WhenConfiguringObjectTrackingInline.cs index 6883eede1..aa68256ca 100644 --- a/AgileMapper.UnitTests/Configuration/Inline/WhenConfiguringObjectTrackingInline.cs +++ b/AgileMapper.UnitTests/Configuration/Inline/WhenConfiguringObjectTrackingInline.cs @@ -4,6 +4,7 @@ using System.Linq; using AgileMapper.Configuration; using Common; + using Common.TestClasses; using TestClasses; #if !NET35 using Xunit; @@ -109,9 +110,9 @@ public void ShouldSupportInlineIdentityIntegrityWithGlobalDisabledObjectTracking [Fact] public void ShouldErrorIfIdentityIntegrityAndDisabledObjectTrackingConfiguredInline() { - using (var mapper = Mapper.CreateNew()) + var configEx = Should.Throw(() => { - var configEx = Should.Throw(() => + using (var mapper = Mapper.CreateNew()) { mapper.DeepClone( new[] { new Customer { Name = "BOOM" } }, @@ -119,20 +120,20 @@ public void ShouldErrorIfIdentityIntegrityAndDisabledObjectTrackingConfiguredInl .MaintainIdentityIntegrity() .And .DisableObjectTracking()); - }); + } + }); - configEx.Message.ShouldContain("Object tracking cannot be disabled"); - configEx.Message.ShouldContain("Customer[] -> Customer[]"); - configEx.Message.ShouldContain("with identity integrity configured"); - } + configEx.Message.ShouldContain("Object tracking cannot be disabled"); + configEx.Message.ShouldContain("Customer[] -> Customer[]"); + configEx.Message.ShouldContain("with identity integrity configured"); } [Fact] public void ShouldErrorIfDisabledObjectTrackingAndIdentityIntegrityConfiguredInline() { - using (var mapper = Mapper.CreateNew()) + var configEx = Should.Throw(() => { - var configEx = Should.Throw(() => + using (var mapper = Mapper.CreateNew()) { mapper.DeepClone( new[] { new Customer { Name = "BOOM" } }, @@ -140,12 +141,12 @@ public void ShouldErrorIfDisabledObjectTrackingAndIdentityIntegrityConfiguredInl .DisableObjectTracking() .And .MaintainIdentityIntegrity()); - }); + } + }); - configEx.Message.ShouldContain("Identity integrity cannot be configured"); - configEx.Message.ShouldContain("Customer[] -> Customer[]"); - configEx.Message.ShouldContain("with object tracking disabled"); - } + configEx.Message.ShouldContain("Identity integrity cannot be configured"); + configEx.Message.ShouldContain("Customer[] -> Customer[]"); + configEx.Message.ShouldContain("with object tracking disabled"); } } } diff --git a/AgileMapper.UnitTests/Configuration/Inline/WhenIgnoringMembersInline.cs b/AgileMapper.UnitTests/Configuration/Inline/WhenIgnoringMembersInline.cs index 77e82307d..b9ba73009 100644 --- a/AgileMapper.UnitTests/Configuration/Inline/WhenIgnoringMembersInline.cs +++ b/AgileMapper.UnitTests/Configuration/Inline/WhenIgnoringMembersInline.cs @@ -3,6 +3,7 @@ using System; using Api.Configuration; using Common; + using Common.TestClasses; using TestClasses; #if !NET35 using Xunit; diff --git a/AgileMapper.UnitTests/Configuration/WhenConfiguringDerivedTypesIncorrectly.cs b/AgileMapper.UnitTests/Configuration/WhenConfiguringDerivedTypesIncorrectly.cs index 45816b133..2b116e6b2 100644 --- a/AgileMapper.UnitTests/Configuration/WhenConfiguringDerivedTypesIncorrectly.cs +++ b/AgileMapper.UnitTests/Configuration/WhenConfiguringDerivedTypesIncorrectly.cs @@ -4,6 +4,7 @@ using System.Reflection; using AgileMapper.Configuration; using Common; + using Common.TestClasses; using TestClasses; using static WhenConfiguringDerivedTypes; #if !NET35 diff --git a/AgileMapper.UnitTests/Configuration/WhenConfiguringEntityMapping.cs b/AgileMapper.UnitTests/Configuration/WhenConfiguringEntityMapping.cs index c15b593b6..06c7b5ab3 100644 --- a/AgileMapper.UnitTests/Configuration/WhenConfiguringEntityMapping.cs +++ b/AgileMapper.UnitTests/Configuration/WhenConfiguringEntityMapping.cs @@ -3,6 +3,7 @@ using System; using AgileMapper.Configuration; using Common; + using Common.TestClasses; using TestClasses; #if !NET35 using Xunit; diff --git a/AgileMapper.UnitTests/Configuration/WhenConfiguringObjectTracking.cs b/AgileMapper.UnitTests/Configuration/WhenConfiguringObjectTracking.cs index 461c1ef1b..c6dd2a443 100644 --- a/AgileMapper.UnitTests/Configuration/WhenConfiguringObjectTracking.cs +++ b/AgileMapper.UnitTests/Configuration/WhenConfiguringObjectTracking.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; using Common; + using Common.TestClasses; using TestClasses; #if !NET35 using Xunit; diff --git a/AgileMapper.UnitTests/Configuration/WhenConfiguringObjectTrackingIncorrectly.cs b/AgileMapper.UnitTests/Configuration/WhenConfiguringObjectTrackingIncorrectly.cs index 6164042ae..e07d3a4f6 100644 --- a/AgileMapper.UnitTests/Configuration/WhenConfiguringObjectTrackingIncorrectly.cs +++ b/AgileMapper.UnitTests/Configuration/WhenConfiguringObjectTrackingIncorrectly.cs @@ -2,6 +2,7 @@ { using AgileMapper.Configuration; using Common; + using Common.TestClasses; using TestClasses; #if !NET35 using Xunit; @@ -15,157 +16,154 @@ public class WhenConfiguringObjectTrackingIncorrectly [Fact] public void ShouldErrorIfGlobalIdentityIntegrityConfiguredWithDisabledObjectTracking() { - using (var mapper = Mapper.CreateNew()) + var configEx = Should.Throw(() => { - mapper.WhenMapping.DisableObjectTracking(); - - var configEx = Should.Throw(() => + using (var mapper = Mapper.CreateNew()) { + mapper.WhenMapping.DisableObjectTracking(); + mapper.WhenMapping.MaintainIdentityIntegrity(); - }); + } + }); - configEx.Message.ShouldContain("Identity integrity cannot be configured"); - configEx.Message.ShouldContain("global object tracking disabled"); - } + configEx.Message.ShouldContain("Identity integrity cannot be configured"); + configEx.Message.ShouldContain("global object tracking disabled"); } [Fact] public void ShouldErrorIfGlobalIdentityIntegrityConfiguredTwice() { - using (var mapper = Mapper.CreateNew()) + var configEx = Should.Throw(() => { - mapper.WhenMapping.MaintainIdentityIntegrity(); - - var configEx = Should.Throw(() => + using (var mapper = Mapper.CreateNew()) { mapper.WhenMapping.MaintainIdentityIntegrity(); - }); + mapper.WhenMapping.MaintainIdentityIntegrity(); + } + }); - configEx.Message.ShouldContain("Identity integrity is already configured"); - } + configEx.Message.ShouldContain("Identity integrity is already configured"); } [Fact] public void ShouldErrorIfIdentityIntegrityConfiguredTwice() { - using (var mapper = Mapper.CreateNew()) + var configEx = Should.Throw(() => { - mapper.WhenMapping - .From() - .To() - .MaintainIdentityIntegrity(); - - var configEx = Should.Throw(() => + using (var mapper = Mapper.CreateNew()) { mapper.WhenMapping .From() .To() .MaintainIdentityIntegrity(); - }); - configEx.Message.ShouldContain("Identity integrity is already configured"); - configEx.Message.ShouldContain("Product -> ProductDto"); - } + mapper.WhenMapping + .From() + .To() + .MaintainIdentityIntegrity(); + } + }); + + configEx.Message.ShouldContain("Identity integrity is already configured"); + configEx.Message.ShouldContain("Product -> ProductDto"); } [Fact] public void ShouldErrorIfRedundantIdentityIntegrityConfigured() { - using (var mapper = Mapper.CreateNew()) + var configEx = Should.Throw(() => { - mapper.WhenMapping - .From() - .To() - .MaintainIdentityIntegrity(); - - var configEx = Should.Throw(() => + using (var mapper = Mapper.CreateNew()) { + mapper.WhenMapping + .From() + .To() + .MaintainIdentityIntegrity(); + mapper.WhenMapping .From() .To() .MaintainIdentityIntegrity(); - }); + } + }); - configEx.Message.ShouldContain("Identity integrity is already configured"); - configEx.Message.ShouldContain("Product -> ProductDto"); - } + configEx.Message.ShouldContain("Identity integrity is already configured"); + configEx.Message.ShouldContain("Product -> ProductDto"); } [Fact] public void ShouldErrorIfGlobalObjectTrackingDisabledWithIdentityIntegrityConfigured() { - using (var mapper = Mapper.CreateNew()) + var configEx = Should.Throw(() => { - mapper.WhenMapping.MaintainIdentityIntegrity(); - - var configEx = Should.Throw(() => + using (var mapper = Mapper.CreateNew()) { + mapper.WhenMapping.MaintainIdentityIntegrity(); mapper.WhenMapping.DisableObjectTracking(); - }); + } + }); - configEx.Message.ShouldContain("Object tracking cannot be disabled"); - configEx.Message.ShouldContain("global identity integrity configured"); - } + configEx.Message.ShouldContain("Object tracking cannot be disabled"); + configEx.Message.ShouldContain("global identity integrity configured"); } [Fact] public void ShouldErrorIfGlobalObjectTrackingDisabledTwice() { - using (var mapper = Mapper.CreateNew()) + var configEx = Should.Throw(() => { - mapper.WhenMapping.DisableObjectTracking(); - - var configEx = Should.Throw(() => + using (var mapper = Mapper.CreateNew()) { mapper.WhenMapping.DisableObjectTracking(); - }); + mapper.WhenMapping.DisableObjectTracking(); + } + }); - configEx.Message.ShouldContain("Object tracking is already disabled"); - } + configEx.Message.ShouldContain("Object tracking is already disabled"); } [Fact] public void ShouldErrorIfDuplicateObjectTrackingDisabled() { - using (var mapper = Mapper.CreateNew()) + var configEx = Should.Throw(() => { - mapper.WhenMapping - .From().To() - .DisableObjectTracking(); - - var configEx = Should.Throw(() => + using (var mapper = Mapper.CreateNew()) { + mapper.WhenMapping + .From().To() + .DisableObjectTracking(); + mapper.WhenMapping .From() .To() .DisableObjectTracking(); - }); + } + }); - configEx.Message.ShouldContain("Object tracking is already disabled"); - configEx.Message.ShouldContain("PersonViewModel -> Person"); - } + configEx.Message.ShouldContain("Object tracking is already disabled"); + configEx.Message.ShouldContain("PersonViewModel -> Person"); } [Fact] public void ShouldErrorIfRedundantObjectTrackingDisabled() { - using (var mapper = Mapper.CreateNew()) + var configEx = Should.Throw(() => { - mapper.WhenMapping - .To() - .DisableObjectTracking(); - - var configEx = Should.Throw(() => + using (var mapper = Mapper.CreateNew()) { + mapper.WhenMapping + .To() + .DisableObjectTracking(); + mapper.WhenMapping .From() .To() .DisableObjectTracking(); - }); + } + }); - configEx.Message.ShouldContain("Object tracking is already disabled"); - configEx.Message.ShouldContain("to Person"); - } + configEx.Message.ShouldContain("Object tracking is already disabled"); + configEx.Message.ShouldContain("to Person"); } } } diff --git a/AgileMapper.UnitTests/Dynamics/WhenMappingFromDynamicsToNewEnumerables.cs b/AgileMapper.UnitTests/Dynamics/WhenMappingFromDynamicsToNewEnumerables.cs index 5b214bc3e..a7f5e9d03 100644 --- a/AgileMapper.UnitTests/Dynamics/WhenMappingFromDynamicsToNewEnumerables.cs +++ b/AgileMapper.UnitTests/Dynamics/WhenMappingFromDynamicsToNewEnumerables.cs @@ -5,6 +5,7 @@ namespace AgileObjects.AgileMapper.UnitTests.Dynamics using System.Collections.Generic; using System.Collections.ObjectModel; using System.Dynamic; + using Common.TestClasses; using TestClasses; using Xunit; diff --git a/AgileMapper.UnitTests/MapperCloning/WhenCloningDictionarySettings.cs b/AgileMapper.UnitTests/MapperCloning/WhenCloningDictionarySettings.cs index 347f8c936..00ac63fbf 100644 --- a/AgileMapper.UnitTests/MapperCloning/WhenCloningDictionarySettings.cs +++ b/AgileMapper.UnitTests/MapperCloning/WhenCloningDictionarySettings.cs @@ -2,7 +2,7 @@ { using System.Collections.Generic; using Common; - using TestClasses; + using Common.TestClasses; #if !NET35 using Xunit; #else diff --git a/AgileMapper.UnitTests/TestClasses/MegaProduct.cs b/AgileMapper.UnitTests/TestClasses/MegaProduct.cs deleted file mode 100644 index 5061258e4..000000000 --- a/AgileMapper.UnitTests/TestClasses/MegaProduct.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace AgileObjects.AgileMapper.UnitTests.TestClasses -{ - internal class MegaProduct : Product - { - public decimal HowMega { get; set; } - } -} \ No newline at end of file diff --git a/AgileMapper.UnitTests/TestClasses/OrderItemDto.cs b/AgileMapper.UnitTests/TestClasses/OrderItemDto.cs index c73b9af1a..ec2e154ff 100644 --- a/AgileMapper.UnitTests/TestClasses/OrderItemDto.cs +++ b/AgileMapper.UnitTests/TestClasses/OrderItemDto.cs @@ -1,5 +1,7 @@ namespace AgileObjects.AgileMapper.UnitTests.TestClasses { + using Common.TestClasses; + internal class OrderItemDto : DtoBase { public int OrderId { get; set; } diff --git a/AgileMapper.UnitTests/TestClasses/ProductDtoMega.cs b/AgileMapper.UnitTests/TestClasses/ProductDtoMega.cs deleted file mode 100644 index 947b7bc7c..000000000 --- a/AgileMapper.UnitTests/TestClasses/ProductDtoMega.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace AgileObjects.AgileMapper.UnitTests.TestClasses -{ - internal class ProductDtoMega : ProductDto - { - public string HowMega { get; set; } - } -} \ No newline at end of file diff --git a/AgileMapper.UnitTests/WhenAnalysingCollections.cs b/AgileMapper.UnitTests/WhenAnalysingCollections.cs index 1aef249ee..fcb7db784 100644 --- a/AgileMapper.UnitTests/WhenAnalysingCollections.cs +++ b/AgileMapper.UnitTests/WhenAnalysingCollections.cs @@ -4,6 +4,7 @@ using System.Linq; using AgileMapper.Extensions; using Common; + using Common.TestClasses; using TestClasses; #if !NET35 using Xunit; diff --git a/AgileMapper.UnitTests/WhenMappingDerivedTypes.cs b/AgileMapper.UnitTests/WhenMappingDerivedTypes.cs index 754e2498c..f27dd9ab8 100644 --- a/AgileMapper.UnitTests/WhenMappingDerivedTypes.cs +++ b/AgileMapper.UnitTests/WhenMappingDerivedTypes.cs @@ -18,7 +18,7 @@ public class WhenMappingDerivedTypes { [Fact] - public void ShouldMapARootComplexTypeFromItsAssignedType() + public void ShouldMapARootComplexTypeFromItsRuntimeType() { object source = new Product { Price = 100.00 }; var result = Mapper.Map(source).ToANew(); @@ -27,7 +27,7 @@ public void ShouldMapARootComplexTypeFromItsAssignedType() } [Fact] - public void ShouldMapARootComplexTypeEnumerableFromItsAssignedType() + public void ShouldMapARootComplexTypeEnumerableFromItsRuntimeType() { object source = new[] { new Product { Price = 10.01 } }; var result = Mapper.Map(source).ToANew>(); @@ -36,7 +36,7 @@ public void ShouldMapARootComplexTypeEnumerableFromItsAssignedType() } [Fact] - public void ShouldMapARootComplexTypeEnumerableElementFromItsAssignedType() + public void ShouldMapARootComplexTypeEnumerableElementFromItsRuntimeType() { var source = new object[] { new Product { Price = 9.99 } }; var result = Mapper.Map(source).ToANew>(); @@ -45,7 +45,7 @@ public void ShouldMapARootComplexTypeEnumerableElementFromItsAssignedType() } [Fact] - public void ShouldMapAComplexTypeMemberFromItsAssignedType() + public void ShouldMapAComplexTypeMemberFromItsRuntimeType() { var source = new PublicProperty { @@ -60,7 +60,7 @@ public void ShouldMapAComplexTypeMemberFromItsAssignedType() } [Fact] - public void ShouldMapAComplexTypeMemberInACollectionFromItsAssignedType() + public void ShouldMapAComplexTypeMemberInACollectionFromItsRuntimeType() { var sourceObjectId = Guid.NewGuid(); @@ -113,14 +113,13 @@ public void ShouldMapDerivedTypesFromNestedMembers() .Map() .To(); - var personSource = new PublicField + var productSource = new PublicField { Value = new MegaProduct { HowMega = 1.10m } }; - var result = mapper.Map(personSource).ToANew>(); + var result = mapper.Map(productSource).ToANew>(); - result.Value.ShouldBeOfType(); - ((ProductDtoMega)result.Value).HowMega.ShouldBe("1.10"); + result.Value.ShouldBeOfType().HowMega.ShouldBe("1.10"); } } @@ -144,8 +143,7 @@ public void ShouldConditionallyMapDerivedTypesFromNestedMembers() }; var result = mapper.Map(mysteryCustomerSource).ToANew>(); - result.Value.ShouldBeOfType(); - ((MysteryCustomerViewModel)result.Value).Discount.ShouldBe(0.5); + result.Value.ShouldBeOfType().Discount.ShouldBe(0.5); var customerSource = new PublicField { diff --git a/AgileMapper/DataSources/Factories/DerivedComplexTypeDataSourcesFactory.cs b/AgileMapper/DataSources/Factories/DerivedComplexTypeDataSourcesFactory.cs index 350ada91d..140fc871b 100644 --- a/AgileMapper/DataSources/Factories/DerivedComplexTypeDataSourcesFactory.cs +++ b/AgileMapper/DataSources/Factories/DerivedComplexTypeDataSourcesFactory.cs @@ -263,7 +263,7 @@ private static void AddDerivedSourceTypeDataSources( groupedTypePairs, targetType); - AddDataSourceIfValid: + AddDataSourceIfValid: if (sourceVariableIsDerivedTypeDataSource.IsValid) { derivedTypeDataSources.Insert(sourceVariableIsDerivedTypeDataSource, insertionOffset); @@ -450,18 +450,25 @@ private static IList GetTypePairsFor( } private static void AddDerivedTargetTypeDataSources( - IEnumerable derivedTargetTypes, + ICollection derivedTargetTypes, IObjectMappingData declaredTypeMappingData, ICollection derivedTypeDataSources) { var declaredTypeMapperData = declaredTypeMappingData.MapperData; - if (((ICollection)derivedTargetTypes).Count > 1) + if (declaredTypeMapperData.TargetMemberNullOrInaccessible()) { - derivedTargetTypes = derivedTargetTypes.OrderBy(t => t, MostToLeastDerived); + return; } - foreach (var derivedTargetType in derivedTargetTypes) + IEnumerable orderedDerivedTargetTypes = derivedTargetTypes; + + if (derivedTargetTypes.Count > 1) + { + orderedDerivedTargetTypes = orderedDerivedTargetTypes.OrderBy(t => t, MostToLeastDerived); + } + + foreach (var derivedTargetType in orderedDerivedTargetTypes) { var targetTypeCondition = declaredTypeMapperData.GetTargetIsDerivedTypeCheck(derivedTargetType); @@ -524,7 +531,7 @@ private static Expression GetReturnMappingResultExpression( private static Expression GetTargetValidCheckOrNull(this IMemberMapperData mapperData, Type targetType) { - if (!mapperData.TargetMember.IsReadable || mapperData.TargetIsDefinitelyUnpopulated()) + if (mapperData.TargetMemberNullOrInaccessible()) { return null; } @@ -542,6 +549,9 @@ private static Expression GetTargetValidCheckOrNull(this IMemberMapperData mappe return targetIsValid; } + private static bool TargetMemberNullOrInaccessible(this IMemberMapperData mapperData) + => !mapperData.TargetMember.IsReadable || mapperData.TargetIsDefinitelyUnpopulated(); + private static Expression GetTargetIsDerivedTypeCheck(this IMemberMapperData mapperData, Type targetType) => TypeIs(mapperData.TargetObject, targetType); @@ -553,7 +563,7 @@ public DerivedComplexTypeDataSource( IQualifiedMember sourceMember, Expression condition, Expression value) - : base(sourceMember, Constants.EmptyParameters, value, condition) + : base(sourceMember, EmptyParameters, value, condition) { } diff --git a/AgileMapper/ObjectPopulation/ComplexTypes/TargetObjectResolutionFactory.cs b/AgileMapper/ObjectPopulation/ComplexTypes/TargetObjectResolutionFactory.cs index bd77a497e..53b752165 100644 --- a/AgileMapper/ObjectPopulation/ComplexTypes/TargetObjectResolutionFactory.cs +++ b/AgileMapper/ObjectPopulation/ComplexTypes/TargetObjectResolutionFactory.cs @@ -19,7 +19,7 @@ public static Expression GetObjectResolution( bool assignTargetObject = false) { return GetObjectResolution( - (md, mps) => construction, + (_, _) => construction, mappingData, null, false, @@ -75,8 +75,7 @@ public static Expression GetObjectResolution( } } - objectValue = AddExistingTargetCheckIfAppropriate(objectValue, mappingData); - + objectValue = AddExistingTargetCheckIfAppropriate(objectValue, mapperData); return objectValue; } @@ -118,17 +117,18 @@ private static bool UseNullFallbackValue( private static bool MemberPopulationsExist(IList populationsAndCallbacks) => populationsAndCallbacks.Any(population => population.NodeType != ExpressionType.Constant); - private static Expression AddExistingTargetCheckIfAppropriate(Expression value, IObjectMappingData mappingData) + private static Expression AddExistingTargetCheckIfAppropriate(Expression value, IMemberMapperData mapperData) { if ((value.NodeType == ExpressionType.Default) || - mappingData.MapperData.RuleSet.Settings.UseSingleRootMappingExpression || - mappingData.MapperData.TargetMemberIsUserStruct() || - mappingData.MapperData.TargetIsDefinitelyUnpopulated()) + mapperData.RuleSet.Settings.UseSingleRootMappingExpression || + !mapperData.TargetMember.IsReadable || + mapperData.TargetMemberIsUserStruct() || + mapperData.TargetIsDefinitelyUnpopulated()) { return value; } - return Expression.Coalesce(mappingData.MapperData.TargetObject, value); + return Expression.Coalesce(mapperData.TargetObject, value); } } } \ No newline at end of file From cec99408afbcdc7359119f40f973ca9e18fc641a Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Wed, 12 May 2021 11:46:12 +0100 Subject: [PATCH 16/65] Extending test coverage --- .../WhenBuildingDerivedTypeMappers.cs | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/AgileMapper.Buildable.UnitTests/WhenBuildingDerivedTypeMappers.cs b/AgileMapper.Buildable.UnitTests/WhenBuildingDerivedTypeMappers.cs index b17970d4b..e795ccd23 100644 --- a/AgileMapper.Buildable.UnitTests/WhenBuildingDerivedTypeMappers.cs +++ b/AgileMapper.Buildable.UnitTests/WhenBuildingDerivedTypeMappers.cs @@ -17,7 +17,7 @@ public void ShouldBuildADerivedTypeCreateNewMapper() .Map() .To(); - string plan = mapper.GetPlanFor().ToANew(); + mapper.GetPlanFor().ToANew(); var sourceCodeExpressions = mapper.BuildSourceCode(); @@ -50,14 +50,22 @@ public void ShouldBuildADerivedTypeCreateNewMapper() var derivedTypeExecutor = staticMapMethod .ShouldCreateMappingExecutor(derivedTypeSource); - var derivedTypeResult = derivedTypeExecutor + var derivedTypeBaseTypeResult = derivedTypeExecutor .ShouldHaveACreateNewMethod() .ShouldExecuteACreateNewMapping(derivedTypeExecutor) .ShouldBeOfType(); - derivedTypeResult.ProductId.ShouldBe("222"); - derivedTypeResult.Price.ShouldBe(119.99m); - derivedTypeResult.HowMega.ShouldBe("1.0"); + derivedTypeBaseTypeResult.ProductId.ShouldBe("222"); + derivedTypeBaseTypeResult.Price.ShouldBe(119.99m); + derivedTypeBaseTypeResult.HowMega.ShouldBe("1.0"); + + var derivedTypeDerivedTypeResult = derivedTypeExecutor + .ShouldHaveACreateNewMethod() + .ShouldExecuteACreateNewMapping(derivedTypeExecutor); + + derivedTypeDerivedTypeResult.ProductId.ShouldBe("222"); + derivedTypeDerivedTypeResult.Price.ShouldBe(119.99m); + derivedTypeDerivedTypeResult.HowMega.ShouldBe("1.0"); } } } From 655e3a341e6c24df1dd508bba8b5c37cdb2accb0 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Wed, 12 May 2021 12:00:41 +0100 Subject: [PATCH 17/65] Using Array.Empty() where framework-supported --- AgileMapper/AgileMapper.csproj | 4 ++-- AgileMapper/Enumerable.cs | 10 +++++++++- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/AgileMapper/AgileMapper.csproj b/AgileMapper/AgileMapper.csproj index 43017fa08..3d7d55022 100644 --- a/AgileMapper/AgileMapper.csproj +++ b/AgileMapper/AgileMapper.csproj @@ -72,11 +72,11 @@ - $(DefineConstants);FEATURE_SERIALIZATION;FEATURE_DYNAMIC;FEATURE_DYNAMIC_ROOT_SOURCE;FEATURE_ISET + $(DefineConstants);FEATURE_SERIALIZATION;FEATURE_DYNAMIC;FEATURE_DYNAMIC_ROOT_SOURCE;FEATURE_ISET;FEATURE_ARRAY_EMPTY - $(DefineConstants);FEATURE_SERIALIZATION;FEATURE_DYNAMIC;FEATURE_DYNAMIC_ROOT_SOURCE;FEATURE_ISET;FEATURE_APPDOMAIN;FEATURE_ASSEMBLY_TRUST + $(DefineConstants);FEATURE_SERIALIZATION;FEATURE_DYNAMIC;FEATURE_DYNAMIC_ROOT_SOURCE;FEATURE_ISET;FEATURE_APPDOMAIN;FEATURE_ASSEMBLY_TRUST;FEATURE_ARRAY_EMPTY diff --git a/AgileMapper/Enumerable.cs b/AgileMapper/Enumerable.cs index 867094337..197674015 100644 --- a/AgileMapper/Enumerable.cs +++ b/AgileMapper/Enumerable.cs @@ -1,5 +1,8 @@ namespace AgileObjects.AgileMapper { +#if FEATURE_ARRAY_EMPTY + using System; +#endif using System.Collections.Generic; /// @@ -11,7 +14,12 @@ public static class Enumerable /// /// Gets a singleton empty array instance. /// - public static readonly TElement[] EmptyArray = { }; + public static readonly TElement[] EmptyArray = +#if FEATURE_ARRAY_EMPTY + Array.Empty(); +#else + { }; +#endif /// /// Gets a singleton empty array instance. From 0577c74ccc7ac717e44428c8b3d97912151d9a68 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Wed, 12 May 2021 15:29:07 +0100 Subject: [PATCH 18/65] Moving qualified member naming methods into extension methods / Support for deferring mapper Func compilation --- .../WhenBuildingDerivedTypeMappers.cs | 11 ++-- .../WhenApplyingMapperConfigurations.cs | 7 +- ...tteningToQueryStringViaExtensionMethods.cs | 1 - .../Members/MemberTestsBase.cs | 1 + ...henCreatingTargetMembersFromExpressions.cs | 1 + .../Members/WhenDeterminingRecursion.cs | 1 + .../ConfigInfoDataSourceExtensions.cs | 2 +- .../CustomDataSourceTargetMemberSpecifier.cs | 1 + .../TargetDictionaryMappingConfigurator.cs | 1 + .../TargetDynamicMappingConfigurator.cs | 1 + .../EventConfigStartingPointBase.cs | 1 + .../Api/IPlanTargetAndRuleSetSelector.cs | 46 +++++++++---- AgileMapper/Api/PlanTargetSelector.cs | 65 ++++++++++++------- .../ConfiguredDataSourceFactory.cs | 1 + .../ConfiguredDataSourceFactoryBase.cs | 1 + .../Dictionaries/CustomDictionaryKey.cs | 5 +- .../Lambdas/ConfiguredLambdaInfo.cs | 5 +- .../ConfiguredSourceMemberIgnore.cs | 1 + .../SourceValueFilters/FilterCondition.cs | 3 +- .../Configuration/UserConfiguredItemBase.cs | 1 + .../DataSources/ComplexTypeDataSource.cs | 1 + .../ConfiguredMappingDataSource.cs | 1 + AgileMapper/DataSources/DataSourceBase.cs | 1 + .../DataSourceFilteringExtensions.cs | 1 + AgileMapper/DataSources/DataSourceSet.cs | 2 +- .../DictionaryNonSimpleMemberDataSource.cs | 1 + .../Optimisation/MultiInvocationsFinder.cs | 2 +- AgileMapper/IMappingContext.cs | 8 +-- AgileMapper/MappingException.cs | 7 +- AgileMapper/MappingExecutionContextBase.cs | 9 +-- AgileMapper/MappingExecutor.cs | 7 +- AgileMapper/Members/ConfiguredSourceMember.cs | 5 +- .../DictionaryEntrySourceMember.cs | 5 +- .../DictionaryMemberMapperDataExtensions.cs | 7 +- .../Dictionaries/DictionarySourceMember.cs | 12 ++-- .../Dictionaries/DictionaryTargetMember.cs | 10 ++- .../MemberExpressionExtensions.cs | 2 +- .../MemberExtensionMethods.cs | 27 +++++--- .../NestedAccessChecksFactory.cs | 6 +- AgileMapper/Members/IQualifiedMember.cs | 2 - .../Members/IQualifiedMemberWrapper.cs | 7 ++ AgileMapper/Members/Member.cs | 3 +- AgileMapper/Members/MemberCache.cs | 4 +- AgileMapper/Members/MemberIdentifierSet.cs | 7 +- .../Members/MemberMapperDataExtensions.cs | 4 +- AgileMapper/Members/NamingSettings.cs | 4 +- .../Members/Population/MemberPopulator.cs | 2 +- .../Population/MemberPopulatorFactory.cs | 6 +- .../Members/Population/NullMemberPopulator.cs | 4 +- AgileMapper/Members/QualifiedMember.cs | 11 ++-- AgileMapper/Members/SourceMemberMatch.cs | 5 +- .../Members/SourceMemberMatchContext.cs | 2 +- AgileMapper/Members/SourceMemberMatcher.cs | 2 +- .../Members/Sources/ElementMembersSource.cs | 1 + .../DictionaryPopulationBuilder.cs | 1 + .../EnumerableMappingExpressionFactory.cs | 1 + .../MappingExpressionFactoryBase.cs | 9 ++- .../ObjectPopulation/MappingFactory.cs | 2 +- AgileMapper/ObjectPopulation/ObjectMapper.cs | 35 +++++++++- .../ObjectPopulation/ObjectMapperData.cs | 1 + .../ObjectPopulation/ObjectMappingData.cs | 1 + .../ObjectMappingDataFactory.cs | 1 + .../SimpleMemberMapperData.cs | 1 + AgileMapper/Plans/MappingPlanSettings.cs | 49 ++++++++++++++ .../ComplexTypeToNullComparisonConverter.cs | 1 + AgileMapper/SimpleMappingContext.cs | 18 +++-- .../TypeConversionExtensions.cs | 2 +- .../Validation/EnumMappingMismatchFinder.cs | 2 +- .../Validation/EnumMappingMismatchSet.cs | 3 +- AgileMapper/Validation/MappingValidator.cs | 7 +- 70 files changed, 315 insertions(+), 153 deletions(-) rename AgileMapper/Members/{MemberExtensions => Extensions}/MemberExpressionExtensions.cs (96%) rename AgileMapper/Members/{ => Extensions}/MemberExtensionMethods.cs (96%) rename AgileMapper/Members/{MemberExtensions => Extensions}/NestedAccessChecksFactory.cs (99%) create mode 100644 AgileMapper/Members/IQualifiedMemberWrapper.cs create mode 100644 AgileMapper/Plans/MappingPlanSettings.cs diff --git a/AgileMapper.Buildable.UnitTests/WhenBuildingDerivedTypeMappers.cs b/AgileMapper.Buildable.UnitTests/WhenBuildingDerivedTypeMappers.cs index e795ccd23..a7678a798 100644 --- a/AgileMapper.Buildable.UnitTests/WhenBuildingDerivedTypeMappers.cs +++ b/AgileMapper.Buildable.UnitTests/WhenBuildingDerivedTypeMappers.cs @@ -2,6 +2,7 @@ { using AgileMapper.UnitTests.Common; using AgileMapper.UnitTests.Common.TestClasses; + using Plans; using Xunit; public class WhenBuildingDerivedTypeMappers @@ -11,13 +12,9 @@ public void ShouldBuildADerivedTypeCreateNewMapper() { using (var mapper = Mapper.CreateNew()) { - mapper.WhenMapping - .From() - .To() - .Map() - .To(); - - mapper.GetPlanFor().ToANew(); + mapper.GetPlanFor().ToANew( + new MappingPlanSettings { LazyCompile = true }, + cfg => cfg.Map().To()); var sourceCodeExpressions = mapper.BuildSourceCode(); diff --git a/AgileMapper.UnitTests/Configuration/WhenApplyingMapperConfigurations.cs b/AgileMapper.UnitTests/Configuration/WhenApplyingMapperConfigurations.cs index c571b32dc..9ebb4de47 100644 --- a/AgileMapper.UnitTests/Configuration/WhenApplyingMapperConfigurations.cs +++ b/AgileMapper.UnitTests/Configuration/WhenApplyingMapperConfigurations.cs @@ -8,7 +8,7 @@ using Common.TestClasses; using MoreTestClasses; using NetStandardPolyfills; - using TestClasses; + using Plans; using Testing; #if !NET35 using Xunit; @@ -215,7 +215,10 @@ protected override void Configure() .Map(ctx => ctx.Source.Value * 2) .To(t => t.Value); - GetPlanFor>().ToANew>(); + GetPlanFor>().ToANew>(new MappingPlanSettings + { + LazyCompile = true + }); } public static void VerifyConfigured(IMapper mapper) diff --git a/AgileMapper.UnitTests/Extensions/WhenFlatteningToQueryStringViaExtensionMethods.cs b/AgileMapper.UnitTests/Extensions/WhenFlatteningToQueryStringViaExtensionMethods.cs index 12b0a52fe..d36191ebb 100644 --- a/AgileMapper.UnitTests/Extensions/WhenFlatteningToQueryStringViaExtensionMethods.cs +++ b/AgileMapper.UnitTests/Extensions/WhenFlatteningToQueryStringViaExtensionMethods.cs @@ -4,7 +4,6 @@ using AgileMapper.Extensions; using Common; using Common.TestClasses; - using TestClasses; #if !NET35 using Xunit; #else diff --git a/AgileMapper.UnitTests/Members/MemberTestsBase.cs b/AgileMapper.UnitTests/Members/MemberTestsBase.cs index 73091a69f..4c2676ff1 100644 --- a/AgileMapper.UnitTests/Members/MemberTestsBase.cs +++ b/AgileMapper.UnitTests/Members/MemberTestsBase.cs @@ -4,6 +4,7 @@ using System.Linq; using System.Linq.Expressions; using AgileMapper.Members; + using AgileMapper.Members.Extensions; using NetStandardPolyfills; public abstract class MemberTestsBase diff --git a/AgileMapper.UnitTests/Members/WhenCreatingTargetMembersFromExpressions.cs b/AgileMapper.UnitTests/Members/WhenCreatingTargetMembersFromExpressions.cs index 0e2561f55..08c00e19c 100644 --- a/AgileMapper.UnitTests/Members/WhenCreatingTargetMembersFromExpressions.cs +++ b/AgileMapper.UnitTests/Members/WhenCreatingTargetMembersFromExpressions.cs @@ -5,6 +5,7 @@ using System.Linq; using System.Linq.Expressions; using AgileMapper.Members; + using AgileMapper.Members.Extensions; using Common; using Common.TestClasses; using TestClasses; diff --git a/AgileMapper.UnitTests/Members/WhenDeterminingRecursion.cs b/AgileMapper.UnitTests/Members/WhenDeterminingRecursion.cs index 98d863829..b3d9adb6c 100644 --- a/AgileMapper.UnitTests/Members/WhenDeterminingRecursion.cs +++ b/AgileMapper.UnitTests/Members/WhenDeterminingRecursion.cs @@ -1,6 +1,7 @@ namespace AgileObjects.AgileMapper.UnitTests.Members { using AgileMapper.Members; + using AgileMapper.Members.Extensions; using Common; using Common.TestClasses; using TestClasses; diff --git a/AgileMapper/Api/Configuration/ConfigInfoDataSourceExtensions.cs b/AgileMapper/Api/Configuration/ConfigInfoDataSourceExtensions.cs index 33f252f4b..ba68e9d3d 100644 --- a/AgileMapper/Api/Configuration/ConfigInfoDataSourceExtensions.cs +++ b/AgileMapper/Api/Configuration/ConfigInfoDataSourceExtensions.cs @@ -5,7 +5,7 @@ using System.Linq.Expressions; using AgileMapper.Configuration; using Extensions.Internal; - using Members; + using Members.Extensions; using ReadableExpressions; internal static class ConfigInfoDataSourceExtensions diff --git a/AgileMapper/Api/Configuration/CustomDataSourceTargetMemberSpecifier.cs b/AgileMapper/Api/Configuration/CustomDataSourceTargetMemberSpecifier.cs index 22b24920c..91e854af4 100644 --- a/AgileMapper/Api/Configuration/CustomDataSourceTargetMemberSpecifier.cs +++ b/AgileMapper/Api/Configuration/CustomDataSourceTargetMemberSpecifier.cs @@ -14,6 +14,7 @@ using Extensions.Internal; using Members; using Members.Dictionaries; + using Members.Extensions; using NetStandardPolyfills; using Projection; using ReadableExpressions; diff --git a/AgileMapper/Api/Configuration/Dictionaries/TargetDictionaryMappingConfigurator.cs b/AgileMapper/Api/Configuration/Dictionaries/TargetDictionaryMappingConfigurator.cs index 695787b7d..3140048e1 100644 --- a/AgileMapper/Api/Configuration/Dictionaries/TargetDictionaryMappingConfigurator.cs +++ b/AgileMapper/Api/Configuration/Dictionaries/TargetDictionaryMappingConfigurator.cs @@ -5,6 +5,7 @@ namespace AgileObjects.AgileMapper.Api.Configuration.Dictionaries using System.Linq.Expressions; using AgileMapper.Configuration; using Members; + using Members.Extensions; using ReadableExpressions; internal class TargetDictionaryMappingConfigurator : diff --git a/AgileMapper/Api/Configuration/Dynamics/TargetDynamicMappingConfigurator.cs b/AgileMapper/Api/Configuration/Dynamics/TargetDynamicMappingConfigurator.cs index 99f8c5ed7..4b515bb01 100644 --- a/AgileMapper/Api/Configuration/Dynamics/TargetDynamicMappingConfigurator.cs +++ b/AgileMapper/Api/Configuration/Dynamics/TargetDynamicMappingConfigurator.cs @@ -7,6 +7,7 @@ namespace AgileObjects.AgileMapper.Api.Configuration.Dynamics using AgileMapper.Configuration; using Dictionaries; using Members; + using Members.Extensions; using ReadableExpressions; internal class TargetDynamicMappingConfigurator : diff --git a/AgileMapper/Api/Configuration/EventConfigStartingPointBase.cs b/AgileMapper/Api/Configuration/EventConfigStartingPointBase.cs index eca7ccfc6..e03e56b79 100644 --- a/AgileMapper/Api/Configuration/EventConfigStartingPointBase.cs +++ b/AgileMapper/Api/Configuration/EventConfigStartingPointBase.cs @@ -3,6 +3,7 @@ using System.Linq.Expressions; using AgileMapper.Configuration; using Members; + using Members.Extensions; /// /// Provides options for configuring an element of how this mapper performs a mapping. diff --git a/AgileMapper/Api/IPlanTargetAndRuleSetSelector.cs b/AgileMapper/Api/IPlanTargetAndRuleSetSelector.cs index ea606ae68..c4a50d5f7 100644 --- a/AgileMapper/Api/IPlanTargetAndRuleSetSelector.cs +++ b/AgileMapper/Api/IPlanTargetAndRuleSetSelector.cs @@ -32,35 +32,59 @@ MappingPlan ToANew( params Expression>>[] configurations); /// - /// Create and compile mapping functions for an OnTo (merge) mapping from the source type being + /// Create and compile mapping functions for a create new mapping from the source type being /// configured to the type specified by the type argument. /// - /// The type of object for which to create the mapping plan. + /// The type of object for which to create the mapping plan. + /// + /// to control how the mapping plan should be produced. + /// /// /// Zero or more mapping configurations. If supplied, the mapping functions will be configured by /// combining these inline with any configuration already set up /// via the Mapper.WhenMapping API. /// /// - /// A object detailing the function to be executed during a mapping. To see - /// a string representation of the function assign the result to a string variable, or call .ToString(). + /// A object detailing the function to be executed during a mapping. + /// To see a string representation of the function assign the result to a string variable, + /// or call .ToString(). + /// + MappingPlan ToANew( + MappingPlanSettings settings, + params Expression>>[] configurations); + + /// + /// Create and compile mapping functions for an OnTo (merge) mapping from the source type + /// being configured to the type specified by the type argument. + /// + /// The type of object for which to create the mapping plan. + /// + /// Zero or more mapping configurations. If supplied, the mapping functions will be + /// configured by combining these inline with any + /// configuration already set up via the Mapper.WhenMapping API. + /// + /// + /// A object detailing the function to be executed during a mapping. + /// To see a string representation of the function assign the result to a string variable, + /// or call .ToString(). /// MappingPlan OnTo( params Expression>>[] configurations); /// - /// Create and compile mapping functions for an Over (overwrite) mapping from the source type being - /// configured to the type specified by the type argument. + /// Create and compile mapping functions for an Over (overwrite) mapping from the source type + /// being configured to the type specified by the type argument. /// /// The type of object for which to create the mapping plan. /// - /// Zero or more mapping configurations. If supplied, the mapping functions will be configured by - /// combining these inline with any configuration already set up - /// via the Mapper.WhenMapping API. + /// Zero or more mapping configurations. If supplied, the mapping functions will be + /// configured by combining these inline with any + /// configuration already set up via the Mapper.WhenMapping API. /// /// - /// A object detailing the function to be executed during a mapping. To see - /// a string representation of the function assign the result to a string variable, or call .ToString(). + /// A object detailing the function to be executed during a mapping. + /// To see a string representation of the function assign the result to a string variable, + /// or call .ToString(). /// MappingPlan Over( params Expression>>[] configurations); diff --git a/AgileMapper/Api/PlanTargetSelector.cs b/AgileMapper/Api/PlanTargetSelector.cs index 22241d70a..087a55c64 100644 --- a/AgileMapper/Api/PlanTargetSelector.cs +++ b/AgileMapper/Api/PlanTargetSelector.cs @@ -11,7 +11,8 @@ using ObjectPopulation; using Plans; using Queryables.Api; - + using static Plans.MappingPlanSettings; + internal class PlanTargetSelector : IPlanTargetSelector, IPlanTargetAndRuleSetSelector, @@ -31,51 +32,69 @@ internal PlanTargetSelector(MapperContext mapperContext) _mapperContext = mapperContext.ThrowIfDisposed(); } - public MappingPlanSet To() => To(configurations: null); + public MappingPlanSet To() + => To(configurations: Enumerable>>>.EmptyArray); public MappingPlanSet To( Expression>>[] configurations) { - return new MappingPlanSet( - _mapperContext - .RuleSets - .All - .Filter(_mapperContext, (mc, ruleSet) => ruleSet != mc.RuleSets.Project) - .Project(configurations, (cs, rs) => GetMappingPlan(rs, cs)) - .ToArray()); + return new(_mapperContext + .RuleSets + .All + .Filter(_mapperContext, (mc, ruleSet) => ruleSet != mc.RuleSets.Project) + .Project(configurations, (cs, rs) => GetMappingPlan(rs, EagerPlanned, cs)) + .ToArray()); } public MappingPlan ToANew( Expression>>[] configurations) - => GetMappingPlan(_mapperContext.RuleSets.CreateNew, configurations); + { + return ToANew(EagerPlanned, configurations); + } + + public MappingPlan ToANew( + MappingPlanSettings settings, + Expression>>[] configurations) + { + return GetMappingPlan( + _mapperContext.RuleSets.CreateNew, + settings, + configurations); + } public MappingPlan OnTo( Expression>>[] configurations) - => GetMappingPlan(_mapperContext.RuleSets.Merge, configurations); + { + return GetMappingPlan( + _mapperContext.RuleSets.Merge, + EagerPlanned, + configurations); + } public MappingPlan Over( Expression>>[] configurations) - => GetMappingPlan(_mapperContext.RuleSets.Overwrite, configurations); + { + return GetMappingPlan( + _mapperContext.RuleSets.Overwrite, + EagerPlanned, + configurations); + } MappingPlan IProjectionPlanTargetSelector.To() { - return GetMappingPlan( + return GetMappingPlan( _mapperContext.QueryProjectionMappingContext, - planContext => ObjectMappingDataFactory.ForProjection(_exampleQueryable, planContext)); + planContext => ObjectMappingDataFactory.ForProjection(_exampleQueryable, planContext), + Enumerable>>>.EmptyArray); } private MappingPlan GetMappingPlan( MappingRuleSet ruleSet, + MappingPlanSettings settings, ICollection>>> configurations) { - var planContext = new SimpleMappingContext(ruleSet, _mapperContext) - { - IncludeCodeComments = true, - IgnoreUnsuccessfulMemberPopulations = false - }; - return GetMappingPlan( - planContext, + new SimpleMappingContext(ruleSet, settings, _mapperContext), ObjectMappingDataFactory.ForRootFixedTypes, configurations); } @@ -83,9 +102,9 @@ private MappingPlan GetMappingPlan( private static MappingPlan GetMappingPlan( IMappingContext planContext, Func mappingDataFactory, - ICollection>>> configurations = null) + ICollection>>> configurations) { - if (configurations?.Any() == true) + if (configurations.Any()) { InlineMappingConfigurator #if NET35 diff --git a/AgileMapper/Configuration/DataSources/ConfiguredDataSourceFactory.cs b/AgileMapper/Configuration/DataSources/ConfiguredDataSourceFactory.cs index 5b792bab2..b49577e7a 100644 --- a/AgileMapper/Configuration/DataSources/ConfiguredDataSourceFactory.cs +++ b/AgileMapper/Configuration/DataSources/ConfiguredDataSourceFactory.cs @@ -7,6 +7,7 @@ #endif using Lambdas; using Members; + using Members.Extensions; internal class ConfiguredDataSourceFactory : ConfiguredDataSourceFactoryBase, diff --git a/AgileMapper/Configuration/DataSources/ConfiguredDataSourceFactoryBase.cs b/AgileMapper/Configuration/DataSources/ConfiguredDataSourceFactoryBase.cs index 4e4d518bb..c15d75892 100644 --- a/AgileMapper/Configuration/DataSources/ConfiguredDataSourceFactoryBase.cs +++ b/AgileMapper/Configuration/DataSources/ConfiguredDataSourceFactoryBase.cs @@ -10,6 +10,7 @@ using Api.Configuration; using Lambdas; using Members; + using Members.Extensions; internal abstract class ConfiguredDataSourceFactoryBase : UserConfiguredItemBase, diff --git a/AgileMapper/Configuration/Dictionaries/CustomDictionaryKey.cs b/AgileMapper/Configuration/Dictionaries/CustomDictionaryKey.cs index 169df066d..b7a3a1bb8 100644 --- a/AgileMapper/Configuration/Dictionaries/CustomDictionaryKey.cs +++ b/AgileMapper/Configuration/Dictionaries/CustomDictionaryKey.cs @@ -9,6 +9,7 @@ #endif using DataSources; using Members; + using Members.Extensions; using ReadableExpressions.Extensions; internal class CustomDictionaryKey : UserConfiguredItemBase @@ -105,8 +106,8 @@ private static bool IsPartOfExpandoObjectMapping(IQualifiedMemberContext context { while (context != null) { - if ((context.SourceMember.GetFriendlyTypeName() == nameof(ExpandoObject)) || - (context.TargetMember.GetFriendlyTypeName() == nameof(ExpandoObject))) + if ((context.SourceMember.GetTypeName() == nameof(ExpandoObject)) || + (context.TargetMember.GetTypeName() == nameof(ExpandoObject))) { return true; } diff --git a/AgileMapper/Configuration/Lambdas/ConfiguredLambdaInfo.cs b/AgileMapper/Configuration/Lambdas/ConfiguredLambdaInfo.cs index d640694f4..d9dbce6e4 100644 --- a/AgileMapper/Configuration/Lambdas/ConfiguredLambdaInfo.cs +++ b/AgileMapper/Configuration/Lambdas/ConfiguredLambdaInfo.cs @@ -10,6 +10,7 @@ using Extensions.Internal; using Members; using Members.Dictionaries; + using Members.Extensions; using NetStandardPolyfills; using ReadableExpressions; using ReadableExpressions.Extensions; @@ -102,7 +103,7 @@ public static ConfiguredLambdaInfo ForAction( configInfo, argumentTypes, funcTypes => funcTypes, - funcTypes => typeof(void), + _ => typeof(void), typeof(Action<>), typeof(Action<,>), typeof(Action<,,>), @@ -174,7 +175,7 @@ public bool TryGetSourceMember(out LambdaExpression sourceMemberLambda) return IsNotSourceMember(out sourceMemberLambda); } - var memberAccesses = _lambdaBody.GetMemberAccessChain(nt => { }, out var rootExpression); + var memberAccesses = _lambdaBody.GetMemberAccessChain(_ => { }, out var rootExpression); if (memberAccesses.None()) { diff --git a/AgileMapper/Configuration/MemberIgnores/ConfiguredSourceMemberIgnore.cs b/AgileMapper/Configuration/MemberIgnores/ConfiguredSourceMemberIgnore.cs index dc1e7317a..8a6d7a582 100644 --- a/AgileMapper/Configuration/MemberIgnores/ConfiguredSourceMemberIgnore.cs +++ b/AgileMapper/Configuration/MemberIgnores/ConfiguredSourceMemberIgnore.cs @@ -10,6 +10,7 @@ namespace AgileObjects.AgileMapper.Configuration.MemberIgnores using Extensions.Internal; #endif using Members; + using Members.Extensions; #if NET35 using LinqExp = System.Linq.Expressions; #endif diff --git a/AgileMapper/Configuration/MemberIgnores/SourceValueFilters/FilterCondition.cs b/AgileMapper/Configuration/MemberIgnores/SourceValueFilters/FilterCondition.cs index 861274d08..a75caff1a 100644 --- a/AgileMapper/Configuration/MemberIgnores/SourceValueFilters/FilterCondition.cs +++ b/AgileMapper/Configuration/MemberIgnores/SourceValueFilters/FilterCondition.cs @@ -9,9 +9,8 @@ namespace AgileObjects.AgileMapper.Configuration.MemberIgnores.SourceValueFilter #endif using Api.Configuration; using Extensions.Internal; - using Members.MemberExtensions; + using Members.Extensions; using NetStandardPolyfills; - using ReadableExpressions.Extensions; #if NET35 using LinqExp = System.Linq.Expressions; #endif diff --git a/AgileMapper/Configuration/UserConfiguredItemBase.cs b/AgileMapper/Configuration/UserConfiguredItemBase.cs index 747eead86..4dab837ad 100644 --- a/AgileMapper/Configuration/UserConfiguredItemBase.cs +++ b/AgileMapper/Configuration/UserConfiguredItemBase.cs @@ -7,6 +7,7 @@ using System.Linq.Expressions; #endif using Members; + using Members.Extensions; using NetStandardPolyfills; using ReadableExpressions.Extensions; diff --git a/AgileMapper/DataSources/ComplexTypeDataSource.cs b/AgileMapper/DataSources/ComplexTypeDataSource.cs index d8f6732c9..2a27f0a9f 100644 --- a/AgileMapper/DataSources/ComplexTypeDataSource.cs +++ b/AgileMapper/DataSources/ComplexTypeDataSource.cs @@ -6,6 +6,7 @@ using System.Linq.Expressions; #endif using Members; + using Members.Extensions; using ObjectPopulation; using ObjectPopulation.ComplexTypes; diff --git a/AgileMapper/DataSources/ConfiguredMappingDataSource.cs b/AgileMapper/DataSources/ConfiguredMappingDataSource.cs index 930358c79..6e0b64a3e 100644 --- a/AgileMapper/DataSources/ConfiguredMappingDataSource.cs +++ b/AgileMapper/DataSources/ConfiguredMappingDataSource.cs @@ -6,6 +6,7 @@ using System.Linq.Expressions; #endif using Members; + using Members.Extensions; using ObjectPopulation; internal static class ConfiguredMappingDataSource diff --git a/AgileMapper/DataSources/DataSourceBase.cs b/AgileMapper/DataSources/DataSourceBase.cs index 81ddf523f..ea0dd0740 100644 --- a/AgileMapper/DataSources/DataSourceBase.cs +++ b/AgileMapper/DataSources/DataSourceBase.cs @@ -8,6 +8,7 @@ #endif using Extensions.Internal; using Members; + using Members.Extensions; using NetStandardPolyfills; using Optimisation; diff --git a/AgileMapper/DataSources/DataSourceFilteringExtensions.cs b/AgileMapper/DataSources/DataSourceFilteringExtensions.cs index e2c379c6c..86a6d713b 100644 --- a/AgileMapper/DataSources/DataSourceFilteringExtensions.cs +++ b/AgileMapper/DataSources/DataSourceFilteringExtensions.cs @@ -9,6 +9,7 @@ using Configuration.MemberIgnores.SourceValueFilters; using Extensions.Internal; using Members; + using Members.Extensions; internal static class DataSourceFilteringExtensions { diff --git a/AgileMapper/DataSources/DataSourceSet.cs b/AgileMapper/DataSources/DataSourceSet.cs index b87727918..1cca7d34c 100644 --- a/AgileMapper/DataSources/DataSourceSet.cs +++ b/AgileMapper/DataSources/DataSourceSet.cs @@ -24,7 +24,7 @@ private static IDataSourceSet For( { if (!dataSource.IsValid) { - return info.MappingContext.IgnoreUnsuccessfulMemberPopulations + return info.MappingContext.PlanSettings.CommentUnmappedMembers ? EmptyDataSourceSet.Instance : new NullDataSourceSet(dataSource); } diff --git a/AgileMapper/DataSources/DictionaryNonSimpleMemberDataSource.cs b/AgileMapper/DataSources/DictionaryNonSimpleMemberDataSource.cs index 16693e0d3..589e36cf0 100644 --- a/AgileMapper/DataSources/DictionaryNonSimpleMemberDataSource.cs +++ b/AgileMapper/DataSources/DictionaryNonSimpleMemberDataSource.cs @@ -1,6 +1,7 @@ namespace AgileObjects.AgileMapper.DataSources { using Members; + using Members.Extensions; internal class DictionaryNonSimpleMemberDataSource : DataSourceBase { diff --git a/AgileMapper/DataSources/Optimisation/MultiInvocationsFinder.cs b/AgileMapper/DataSources/Optimisation/MultiInvocationsFinder.cs index 0f37b1e10..2c3cd9434 100644 --- a/AgileMapper/DataSources/Optimisation/MultiInvocationsFinder.cs +++ b/AgileMapper/DataSources/Optimisation/MultiInvocationsFinder.cs @@ -9,7 +9,7 @@ #endif using Extensions.Internal; using Members; - using Members.MemberExtensions; + using Members.Extensions; internal static class MultiInvocationsFinder { diff --git a/AgileMapper/IMappingContext.cs b/AgileMapper/IMappingContext.cs index ece919728..86d5d706e 100644 --- a/AgileMapper/IMappingContext.cs +++ b/AgileMapper/IMappingContext.cs @@ -1,11 +1,9 @@ namespace AgileObjects.AgileMapper { + using Plans; + internal interface IMappingContext : IMapperContextOwner, IRuleSetOwner { - bool IncludeCodeComments { get; } - - bool IgnoreUnsuccessfulMemberPopulations { get; } - - bool LazyLoadRepeatMappingFuncs { get; } + MappingPlanSettings PlanSettings { get; } } } \ No newline at end of file diff --git a/AgileMapper/MappingException.cs b/AgileMapper/MappingException.cs index 72afb9f1b..e98897473 100644 --- a/AgileMapper/MappingException.cs +++ b/AgileMapper/MappingException.cs @@ -5,14 +5,15 @@ #if FEATURE_SERIALIZATION using System.Runtime.Serialization; #endif - using Extensions.Internal; - using Members; - using NetStandardPolyfills; #if NET35 using Microsoft.Scripting.Ast; #else using System.Linq.Expressions; #endif + using Extensions.Internal; + using Members; + using Members.Extensions; + using NetStandardPolyfills; /// /// Represents an error that occurred during a mapping. diff --git a/AgileMapper/MappingExecutionContextBase.cs b/AgileMapper/MappingExecutionContextBase.cs index 45d466d38..78d6514b4 100644 --- a/AgileMapper/MappingExecutionContextBase.cs +++ b/AgileMapper/MappingExecutionContextBase.cs @@ -1,6 +1,7 @@ namespace AgileObjects.AgileMapper { using ObjectPopulation; + using Plans; /// /// Base type providing creation for objects @@ -17,7 +18,7 @@ public abstract class MappingExecutionContextBase : IMappingContext /// /// Initializes a new instance of the - /// class with a default MapperContext and the given . + /// class with the given object. /// /// The source object from which the mapping is to be performed. protected MappingExecutionContextBase(TSource source) @@ -30,11 +31,7 @@ protected MappingExecutionContextBase(TSource source) MappingRuleSet IRuleSetOwner.RuleSet => null; - bool IMappingContext.IncludeCodeComments => false; - - bool IMappingContext.IgnoreUnsuccessfulMemberPopulations => true; - - bool IMappingContext.LazyLoadRepeatMappingFuncs => true; + MappingPlanSettings IMappingContext.PlanSettings => null; /// /// Creates a root object for this diff --git a/AgileMapper/MappingExecutor.cs b/AgileMapper/MappingExecutor.cs index e8d880079..ac919d566 100644 --- a/AgileMapper/MappingExecutor.cs +++ b/AgileMapper/MappingExecutor.cs @@ -11,6 +11,7 @@ using Extensions; using Extensions.Internal; using ObjectPopulation; + using Plans; internal class MappingExecutor : ITargetSelector, @@ -30,11 +31,7 @@ public MappingExecutor(MapperContext mapperContext, TSource source) public MappingRuleSet RuleSet { get; private set; } - bool IMappingContext.IncludeCodeComments => false; - - public bool IgnoreUnsuccessfulMemberPopulations => true; - - public bool LazyLoadRepeatMappingFuncs => true; + MappingPlanSettings IMappingContext.PlanSettings => MappingPlanSettings.LazyPlanned; #region ToANew Overloads diff --git a/AgileMapper/Members/ConfiguredSourceMember.cs b/AgileMapper/Members/ConfiguredSourceMember.cs index 9cd842569..6a11ef4a6 100644 --- a/AgileMapper/Members/ConfiguredSourceMember.cs +++ b/AgileMapper/Members/ConfiguredSourceMember.cs @@ -8,9 +8,10 @@ namespace AgileObjects.AgileMapper.Members #else using System.Linq.Expressions; #endif + using AgileMapper.Extensions; + using AgileMapper.Extensions.Internal; using Caching; using Extensions; - using Extensions.Internal; using NetStandardPolyfills; using ReadableExpressions; using ReadableExpressions.Extensions; @@ -100,8 +101,6 @@ private ConfiguredSourceMember( public Type ElementType { get; } - public string GetFriendlyTypeName() => Type.GetFriendlyName(); - public bool IsEnumerable { get; } public bool IsSimple { get; } diff --git a/AgileMapper/Members/Dictionaries/DictionaryEntrySourceMember.cs b/AgileMapper/Members/Dictionaries/DictionaryEntrySourceMember.cs index dd2e92923..fea6b141d 100644 --- a/AgileMapper/Members/Dictionaries/DictionaryEntrySourceMember.cs +++ b/AgileMapper/Members/Dictionaries/DictionaryEntrySourceMember.cs @@ -6,7 +6,8 @@ namespace AgileObjects.AgileMapper.Members.Dictionaries #else using System.Linq.Expressions; #endif - using Extensions.Internal; + using AgileMapper.Extensions.Internal; + using Extensions; using ReadableExpressions.Extensions; internal class DictionaryEntrySourceMember : IQualifiedMember @@ -77,8 +78,6 @@ private DictionaryEntrySourceMember( public Type RootType => Parent.Type; - public string GetFriendlyTypeName() => Type.GetFriendlyName(); - public Type ElementType => _childMembers.Last().ElementType; public bool IsEnumerable { get; } diff --git a/AgileMapper/Members/Dictionaries/DictionaryMemberMapperDataExtensions.cs b/AgileMapper/Members/Dictionaries/DictionaryMemberMapperDataExtensions.cs index 2ff0bf2dd..a4ef028b1 100644 --- a/AgileMapper/Members/Dictionaries/DictionaryMemberMapperDataExtensions.cs +++ b/AgileMapper/Members/Dictionaries/DictionaryMemberMapperDataExtensions.cs @@ -2,14 +2,15 @@ namespace AgileObjects.AgileMapper.Members.Dictionaries { using System.Collections.Generic; using System.Linq; - using Extensions.Internal; - using NetStandardPolyfills; - using ReadableExpressions.Extensions; #if NET35 using Microsoft.Scripting.Ast; #else using System.Linq.Expressions; #endif + using AgileMapper.Extensions.Internal; + using Extensions; + using NetStandardPolyfills; + using ReadableExpressions.Extensions; internal static class DictionaryMemberMapperDataExtensions { diff --git a/AgileMapper/Members/Dictionaries/DictionarySourceMember.cs b/AgileMapper/Members/Dictionaries/DictionarySourceMember.cs index b190ee7bb..381545d16 100644 --- a/AgileMapper/Members/Dictionaries/DictionarySourceMember.cs +++ b/AgileMapper/Members/Dictionaries/DictionarySourceMember.cs @@ -6,10 +6,11 @@ namespace AgileObjects.AgileMapper.Members.Dictionaries #else using System.Linq.Expressions; #endif + using Extensions; using NetStandardPolyfills; using ReadableExpressions.Extensions; - internal class DictionarySourceMember : IQualifiedMember + internal class DictionarySourceMember : IQualifiedMemberWrapper { private readonly IQualifiedMember _wrappedSourceMember; private readonly QualifiedMember _matchedTargetMember; @@ -72,8 +73,6 @@ private DictionarySourceMember( public Type ElementType => ValueType; - public string GetFriendlyTypeName() => _wrappedSourceMember.GetFriendlyTypeName(); - public Type KeyType { get; } public Type ValueType { get; } @@ -92,6 +91,8 @@ private DictionarySourceMember( public string Name => _wrappedSourceMember.Name; + IQualifiedMember IQualifiedMemberWrapper.WrappedMember => _wrappedSourceMember; + public string GetPath() => _wrappedSourceMember.GetPath(); public IQualifiedMember GetElementMember() @@ -132,10 +133,7 @@ public Expression GetQualifiedAccess(Expression parentInstance) : EntryMember.GetQualifiedAccess(parentInstance); } - public IQualifiedMember SetContext(IQualifiedMemberContext context) - { - return this; - } + public IQualifiedMember SetContext(IQualifiedMemberContext context) => this; #region ExcludeFromCodeCoverage #if DEBUG diff --git a/AgileMapper/Members/Dictionaries/DictionaryTargetMember.cs b/AgileMapper/Members/Dictionaries/DictionaryTargetMember.cs index 68f494ec3..917e8eac2 100644 --- a/AgileMapper/Members/Dictionaries/DictionaryTargetMember.cs +++ b/AgileMapper/Members/Dictionaries/DictionaryTargetMember.cs @@ -6,16 +6,20 @@ namespace AgileObjects.AgileMapper.Members.Dictionaries using System.Linq; #if NET35 using Microsoft.Scripting.Ast; - using static Microsoft.Scripting.Ast.ExpressionType; #else using System.Linq.Expressions; - using static System.Linq.Expressions.ExpressionType; #endif + using AgileMapper.Extensions; + using AgileMapper.Extensions.Internal; using Extensions; - using Extensions.Internal; using NetStandardPolyfills; using ReadableExpressions.Extensions; using TypeConversion; +#if NET35 + using static Microsoft.Scripting.Ast.ExpressionType; +#else + using static System.Linq.Expressions.ExpressionType; +#endif internal class DictionaryTargetMember : QualifiedMember { diff --git a/AgileMapper/Members/MemberExtensions/MemberExpressionExtensions.cs b/AgileMapper/Members/Extensions/MemberExpressionExtensions.cs similarity index 96% rename from AgileMapper/Members/MemberExtensions/MemberExpressionExtensions.cs rename to AgileMapper/Members/Extensions/MemberExpressionExtensions.cs index 10d137430..f508bad8c 100644 --- a/AgileMapper/Members/MemberExtensions/MemberExpressionExtensions.cs +++ b/AgileMapper/Members/Extensions/MemberExpressionExtensions.cs @@ -1,4 +1,4 @@ -namespace AgileObjects.AgileMapper.Members.MemberExtensions +namespace AgileObjects.AgileMapper.Members.Extensions { using System; #if NET35 diff --git a/AgileMapper/Members/MemberExtensionMethods.cs b/AgileMapper/Members/Extensions/MemberExtensionMethods.cs similarity index 96% rename from AgileMapper/Members/MemberExtensionMethods.cs rename to AgileMapper/Members/Extensions/MemberExtensionMethods.cs index 24a5d63f5..f7609716f 100644 --- a/AgileMapper/Members/MemberExtensionMethods.cs +++ b/AgileMapper/Members/Extensions/MemberExtensionMethods.cs @@ -1,4 +1,4 @@ -namespace AgileObjects.AgileMapper.Members +namespace AgileObjects.AgileMapper.Members.Extensions { using System; using System.Collections.Generic; @@ -7,15 +7,14 @@ using System.Linq; #if NET35 using Microsoft.Scripting.Ast; - using LinqExp = System.Linq.Expressions; #else using System.Linq.Expressions; #endif using System.Reflection; + using AgileMapper.Extensions; + using AgileMapper.Extensions.Internal; using Caching.Dictionaries; using Configuration; - using Extensions; - using Extensions.Internal; using NetStandardPolyfills; using ObjectPopulation; using ReadableExpressions; @@ -23,12 +22,24 @@ using static System.StringComparison; using static Constants; using static Member; +#if NET35 + using LinqExp = System.Linq.Expressions; +#endif internal static class MemberExtensionMethods { public static string GetFullName(this IEnumerable members) => members.Project(m => m.JoiningName).Join(string.Empty); + public static string GetTypeName(this IQualifiedMember member) + => member.GetNamingType().Name; + + public static string GetFriendlyTypeName(this IQualifiedMember member) + => member.GetNamingType().GetFriendlyName(); + + private static Type GetNamingType(this IQualifiedMember member) + => (member is IQualifiedMemberWrapper wrapper ? wrapper.WrappedMember : member).Type; + public static string GetFriendlySourcePath(this IQualifiedMember sourceMember, IMemberMapperData rootMapperData) => GetFriendlyMemberPath(sourceMember, rootMapperData.SourceMember); @@ -280,7 +291,7 @@ public static QualifiedMember ToSourceMemberOrNull( this Expression memberAccess, MapperContext mapperContext) { - return memberAccess.ToSourceMember(mapperContext, nt => { }); + return memberAccess.ToSourceMember(mapperContext, _ => { }); } public static QualifiedMember ToSourceMemberOrNull( @@ -289,7 +300,7 @@ public static QualifiedMember ToSourceMemberOrNull( out string failureReason) { var hasUnsupportedNodeType = false; - var sourceMember = memberAccess.ToSourceMember(mapperContext, nt => hasUnsupportedNodeType = true); + var sourceMember = memberAccess.ToSourceMember(mapperContext, _ => hasUnsupportedNodeType = true); if (hasUnsupportedNodeType) { @@ -323,7 +334,7 @@ public static QualifiedMember ToSourceMember( nonMemberAction ?? ThrowIfUnsupported, mapperContext); } - + #if NET35 public static QualifiedMember ToTargetMemberOrNull(this LinqExp.LambdaExpression memberAccess, MapperContext mapperContext) => memberAccess.ToDlrExpression().ToTargetMemberOrNull(mapperContext); @@ -342,7 +353,7 @@ public static QualifiedMember ToTargetMemberOrNull( out string failureReason) { var hasUnsupportedNodeType = false; - var targetMember = memberAccess.ToTargetMember(mapperContext, nt => hasUnsupportedNodeType = true); + var targetMember = memberAccess.ToTargetMember(mapperContext, _ => hasUnsupportedNodeType = true); if (hasUnsupportedNodeType) { diff --git a/AgileMapper/Members/MemberExtensions/NestedAccessChecksFactory.cs b/AgileMapper/Members/Extensions/NestedAccessChecksFactory.cs similarity index 99% rename from AgileMapper/Members/MemberExtensions/NestedAccessChecksFactory.cs rename to AgileMapper/Members/Extensions/NestedAccessChecksFactory.cs index 6248e0168..18b3fa715 100644 --- a/AgileMapper/Members/MemberExtensions/NestedAccessChecksFactory.cs +++ b/AgileMapper/Members/Extensions/NestedAccessChecksFactory.cs @@ -1,4 +1,4 @@ -namespace AgileObjects.AgileMapper.Members.MemberExtensions +namespace AgileObjects.AgileMapper.Members.Extensions { using System; using System.Collections.Generic; @@ -8,8 +8,8 @@ #else using System.Linq.Expressions; #endif - using Extensions; - using Extensions.Internal; + using AgileMapper.Extensions; + using AgileMapper.Extensions.Internal; using NetStandardPolyfills; using ReadableExpressions.Extensions; #if NET35 diff --git a/AgileMapper/Members/IQualifiedMember.cs b/AgileMapper/Members/IQualifiedMember.cs index 47a78c9c6..b5c101b66 100644 --- a/AgileMapper/Members/IQualifiedMember.cs +++ b/AgileMapper/Members/IQualifiedMember.cs @@ -17,8 +17,6 @@ internal interface IQualifiedMember Type ElementType { get; } - string GetFriendlyTypeName(); - bool IsEnumerable { get; } bool IsSimple { get; } diff --git a/AgileMapper/Members/IQualifiedMemberWrapper.cs b/AgileMapper/Members/IQualifiedMemberWrapper.cs new file mode 100644 index 000000000..aacb20e94 --- /dev/null +++ b/AgileMapper/Members/IQualifiedMemberWrapper.cs @@ -0,0 +1,7 @@ +namespace AgileObjects.AgileMapper.Members +{ + internal interface IQualifiedMemberWrapper : IQualifiedMember + { + IQualifiedMember WrappedMember { get; } + } +} \ No newline at end of file diff --git a/AgileMapper/Members/Member.cs b/AgileMapper/Members/Member.cs index 8d6363ce8..53076ff06 100644 --- a/AgileMapper/Members/Member.cs +++ b/AgileMapper/Members/Member.cs @@ -7,9 +7,10 @@ namespace AgileObjects.AgileMapper.Members #else using System.Linq.Expressions; #endif + using AgileMapper.Extensions; + using AgileMapper.Extensions.Internal; using Dictionaries; using Extensions; - using Extensions.Internal; using NetStandardPolyfills; using ObjectPopulation; using ReadableExpressions.Extensions; diff --git a/AgileMapper/Members/MemberCache.cs b/AgileMapper/Members/MemberCache.cs index 837ab19ab..558a1f4ed 100644 --- a/AgileMapper/Members/MemberCache.cs +++ b/AgileMapper/Members/MemberCache.cs @@ -4,9 +4,9 @@ using System.Collections.Generic; using System.Linq; using System.Reflection; + using AgileMapper.Extensions; + using AgileMapper.Extensions.Internal; using Caching; - using Extensions; - using Extensions.Internal; using NetStandardPolyfills; using ReadableExpressions.Extensions; using static System.StringComparer; diff --git a/AgileMapper/Members/MemberIdentifierSet.cs b/AgileMapper/Members/MemberIdentifierSet.cs index c67ebee0f..9bfc02e11 100644 --- a/AgileMapper/Members/MemberIdentifierSet.cs +++ b/AgileMapper/Members/MemberIdentifierSet.cs @@ -3,14 +3,15 @@ namespace AgileObjects.AgileMapper.Members using System; using System.Collections.Generic; using System.Linq; - using Configuration; - using NetStandardPolyfills; - using ReadableExpressions.Extensions; #if NET35 using Microsoft.Scripting.Ast; #else using System.Linq.Expressions; #endif + using Configuration; + using Extensions; + using NetStandardPolyfills; + using ReadableExpressions.Extensions; internal class MemberIdentifierSet { diff --git a/AgileMapper/Members/MemberMapperDataExtensions.cs b/AgileMapper/Members/MemberMapperDataExtensions.cs index d9a2fed37..00e281b93 100644 --- a/AgileMapper/Members/MemberMapperDataExtensions.cs +++ b/AgileMapper/Members/MemberMapperDataExtensions.cs @@ -10,14 +10,14 @@ namespace AgileObjects.AgileMapper.Members using System.Linq.Expressions; #endif using System.Reflection; + using AgileMapper.Extensions; + using AgileMapper.Extensions.Internal; using Configuration; using Configuration.DataSources; using Configuration.MemberIgnores.SourceValueFilters; using DataSources; using Dictionaries; using Extensions; - using Extensions.Internal; - using MemberExtensions; using NetStandardPolyfills; using ObjectPopulation; using ObjectPopulation.Enumerables.EnumerableExtensions; diff --git a/AgileMapper/Members/NamingSettings.cs b/AgileMapper/Members/NamingSettings.cs index d142ed6fd..4138558ac 100644 --- a/AgileMapper/Members/NamingSettings.cs +++ b/AgileMapper/Members/NamingSettings.cs @@ -3,10 +3,10 @@ using System; using System.Collections.Generic; using System.Linq; + using AgileMapper.Extensions; + using AgileMapper.Extensions.Internal; using Caching; using Configuration; - using Extensions; - using Extensions.Internal; internal class NamingSettings { diff --git a/AgileMapper/Members/Population/MemberPopulator.cs b/AgileMapper/Members/Population/MemberPopulator.cs index 47134673f..0e4001b26 100644 --- a/AgileMapper/Members/Population/MemberPopulator.cs +++ b/AgileMapper/Members/Population/MemberPopulator.cs @@ -5,8 +5,8 @@ namespace AgileObjects.AgileMapper.Members.Population #else using System.Linq.Expressions; #endif + using AgileMapper.Extensions.Internal; using DataSources; - using Extensions.Internal; internal class MemberPopulator : IMemberPopulator { diff --git a/AgileMapper/Members/Population/MemberPopulatorFactory.cs b/AgileMapper/Members/Population/MemberPopulatorFactory.cs index 5c9e308e2..d32e0aa1e 100644 --- a/AgileMapper/Members/Population/MemberPopulatorFactory.cs +++ b/AgileMapper/Members/Population/MemberPopulatorFactory.cs @@ -2,15 +2,15 @@ namespace AgileObjects.AgileMapper.Members.Population { using System; using System.Collections.Generic; + using AgileMapper.Extensions; + using AgileMapper.Extensions.Internal; using DataSources.Factories; - using Extensions; - using Extensions.Internal; using Members; using ObjectPopulation; internal class MemberPopulatorFactory { - public static readonly MemberPopulatorFactory Default = new MemberPopulatorFactory(mapperData => + public static readonly MemberPopulatorFactory Default = new(mapperData => GlobalContext.Instance .MemberCache .GetTargetMembers(mapperData.TargetType) diff --git a/AgileMapper/Members/Population/NullMemberPopulator.cs b/AgileMapper/Members/Population/NullMemberPopulator.cs index 4232926d5..b4360d82b 100644 --- a/AgileMapper/Members/Population/NullMemberPopulator.cs +++ b/AgileMapper/Members/Population/NullMemberPopulator.cs @@ -38,7 +38,7 @@ public static IMemberPopulator NoDataSources(MemberPopulationContext context) context.MemberMapperData.RegisterTargetMemberDataSources(noDataSources); - return CreateNullMemberPopulator(noDataSourcesMessage, context, (msg, md) => msg); + return CreateNullMemberPopulator(noDataSourcesMessage, context, (msg, _) => msg); } private static string GetNoDataSourcesMessage(QualifiedMember targetMember) @@ -63,7 +63,7 @@ private static IMemberPopulator CreateNullMemberPopulator( MemberPopulationContext context, Func descriptionFactory) { - if (context.MappingContext.IgnoreUnsuccessfulMemberPopulations) + if (!context.MappingContext.PlanSettings.CommentUnmappedMembers) { return null; } diff --git a/AgileMapper/Members/QualifiedMember.cs b/AgileMapper/Members/QualifiedMember.cs index 18fd2dbb2..c52b11f1a 100644 --- a/AgileMapper/Members/QualifiedMember.cs +++ b/AgileMapper/Members/QualifiedMember.cs @@ -9,16 +9,17 @@ namespace AgileObjects.AgileMapper.Members #else using System.Linq.Expressions; #endif + using AgileMapper.Extensions.Internal; using Caching; using Dictionaries; - using Extensions.Internal; + using Extensions; using NetStandardPolyfills; using ReadableExpressions.Extensions; internal class QualifiedMember : IQualifiedMember { - public static readonly QualifiedMember All = new QualifiedMember(default(Member), null); - public static readonly QualifiedMember None = new QualifiedMember(default(Member), null); + public static readonly QualifiedMember All = new(default(Member), null); + public static readonly QualifiedMember None = new(default(Member), null); private readonly MapperContext _mapperContext; private readonly QualifiedMember _parent; @@ -38,7 +39,7 @@ protected QualifiedMember(Member[] memberChain, QualifiedMember adaptedMember) foreach (var childMember in adaptedMember._childMemberCache.Values) { - _childMemberCache.GetOrAdd(childMember.LeafMember, m => childMember); + _childMemberCache.GetOrAdd(childMember.LeafMember, _ => childMember); } } @@ -155,8 +156,6 @@ public static QualifiedMember Create(Member[] memberChain, MapperContext mapperC public Type RootType => MemberChain[0].Type; - public string GetFriendlyTypeName() => Type.GetFriendlyName(); - public Type ElementType => LeafMember?.ElementType; public virtual Type GetElementType(Type sourceElementType) => ElementType; diff --git a/AgileMapper/Members/SourceMemberMatch.cs b/AgileMapper/Members/SourceMemberMatch.cs index bc462973a..ba76ed96f 100644 --- a/AgileMapper/Members/SourceMemberMatch.cs +++ b/AgileMapper/Members/SourceMemberMatch.cs @@ -5,12 +5,13 @@ #else using System.Linq.Expressions; #endif + using AgileMapper.Extensions.Internal; using DataSources; - using Extensions.Internal; + using Extensions; internal class SourceMemberMatch { - public static readonly SourceMemberMatch Null = new SourceMemberMatch(); + public static readonly SourceMemberMatch Null = new(); private SourceMemberMatch() { diff --git a/AgileMapper/Members/SourceMemberMatchContext.cs b/AgileMapper/Members/SourceMemberMatchContext.cs index a2bb6dc9b..1be112f75 100644 --- a/AgileMapper/Members/SourceMemberMatchContext.cs +++ b/AgileMapper/Members/SourceMemberMatchContext.cs @@ -6,9 +6,9 @@ #else using System.Linq.Expressions; #endif + using AgileMapper.Extensions.Internal; using Configuration; using Configuration.MemberIgnores; - using Extensions.Internal; using NetStandardPolyfills; internal class SourceMemberMatchContext diff --git a/AgileMapper/Members/SourceMemberMatcher.cs b/AgileMapper/Members/SourceMemberMatcher.cs index 8c4a61c02..a9991f286 100644 --- a/AgileMapper/Members/SourceMemberMatcher.cs +++ b/AgileMapper/Members/SourceMemberMatcher.cs @@ -3,7 +3,7 @@ using System; using System.Collections.Generic; using System.Linq; - using Extensions; + using AgileMapper.Extensions; using TypeConversion; internal static class SourceMemberMatcher diff --git a/AgileMapper/Members/Sources/ElementMembersSource.cs b/AgileMapper/Members/Sources/ElementMembersSource.cs index bcd687a6a..e42f5e19d 100644 --- a/AgileMapper/Members/Sources/ElementMembersSource.cs +++ b/AgileMapper/Members/Sources/ElementMembersSource.cs @@ -1,5 +1,6 @@ namespace AgileObjects.AgileMapper.Members.Sources { + using Extensions; using ObjectPopulation; internal class ElementMembersSource : IMembersSource diff --git a/AgileMapper/ObjectPopulation/Enumerables/Dictionaries/DictionaryPopulationBuilder.cs b/AgileMapper/ObjectPopulation/Enumerables/Dictionaries/DictionaryPopulationBuilder.cs index 10ec81a27..0ad31628a 100644 --- a/AgileMapper/ObjectPopulation/Enumerables/Dictionaries/DictionaryPopulationBuilder.cs +++ b/AgileMapper/ObjectPopulation/Enumerables/Dictionaries/DictionaryPopulationBuilder.cs @@ -14,6 +14,7 @@ using Looping; using Members; using Members.Dictionaries; + using Members.Extensions; using Members.Population; using TypeConversion; diff --git a/AgileMapper/ObjectPopulation/Enumerables/EnumerableMappingExpressionFactory.cs b/AgileMapper/ObjectPopulation/Enumerables/EnumerableMappingExpressionFactory.cs index b21370b8c..ae2b49e7f 100644 --- a/AgileMapper/ObjectPopulation/Enumerables/EnumerableMappingExpressionFactory.cs +++ b/AgileMapper/ObjectPopulation/Enumerables/EnumerableMappingExpressionFactory.cs @@ -7,6 +7,7 @@ namespace AgileObjects.AgileMapper.ObjectPopulation.Enumerables #endif using Extensions.Internal; using Members; + using Members.Extensions; using TypeConversion; internal class EnumerableMappingExpressionFactory : MappingExpressionFactoryBase diff --git a/AgileMapper/ObjectPopulation/MappingExpressionFactoryBase.cs b/AgileMapper/ObjectPopulation/MappingExpressionFactoryBase.cs index fde58f460..45f657f44 100644 --- a/AgileMapper/ObjectPopulation/MappingExpressionFactoryBase.cs +++ b/AgileMapper/ObjectPopulation/MappingExpressionFactoryBase.cs @@ -3,20 +3,19 @@ namespace AgileObjects.AgileMapper.ObjectPopulation using System; using System.Collections.Generic; using System.Linq; - using ComplexTypes.ShortCircuits; - using DataSources; #if NET35 using Microsoft.Scripting.Ast; #else using System.Linq.Expressions; #endif + using ComplexTypes.ShortCircuits; + using DataSources; using Enumerables.EnumerableExtensions; using Extensions; using Extensions.Internal; using Members; - using Members.MemberExtensions; + using Members.Extensions; using NetStandardPolyfills; - using ReadableExpressions; #if NET35 using static Microsoft.Scripting.Ast.ExpressionType; #else @@ -34,7 +33,7 @@ public Expression Create(IObjectMappingData mappingData) { var fallbackValue = GetNullMappingFallbackValue(mapperData); - return mappingData.MappingContext.IncludeCodeComments + return mappingData.MappingContext.PlanSettings.CommentUnmappableMembers ? Expression.Block(Comment(reason), fallbackValue) : fallbackValue; } diff --git a/AgileMapper/ObjectPopulation/MappingFactory.cs b/AgileMapper/ObjectPopulation/MappingFactory.cs index 5c94cd8b2..d1a773acd 100644 --- a/AgileMapper/ObjectPopulation/MappingFactory.cs +++ b/AgileMapper/ObjectPopulation/MappingFactory.cs @@ -8,7 +8,7 @@ using Caching.Dictionaries; using Extensions.Internal; using Members; - using Members.MemberExtensions; + using Members.Extensions; using NetStandardPolyfills; internal static class MappingFactory diff --git a/AgileMapper/ObjectPopulation/ObjectMapper.cs b/AgileMapper/ObjectPopulation/ObjectMapper.cs index 035f7f36d..d2bdd8c07 100644 --- a/AgileMapper/ObjectPopulation/ObjectMapper.cs +++ b/AgileMapper/ObjectPopulation/ObjectMapper.cs @@ -16,9 +16,11 @@ namespace AgileObjects.AgileMapper.ObjectPopulation internal class ObjectMapper : IObjectMapper { private readonly ObjectMapperKeyBase _mapperKey; - private readonly MapperFunc _mapperFunc; + private MapperFunc _mapperFunc; + private readonly object _lazyMapperFuncSync; private readonly ICache _subMappersByKey; private readonly ICache _repeatedMappingFuncsByKey; + private bool _mapperFuncCompiled; private Action _resetCallback; public ObjectMapper(Expression mapping, IObjectMappingData mappingData) @@ -31,7 +33,15 @@ public ObjectMapper(Expression mapping, IObjectMappingData mappingData) if (mapperDataContext.Compile) { - _mapperFunc = GetMappingLambda().Compile(); + if (mappingData.MappingContext.PlanSettings.LazyCompile) + { + _mapperFunc = LazyCompileMapperFunc; + _lazyMapperFuncSync = new object(); + } + else + { + _mapperFunc = CompileMapperFunc(); + } } else if (mapperDataContext.NeedsRuntimeTypedMapping) { @@ -54,6 +64,25 @@ public ObjectMapper(Expression mapping, IObjectMappingData mappingData) #region Setup + private TTarget LazyCompileMapperFunc(ObjectMappingData mappingData) + { + lock (_lazyMapperFuncSync) + { + if (_mapperFuncCompiled) + { + return Map(mappingData); + } + + _mapperFunc = CompileMapperFunc(); + _mapperFuncCompiled = true; + } + + return Map(mappingData); + } + + private MapperFunc CompileMapperFunc() + => GetMappingLambda().Compile(); + public void CacheRepeatedMappingFuncs() { // Using a for loop here because creation of a repeated mapping func can @@ -87,7 +116,7 @@ public void CacheRepeatedMappingFuncs() var mapperFunc = mapperFuncCreator.Invoke( mapperKey.MappingData, - mapperKey.MappingData.MappingContext.LazyLoadRepeatMappingFuncs); + mapperKey.MappingData.MappingContext.PlanSettings.LazyLoadRepeatMappingFuncs); _repeatedMappingFuncsByKey.GetOrAdd(mapperKey, _ => mapperFunc); } diff --git a/AgileMapper/ObjectPopulation/ObjectMapperData.cs b/AgileMapper/ObjectPopulation/ObjectMapperData.cs index 094d7464d..d530bee5b 100644 --- a/AgileMapper/ObjectPopulation/ObjectMapperData.cs +++ b/AgileMapper/ObjectPopulation/ObjectMapperData.cs @@ -15,6 +15,7 @@ namespace AgileObjects.AgileMapper.ObjectPopulation using Extensions.Internal; using MapperKeys; using Members; + using Members.Extensions; using Members.Sources; using NetStandardPolyfills; using ReadableExpressions.Extensions; diff --git a/AgileMapper/ObjectPopulation/ObjectMappingData.cs b/AgileMapper/ObjectPopulation/ObjectMappingData.cs index 817928197..74a76245a 100644 --- a/AgileMapper/ObjectPopulation/ObjectMappingData.cs +++ b/AgileMapper/ObjectPopulation/ObjectMappingData.cs @@ -12,6 +12,7 @@ namespace AgileObjects.AgileMapper.ObjectPopulation using Extensions.Internal; using MapperKeys; using Members; + using Members.Extensions; using NetStandardPolyfills; using Validation; #if NET35 diff --git a/AgileMapper/ObjectPopulation/ObjectMappingDataFactory.cs b/AgileMapper/ObjectPopulation/ObjectMappingDataFactory.cs index dfd4e8d7a..dccf05d01 100644 --- a/AgileMapper/ObjectPopulation/ObjectMappingDataFactory.cs +++ b/AgileMapper/ObjectPopulation/ObjectMappingDataFactory.cs @@ -13,6 +13,7 @@ namespace AgileObjects.AgileMapper.ObjectPopulation using Extensions.Internal; using MapperKeys; using Members; + using Members.Extensions; using Members.Sources; using NetStandardPolyfills; diff --git a/AgileMapper/ObjectPopulation/SimpleMemberMapperData.cs b/AgileMapper/ObjectPopulation/SimpleMemberMapperData.cs index 36e98b2c7..3ae10eabc 100644 --- a/AgileMapper/ObjectPopulation/SimpleMemberMapperData.cs +++ b/AgileMapper/ObjectPopulation/SimpleMemberMapperData.cs @@ -7,6 +7,7 @@ namespace AgileObjects.AgileMapper.ObjectPopulation #endif using Extensions.Internal; using Members; + using Members.Extensions; using Members.Sources; internal class SimpleMemberMapperData : MemberMapperDataBase, IMemberMapperData diff --git a/AgileMapper/Plans/MappingPlanSettings.cs b/AgileMapper/Plans/MappingPlanSettings.cs new file mode 100644 index 000000000..eea891176 --- /dev/null +++ b/AgileMapper/Plans/MappingPlanSettings.cs @@ -0,0 +1,49 @@ +namespace AgileObjects.AgileMapper.Plans +{ + /// + /// Provides options to control how a mapping plan should be created. + /// + public class MappingPlanSettings + { + internal static readonly MappingPlanSettings EagerPlanned = new() + { + LazyCompile = false, + CommentUnmappableMembers = true, + CommentUnmappedMembers = true, + LazyLoadRepeatMappingFuncs = false + }; + + internal static readonly MappingPlanSettings LazyPlanned = new() + { + LazyCompile = false, + CommentUnmappableMembers = false, + CommentUnmappedMembers = false, + LazyLoadRepeatMappingFuncs = true + }; + + /// + /// Gets or sets a value indicating whether the mapping plan should lazy-compile its + /// generated Expression tree into the Func with which mapping is performed. If true, the + /// mapping Func will be compiled from the mapping Expression tree when it is first needed,. + /// instead of when the plan is created. Defaults to false for an upfront-cached mapping + /// plan. + /// + public bool LazyCompile { get; set; } + + /// + /// Gets or sets a value indicating whether to include code comments explaining why any + /// unmappable members cannot be mapped, e.g if a complex type member has no usable + /// constructor. Defaults to true for an upfront-cached mapping plan. + /// + public bool CommentUnmappableMembers { get; set; } + + /// + /// Gets or sets a value indicating whether to include code comments noting why any unmapped + /// members are not mapped, e.g if they've been explicitly ignored, or have no matching + /// source values. Defaults to true for an upfront-cached mapping plan. + /// + public bool CommentUnmappedMembers { get; set; } + + internal bool LazyLoadRepeatMappingFuncs { get; set; } + } +} diff --git a/AgileMapper/Queryables/Converters/ComplexTypeToNullComparisonConverter.cs b/AgileMapper/Queryables/Converters/ComplexTypeToNullComparisonConverter.cs index c59ff778f..a18b7ccc3 100644 --- a/AgileMapper/Queryables/Converters/ComplexTypeToNullComparisonConverter.cs +++ b/AgileMapper/Queryables/Converters/ComplexTypeToNullComparisonConverter.cs @@ -8,6 +8,7 @@ #endif using Extensions.Internal; using Members; + using Members.Extensions; using ReadableExpressions.Extensions; internal static class ComplexTypeToNullComparisonConverter diff --git a/AgileMapper/SimpleMappingContext.cs b/AgileMapper/SimpleMappingContext.cs index 60919016e..31502f4f0 100644 --- a/AgileMapper/SimpleMappingContext.cs +++ b/AgileMapper/SimpleMappingContext.cs @@ -1,22 +1,28 @@ namespace AgileObjects.AgileMapper { + using Plans; + internal class SimpleMappingContext : IMappingContext { public SimpleMappingContext(MappingRuleSet ruleSet, MapperContext mapperContext) + : this(ruleSet, MappingPlanSettings.LazyPlanned, mapperContext) + { + } + + public SimpleMappingContext( + MappingRuleSet ruleSet, + MappingPlanSettings planSettings, + MapperContext mapperContext) { MapperContext = mapperContext; RuleSet = ruleSet; - IgnoreUnsuccessfulMemberPopulations = true; + PlanSettings = planSettings; } public MapperContext MapperContext { get; } public MappingRuleSet RuleSet { get; } - public bool IncludeCodeComments { get; set; } - - public bool IgnoreUnsuccessfulMemberPopulations { get; set; } - - public bool LazyLoadRepeatMappingFuncs => false; + public MappingPlanSettings PlanSettings { get; } } } \ No newline at end of file diff --git a/AgileMapper/TypeConversion/TypeConversionExtensions.cs b/AgileMapper/TypeConversion/TypeConversionExtensions.cs index 5425f53b1..95e7699d2 100644 --- a/AgileMapper/TypeConversion/TypeConversionExtensions.cs +++ b/AgileMapper/TypeConversion/TypeConversionExtensions.cs @@ -13,7 +13,7 @@ using Extensions; using Extensions.Internal; using Members; - using Members.MemberExtensions; + using Members.Extensions; using NetStandardPolyfills; using ObjectPopulation; diff --git a/AgileMapper/Validation/EnumMappingMismatchFinder.cs b/AgileMapper/Validation/EnumMappingMismatchFinder.cs index e0c5683e0..9bfb6a98f 100644 --- a/AgileMapper/Validation/EnumMappingMismatchFinder.cs +++ b/AgileMapper/Validation/EnumMappingMismatchFinder.cs @@ -13,9 +13,9 @@ using Extensions; using Extensions.Internal; using Members; + using Members.Extensions; using NetStandardPolyfills; using ObjectPopulation; - using ReadableExpressions.Extensions; internal class EnumMappingMismatchFinder : ExpressionVisitor { diff --git a/AgileMapper/Validation/EnumMappingMismatchSet.cs b/AgileMapper/Validation/EnumMappingMismatchSet.cs index 30f28a589..719f02de1 100644 --- a/AgileMapper/Validation/EnumMappingMismatchSet.cs +++ b/AgileMapper/Validation/EnumMappingMismatchSet.cs @@ -12,6 +12,7 @@ using Extensions; using Extensions.Internal; using Members; + using Members.Extensions; using NetStandardPolyfills; using ReadableExpressions; @@ -20,7 +21,7 @@ internal class EnumMappingMismatchSet public static readonly IEqualityComparer Comparer = default(MismatchSetComparer); private static readonly EnumMappingMismatchSet _emptySet = - new EnumMappingMismatchSet(Enumerable.EmptyArray); + new (Enumerable.EmptyArray); private readonly IList _mappingMismatches; private Expression _warning; diff --git a/AgileMapper/Validation/MappingValidator.cs b/AgileMapper/Validation/MappingValidator.cs index 89e9785f9..745408335 100644 --- a/AgileMapper/Validation/MappingValidator.cs +++ b/AgileMapper/Validation/MappingValidator.cs @@ -8,6 +8,7 @@ using Extensions; using Extensions.Internal; using Members; + using Members.Extensions; using NetStandardPolyfills; using ObjectPopulation; using ReadableExpressions.Extensions; @@ -24,10 +25,8 @@ public static void Validate(Mapper mapper) VerifyMappingPlanIsComplete(GetAllMapperDatas(rootMapperDatas)); } - public static void Validate(ObjectMapperData mapperData) - { - VerifyMappingPlanIsComplete(GetAllMapperDatas(new[] { mapperData })); - } + public static void Validate(ObjectMapperData mapperData) + => VerifyMappingPlanIsComplete(GetAllMapperDatas(new[] { mapperData })); public static void Validate(MappingConfigInfo configInfo) { From 6589b899bd1ba30b94c989be16b882b4e8897015 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Wed, 12 May 2021 18:18:31 +0100 Subject: [PATCH 19/65] Test coverage for building a simple create new enumerable mapper --- .../WhenBuildingEnumerableCreateNewMappers.cs | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 AgileMapper.Buildable.UnitTests/WhenBuildingEnumerableCreateNewMappers.cs diff --git a/AgileMapper.Buildable.UnitTests/WhenBuildingEnumerableCreateNewMappers.cs b/AgileMapper.Buildable.UnitTests/WhenBuildingEnumerableCreateNewMappers.cs new file mode 100644 index 000000000..121527e4a --- /dev/null +++ b/AgileMapper.Buildable.UnitTests/WhenBuildingEnumerableCreateNewMappers.cs @@ -0,0 +1,44 @@ +namespace AgileObjects.AgileMapper.Buildable.UnitTests +{ + using System.Collections.Generic; + using System.Collections.ObjectModel; + using AgileMapper.UnitTests.Common; + using Plans; + using Xunit; + + public class WhenBuildingEnumerableCreateNewMappers + { + [Fact] + public void ShouldBuildSimpleTypeListToCollectionMapper() + { + using (var mapper = Mapper.CreateNew()) + { + mapper.GetPlanFor>().ToANew>(new MappingPlanSettings + { + LazyCompile = true + }); + + var sourceCodeExpressions = mapper.BuildSourceCode(); + + var staticMapperClass = sourceCodeExpressions + .ShouldCompileAStaticMapperClass(); + + var staticMapMethod = staticMapperClass + .GetMapMethods() + .ShouldHaveSingleItem(); + + var source = new List { "3", "2", "1", "12345" }; + + var executor = staticMapMethod + .ShouldCreateMappingExecutor(source); + + var result = executor + .ShouldHaveACreateNewMethod() + .ShouldExecuteACreateNewMapping>(executor); + + result.ShouldNotBeNull(); + result.ShouldBe(3, 2, 1, null); + } + } + } +} From 55a9d014ecfacc89ab225130e2e227116f883327 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Wed, 12 May 2021 20:48:28 +0100 Subject: [PATCH 20/65] Support for single-target create new mapping to arrays --- .../WhenBuildingCircularReferenceMappers.cs | 2 +- ...WhenBuildingComplexTypeCreateNewMappers.cs | 4 +- .../WhenBuildingComplexTypeMergeMappers.cs | 2 +- ...WhenBuildingComplexTypeOverwriteMappers.cs | 2 +- .../WhenBuildingDerivedTypeMappers.cs | 2 +- .../WhenBuildingEnumerableCreateNewMappers.cs | 74 ++++++++++++++++++- .../BuildableMapperExtensions.cs | 18 +++-- .../WhenMappingToNewEnumerables.cs | 18 ++--- AgileMapper/Plans/MappingPlanSettings.cs | 2 +- 9 files changed, 97 insertions(+), 27 deletions(-) diff --git a/AgileMapper.Buildable.UnitTests/WhenBuildingCircularReferenceMappers.cs b/AgileMapper.Buildable.UnitTests/WhenBuildingCircularReferenceMappers.cs index 14c65f136..cf118bd67 100644 --- a/AgileMapper.Buildable.UnitTests/WhenBuildingCircularReferenceMappers.cs +++ b/AgileMapper.Buildable.UnitTests/WhenBuildingCircularReferenceMappers.cs @@ -13,7 +13,7 @@ public void ShouldBuildACircularReferenceMapper() { mapper.GetPlanFor().ToANew(); - var sourceCodeExpressions = mapper.BuildSourceCode(); + var sourceCodeExpressions = mapper.GetPlanSourceCodeInCache(); var staticMapperClass = sourceCodeExpressions .ShouldCompileAStaticMapperClass(); diff --git a/AgileMapper.Buildable.UnitTests/WhenBuildingComplexTypeCreateNewMappers.cs b/AgileMapper.Buildable.UnitTests/WhenBuildingComplexTypeCreateNewMappers.cs index f80ccb49a..4faf934ce 100644 --- a/AgileMapper.Buildable.UnitTests/WhenBuildingComplexTypeCreateNewMappers.cs +++ b/AgileMapper.Buildable.UnitTests/WhenBuildingComplexTypeCreateNewMappers.cs @@ -15,7 +15,7 @@ public void ShouldBuildSingleSourceSingleTargetMapper() { mapper.GetPlanFor>().ToANew>(); - var sourceCodeExpressions = mapper.BuildSourceCode(); + var sourceCodeExpressions = mapper.GetPlanSourceCodeInCache(); var staticMapperClass = sourceCodeExpressions .ShouldCompileAStaticMapperClass(); @@ -45,7 +45,7 @@ public void ShouldBuildSingleSourceMultipleTargetMapper() mapper.GetPlanFor>().ToANew>(); mapper.GetPlanFor>().ToANew>(); - var sourceCodeExpressions = mapper.BuildSourceCode(); + var sourceCodeExpressions = mapper.GetPlanSourceCodeInCache(); var staticMapperClass = sourceCodeExpressions .ShouldCompileAStaticMapperClass(); diff --git a/AgileMapper.Buildable.UnitTests/WhenBuildingComplexTypeMergeMappers.cs b/AgileMapper.Buildable.UnitTests/WhenBuildingComplexTypeMergeMappers.cs index 84ba04c39..16ee82801 100644 --- a/AgileMapper.Buildable.UnitTests/WhenBuildingComplexTypeMergeMappers.cs +++ b/AgileMapper.Buildable.UnitTests/WhenBuildingComplexTypeMergeMappers.cs @@ -13,7 +13,7 @@ public void ShouldBuildASingleSourceSingleTargetMapper() { mapper.GetPlanFor
().OnTo
(); - var sourceCodeExpressions = mapper.BuildSourceCode(); + var sourceCodeExpressions = mapper.GetPlanSourceCodeInCache(); var staticMapperClass = sourceCodeExpressions .ShouldCompileAStaticMapperClass(); diff --git a/AgileMapper.Buildable.UnitTests/WhenBuildingComplexTypeOverwriteMappers.cs b/AgileMapper.Buildable.UnitTests/WhenBuildingComplexTypeOverwriteMappers.cs index 24a430b43..c28218bbb 100644 --- a/AgileMapper.Buildable.UnitTests/WhenBuildingComplexTypeOverwriteMappers.cs +++ b/AgileMapper.Buildable.UnitTests/WhenBuildingComplexTypeOverwriteMappers.cs @@ -13,7 +13,7 @@ public void ShouldBuildASingleSourceSingleTargetMapper() { mapper.GetPlanFor
().Over
(); - var sourceCodeExpressions = mapper.BuildSourceCode(); + var sourceCodeExpressions = mapper.GetPlanSourceCodeInCache(); var staticMapperClass = sourceCodeExpressions .ShouldCompileAStaticMapperClass(); diff --git a/AgileMapper.Buildable.UnitTests/WhenBuildingDerivedTypeMappers.cs b/AgileMapper.Buildable.UnitTests/WhenBuildingDerivedTypeMappers.cs index a7678a798..7c4e4f294 100644 --- a/AgileMapper.Buildable.UnitTests/WhenBuildingDerivedTypeMappers.cs +++ b/AgileMapper.Buildable.UnitTests/WhenBuildingDerivedTypeMappers.cs @@ -16,7 +16,7 @@ public void ShouldBuildADerivedTypeCreateNewMapper() new MappingPlanSettings { LazyCompile = true }, cfg => cfg.Map().To()); - var sourceCodeExpressions = mapper.BuildSourceCode(); + var sourceCodeExpressions = mapper.GetPlanSourceCodeInCache(); var staticMapperClass = sourceCodeExpressions .ShouldCompileAStaticMapperClass(); diff --git a/AgileMapper.Buildable.UnitTests/WhenBuildingEnumerableCreateNewMappers.cs b/AgileMapper.Buildable.UnitTests/WhenBuildingEnumerableCreateNewMappers.cs index 121527e4a..70bbdf201 100644 --- a/AgileMapper.Buildable.UnitTests/WhenBuildingEnumerableCreateNewMappers.cs +++ b/AgileMapper.Buildable.UnitTests/WhenBuildingEnumerableCreateNewMappers.cs @@ -1,5 +1,6 @@ namespace AgileObjects.AgileMapper.Buildable.UnitTests { + using System; using System.Collections.Generic; using System.Collections.ObjectModel; using AgileMapper.UnitTests.Common; @@ -9,7 +10,7 @@ public class WhenBuildingEnumerableCreateNewMappers { [Fact] - public void ShouldBuildSimpleTypeListToCollectionMapper() + public void ShouldBuildASimpleTypeListToCollectionMapper() { using (var mapper = Mapper.CreateNew()) { @@ -18,7 +19,7 @@ public void ShouldBuildSimpleTypeListToCollectionMapper() LazyCompile = true }); - var sourceCodeExpressions = mapper.BuildSourceCode(); + var sourceCodeExpressions = mapper.GetPlanSourceCodeInCache(); var staticMapperClass = sourceCodeExpressions .ShouldCompileAStaticMapperClass(); @@ -40,5 +41,74 @@ public void ShouldBuildSimpleTypeListToCollectionMapper() result.ShouldBe(3, 2, 1, null); } } + + [Fact] + public void ShouldBuildASimpleTypeArrayToReadOnlyCollectionMapper() + { + using (var mapper = Mapper.CreateNew()) + { + mapper.GetPlanFor().ToANew>(new MappingPlanSettings + { + LazyCompile = true + }); + + var sourceCodeExpressions = mapper.GetPlanSourceCodeInCache(); + + var staticMapperClass = sourceCodeExpressions + .ShouldCompileAStaticMapperClass(); + + var staticMapMethod = staticMapperClass + .GetMapMethods() + .ShouldHaveSingleItem(); + + var source = new[] { 1, 2, 3 }; + + var executor = staticMapMethod + .ShouldCreateMappingExecutor(source); + + var result = executor + .ShouldHaveACreateNewMethod() + .ShouldExecuteACreateNewMapping>(executor); + + result.ShouldNotBeNull(); + result.ShouldBe(1, 2, 3); + } + } + + [Fact] + public void ShouldBuildASimpleTypeHashSetToArrayMapper() + { + using (var mapper = Mapper.CreateNew()) + { + mapper.GetPlanFor>().ToANew(new MappingPlanSettings + { + LazyCompile = true + }); + + var sourceCodeExpressions = mapper.GetPlanSourceCodeInCache(); + + var staticMapperClass = sourceCodeExpressions + .ShouldCompileAStaticMapperClass(); + + var staticMapMethod = staticMapperClass + .GetMapMethods() + .ShouldHaveSingleItem(); + + var today = DateTime.Today; + var tomorrow = today.AddDays(+1); + var yesterday = today.AddDays(-1); + + var source = new HashSet { yesterday, today, tomorrow }; + + var executor = staticMapMethod + .ShouldCreateMappingExecutor(source); + + var result = executor + .ShouldHaveACreateNewMethod() + .ShouldExecuteACreateNewMapping(executor); + + result.ShouldNotBeNull().ShouldBe(yesterday, today, tomorrow); + } + } } } diff --git a/AgileMapper.Buildable/BuildableMapperExtensions.cs b/AgileMapper.Buildable/BuildableMapperExtensions.cs index dbb3ca982..a416bb498 100644 --- a/AgileMapper.Buildable/BuildableMapperExtensions.cs +++ b/AgileMapper.Buildable/BuildableMapperExtensions.cs @@ -29,7 +29,7 @@ public static class BuildableMapperExtensions /// /// A for each mapper configured in this . /// - public static IEnumerable BuildSourceCode( + public static IEnumerable GetPlanSourceCodeInCache( this IMapper mapper) { yield return BuildableExpression @@ -82,7 +82,8 @@ public static IEnumerable BuildSourceCode( { doMapping.SetVisibility(Private); doMapping.SetStatic(); - doMapping.SetBody(plan.Root.Mapping); + + doMapping.SetBody(plan.Root.Mapping, typeof(object)); })); } @@ -159,17 +160,19 @@ private static void AddCreateNewMapMethod( { mapperClass.AddMethod("ToANew", mapNewMethod => { - var hasSingleTarget = mapMethodInfos.Count == 1; + var useTypeConstraint = + mapMethodInfos.Count == 1 && + !mapMethodInfos[0].TargetType.IsArray; var targetGenericParameter = mapNewMethod.AddGenericParameter("TTarget", param => { - if (hasSingleTarget) + if (useTypeConstraint) { param.AddTypeConstraints(mapMethodInfos[0].TargetType); } }); - if (hasSingleTarget) + if (useTypeConstraint) { mapNewMethod.SetBody(mapMethodInfos[0].CreateMapCall(Default)); return; @@ -186,7 +189,10 @@ private static void AddCreateNewMapMethod( var targetType = mapMethodInfo.TargetType; var typeofTargetType = BuildableExpression.TypeOf(targetType); - var typesAssignable = Call(IsAssignableToMethod, typeofTarget, typeofTargetType); + + var typesAssignable = targetType.IsSealed() + ? (Expression)Equal(typeofTarget, typeofTargetType) + : Call(IsAssignableToMethod, typeofTarget, typeofTargetType); var mapCall = mapMethodInfo.CreateMapCall(Default); var mapCallResultAsObject = Convert(mapCall, typeof(object)); diff --git a/AgileMapper.UnitTests/WhenMappingToNewEnumerables.cs b/AgileMapper.UnitTests/WhenMappingToNewEnumerables.cs index e47c2ab66..d4fa546be 100644 --- a/AgileMapper.UnitTests/WhenMappingToNewEnumerables.cs +++ b/AgileMapper.UnitTests/WhenMappingToNewEnumerables.cs @@ -43,8 +43,7 @@ public void ShouldCreateAConvertedSimpleTypeCollection() var source = new List { "1", "2", "3" }; var result = Mapper.Map(source).ToANew>(); - result.ShouldNotBeNull(); - result.ShouldBe(1, 2, 3); + result.ShouldNotBeNull().ShouldBe(1, 2, 3); } [Fact] @@ -103,8 +102,7 @@ public void ShouldCreateAReadOnlyCollection() var source = new[] { 1, 2, 3 }; var result = Mapper.Map(source).ToANew>(); - result.ShouldNotBeNull(); - result.ShouldBe(1, 2, 3); + result.ShouldNotBeNull().ShouldBe(1, 2, 3); } [Fact] @@ -113,8 +111,7 @@ public void ShouldMapFromAReadOnlyCollection() var source = new ReadOnlyCollection(new[] { 1, 2, 3L }); var result = Mapper.Map(source).ToANew(); - result.ShouldNotBeNull(); - result.ShouldBe(1, 2, 3); + result.ShouldNotBeNull().ShouldBe(1, 2, 3); } #if !NET35 @@ -124,8 +121,7 @@ public void ShouldCreateAnIReadOnlyCollection() var source = new[] { 1, 2, 3 }; var result = Mapper.Map(source).ToANew>(); - result.ShouldNotBeNull(); - result.ShouldBe((short)1, (short)2, (short)3); + result.ShouldNotBeNull().ShouldBe(1, 2, 3); } #endif [Fact] @@ -138,8 +134,7 @@ public void ShouldMapFromAHashset() var source = new HashSet { yesterday, today, tomorrow }; var result = Mapper.Map(source).ToANew(); - result.ShouldNotBeNull(); - result.ShouldBe(yesterday, today, tomorrow); + result.ShouldNotBeNull().ShouldBe(yesterday, today, tomorrow); } #if !NET35 @@ -149,8 +144,7 @@ public void ShouldMapToAnISet() var source = new[] { "1", "2", "3" }; var result = Mapper.Map(source).ToANew>(); - result.ShouldNotBeNull(); - result.ShouldBe(1L, 2L, 3L); + result.ShouldNotBeNull().ShouldBe(1L, 2L, 3L); } #endif [Fact] diff --git a/AgileMapper/Plans/MappingPlanSettings.cs b/AgileMapper/Plans/MappingPlanSettings.cs index eea891176..5366cd0c2 100644 --- a/AgileMapper/Plans/MappingPlanSettings.cs +++ b/AgileMapper/Plans/MappingPlanSettings.cs @@ -24,7 +24,7 @@ public class MappingPlanSettings /// /// Gets or sets a value indicating whether the mapping plan should lazy-compile its /// generated Expression tree into the Func with which mapping is performed. If true, the - /// mapping Func will be compiled from the mapping Expression tree when it is first needed,. + /// mapping Func will be compiled from the mapping Expression tree when it is first needed, /// instead of when the plan is created. Defaults to false for an upfront-cached mapping /// plan. /// From 45df504792c5da4fc48926295735302f06d704f9 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Thu, 13 May 2021 09:47:51 +0100 Subject: [PATCH 21/65] Test coverage for complex type enumerable ToANew mapping --- .../WhenBuildingEnumerableCreateNewMappers.cs | 43 +++++++++++++++++++ .../BuildableMapperExtensions.cs | 3 +- .../FluentAssertionExtensions.cs | 4 +- AgileMapper/IMapper.cs | 7 +-- 4 files changed, 51 insertions(+), 6 deletions(-) diff --git a/AgileMapper.Buildable.UnitTests/WhenBuildingEnumerableCreateNewMappers.cs b/AgileMapper.Buildable.UnitTests/WhenBuildingEnumerableCreateNewMappers.cs index 70bbdf201..d4997d694 100644 --- a/AgileMapper.Buildable.UnitTests/WhenBuildingEnumerableCreateNewMappers.cs +++ b/AgileMapper.Buildable.UnitTests/WhenBuildingEnumerableCreateNewMappers.cs @@ -3,7 +3,9 @@ using System; using System.Collections.Generic; using System.Collections.ObjectModel; + using System.Linq; using AgileMapper.UnitTests.Common; + using AgileMapper.UnitTests.Common.TestClasses; using Plans; using Xunit; @@ -110,5 +112,46 @@ public void ShouldBuildASimpleTypeHashSetToArrayMapper() result.ShouldNotBeNull().ShouldBe(yesterday, today, tomorrow); } } + + [Fact] + public void ShouldBuildAComplexTypeListToIListMapper() + { + using (var mapper = Mapper.CreateNew()) + { + mapper.GetPlanFor>().ToANew>(new MappingPlanSettings + { + LazyCompile = true + }); + + var sourceCodeExpressions = mapper.GetPlanSourceCodeInCache(); + + var staticMapperClass = sourceCodeExpressions + .ShouldCompileAStaticMapperClass(); + + var staticMapMethod = staticMapperClass + .GetMapMethods() + .ShouldHaveSingleItem(); + + var source = new List + { + new ProductDto { ProductId = "Surprise" }, + null, + new ProductDto { ProductId = "Boomstick" } + }; + + var executor = staticMapMethod + .ShouldCreateMappingExecutor(source); + + var result = executor + .ShouldHaveACreateNewMethod() + .ShouldExecuteACreateNewMapping>(executor); + + result.ShouldNotBeNull(); + result.ShouldNotBeSameAs(source); + result.First().ShouldNotBeNull().ProductId.ShouldBe("Surprise"); + result.Second().ShouldBeNull(); + result.Third().ShouldNotBeNull().ProductId.ShouldBe("Boomstick"); + } + } } } diff --git a/AgileMapper.Buildable/BuildableMapperExtensions.cs b/AgileMapper.Buildable/BuildableMapperExtensions.cs index a416bb498..8b773faad 100644 --- a/AgileMapper.Buildable/BuildableMapperExtensions.cs +++ b/AgileMapper.Buildable/BuildableMapperExtensions.cs @@ -82,8 +82,7 @@ public static IEnumerable GetPlanSourceCodeInCache( { doMapping.SetVisibility(Private); doMapping.SetStatic(); - - doMapping.SetBody(plan.Root.Mapping, typeof(object)); + doMapping.SetBody(plan.Root.Mapping); })); } diff --git a/AgileMapper.UnitTests.Common/FluentAssertionExtensions.cs b/AgileMapper.UnitTests.Common/FluentAssertionExtensions.cs index 140057b41..7bbbce535 100644 --- a/AgileMapper.UnitTests.Common/FluentAssertionExtensions.cs +++ b/AgileMapper.UnitTests.Common/FluentAssertionExtensions.cs @@ -178,13 +178,15 @@ public static void ShouldNotBe(this TActual value, TExpected } } - public static void ShouldNotBeSameAs(this T actualItem, T nonExpectedItem) + public static T ShouldNotBeSameAs(this T actualItem, T nonExpectedItem) where T : class { if (ReferenceEquals(nonExpectedItem, actualItem)) { Asplode("Not " + nonExpectedItem, nonExpectedItem.ToString()); } + + return actualItem; } public static T ShouldBeSameAs(this T actualItem, T expectedItem) diff --git a/AgileMapper/IMapper.cs b/AgileMapper/IMapper.cs index 05b2d1a17..069356b93 100644 --- a/AgileMapper/IMapper.cs +++ b/AgileMapper/IMapper.cs @@ -95,8 +95,8 @@ IProjectionPlanTargetSelector GetPlanForProjecting GetPlansFor(); /// - /// Returns a containing plans for all mapping functions currently - /// cached by the . + /// Returns a containing plans for all mapping functions + /// currently cached by the . /// /// /// A containing the currently-cached functions to be executed @@ -105,7 +105,8 @@ IProjectionPlanTargetSelector GetPlanForProjecting - /// Returns mapping plan Expressions for all mapping functions currently cached by the . + /// Returns mapping plan Expressions for all mapping functions currently cached by the + /// . ///
/// An Expression containing the currently-cached functions to be executed during mappings. ReadOnlyCollection GetPlanExpressionsInCache(); From be8a6f7ea827278b45d568fb1890a8e7fabb4a27 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Thu, 13 May 2021 10:23:25 +0100 Subject: [PATCH 22/65] Test coverage for building a create new dictionary mapper --- .../WhenBuildingDictionaryCreateNewMappers.cs | 46 +++++++++++++++++++ .../TestClasses/PublicTwoFields.cs | 2 +- .../Inline/WhenIgnoringSourceMembersInline.cs | 1 + 3 files changed, 48 insertions(+), 1 deletion(-) create mode 100644 AgileMapper.Buildable.UnitTests/Dictionaries/WhenBuildingDictionaryCreateNewMappers.cs rename {AgileMapper.UnitTests => AgileMapper.UnitTests.Common}/TestClasses/PublicTwoFields.cs (61%) diff --git a/AgileMapper.Buildable.UnitTests/Dictionaries/WhenBuildingDictionaryCreateNewMappers.cs b/AgileMapper.Buildable.UnitTests/Dictionaries/WhenBuildingDictionaryCreateNewMappers.cs new file mode 100644 index 000000000..e661f7e61 --- /dev/null +++ b/AgileMapper.Buildable.UnitTests/Dictionaries/WhenBuildingDictionaryCreateNewMappers.cs @@ -0,0 +1,46 @@ +namespace AgileObjects.AgileMapper.Buildable.UnitTests.Dictionaries +{ + using System.Collections.Generic; + using AgileMapper.UnitTests.Common; + using AgileMapper.UnitTests.Common.TestClasses; + using Xunit; + + public class WhenBuildingDictionaryCreateNewMappers + { + [Fact] + public void ShouldBuildANestedComplexTypeToStringDictionaryMapper() + { + using (var mapper = Mapper.CreateNew()) + { + mapper.GetPlanFor>().ToANew>(); + + var sourceCodeExpressions = mapper.GetPlanSourceCodeInCache(); + + var staticMapperClass = sourceCodeExpressions + .ShouldCompileAStaticMapperClass(); + + var staticMapMethod = staticMapperClass + .GetMapMethods() + .ShouldHaveSingleItem(); + + var source = new PublicTwoFields + { + Value1 = 12345, + Value2 = new Address { Line1 = "Line 1!", Line2 = "Line 2!" } + }; + + var executor = staticMapMethod + .ShouldCreateMappingExecutor(source); + + var result = executor + .ShouldHaveACreateNewMethod() + .ShouldExecuteACreateNewMapping>(executor); + + result.ShouldNotBeNull(); + result.ShouldContainKeyAndValue("Value1", "12345"); + result.ShouldContainKeyAndValue("Value2.Line1", "Line 1!"); + result.ShouldContainKeyAndValue("Value2.Line2", "Line 2!"); + } + } + } +} diff --git a/AgileMapper.UnitTests/TestClasses/PublicTwoFields.cs b/AgileMapper.UnitTests.Common/TestClasses/PublicTwoFields.cs similarity index 61% rename from AgileMapper.UnitTests/TestClasses/PublicTwoFields.cs rename to AgileMapper.UnitTests.Common/TestClasses/PublicTwoFields.cs index 7db5078dc..42e0b60eb 100644 --- a/AgileMapper.UnitTests/TestClasses/PublicTwoFields.cs +++ b/AgileMapper.UnitTests.Common/TestClasses/PublicTwoFields.cs @@ -1,4 +1,4 @@ -namespace AgileObjects.AgileMapper.UnitTests.TestClasses +namespace AgileObjects.AgileMapper.UnitTests.Common.TestClasses { public class PublicTwoFields { diff --git a/AgileMapper.UnitTests/Configuration/Inline/WhenIgnoringSourceMembersInline.cs b/AgileMapper.UnitTests/Configuration/Inline/WhenIgnoringSourceMembersInline.cs index b4cf2f8d5..0477170f6 100644 --- a/AgileMapper.UnitTests/Configuration/Inline/WhenIgnoringSourceMembersInline.cs +++ b/AgileMapper.UnitTests/Configuration/Inline/WhenIgnoringSourceMembersInline.cs @@ -1,6 +1,7 @@ namespace AgileObjects.AgileMapper.UnitTests.Configuration.Inline { using Common; + using Common.TestClasses; using TestClasses; #if !NET35 using Xunit; From 6599700e2df75ac8c206c99abe1e3240f05caa05 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Thu, 13 May 2021 10:42:12 +0100 Subject: [PATCH 23/65] Removing unused usings --- .../DataSources/WhenConfiguringMatcherDataSources.cs | 1 - .../WhenConfiguringMatcherDataSourcesIncorrectly.cs | 1 - .../DataSources/WhenConfiguringSequentialDataSources.cs | 1 - .../WhenConfiguringSequentialDataSourcesIncorrectly.cs | 1 - .../WhenConfiguringToTargetInsteadDataSources.cs | 1 - .../Inline/WhenConfiguringNameMatchingInline.cs | 1 - .../Inline/WhenConfiguringObjectMappingInline.cs | 1 - .../Inline/WhenConfiguringStringFormattingInline.cs | 1 - .../Inline/WhenIgnoringMembersInlineIncorrectly.cs | 1 - .../Inline/WhenIgnoringSourceMemberInlineIncorrectly.cs | 1 - .../Configuration/WhenConfiguringNameMatching.cs | 1 - .../Configuration/WhenConfiguringStringFormatting.cs | 1 - .../Configuration/WhenResolvingServices.cs | 1 - .../Dictionaries/WhenFlatteningToDictionaries.cs | 1 - .../Dictionaries/WhenMappingFromDictionaryMembers.cs | 1 - .../Dictionaries/WhenUnflatteningFromDictionaries.cs | 1 - .../Dynamics/WhenFlatteningToDynamics.cs | 1 - .../WhenMappingFromDynamicsOverEnumerableMembers.cs | 1 - .../Dynamics/WhenMappingOnToDynamicMembers.cs | 1 - .../Dynamics/WhenMappingOverDynamicMembers.cs | 1 - .../Dynamics/WhenMappingToNewDynamicMembers.cs | 1 - .../Extensions/WhenUnflatteningViaExtensionMethods.cs | 1 - .../MapperCloning/WhenCloningStringFormatting.cs | 1 - AgileMapper.UnitTests/WhenMappingToMetaMembers.cs | 1 - .../WhenUnflatteningFromQueryStrings.cs | 1 - AgileMapper/Api/Configuration/FactorySpecifier.cs | 1 - AgileMapper/Configuration/EnumComparisonFixer.cs | 1 - AgileMapper/Configuration/MapToNullCondition.cs | 3 +-- .../Mapping/RootEnumMappingDataSourceFactory.cs | 1 - AgileMapper/Extensions/PublicTypeExtensions.cs | 1 - .../AlreadyMappedObjectShortCircuitFactory.cs | 1 - .../SourceAdapters/SourceObjectDictionaryAdapter.cs | 1 - AgileMapper/Queryables/Converters/ToStringConverter.cs | 5 ++--- .../Settings/EntityFramework/Ef5QueryProviderSettings.cs | 1 - .../TypeConversion/NumericValueIsInRangeComparison.cs | 5 ++--- AgileMapper/TypeConversion/OperatorConverter.cs | 1 - AgileMapper/TypeConversion/ToNumericConverter.cs | 1 - AgileMapper/TypeConversion/ToStringConverter.cs | 9 ++++----- AgileMapper/TypeConversion/TryParseConverter.cs | 7 +++---- 39 files changed, 12 insertions(+), 51 deletions(-) diff --git a/AgileMapper.UnitTests/Configuration/DataSources/WhenConfiguringMatcherDataSources.cs b/AgileMapper.UnitTests/Configuration/DataSources/WhenConfiguringMatcherDataSources.cs index 25075aba8..6e7672c86 100644 --- a/AgileMapper.UnitTests/Configuration/DataSources/WhenConfiguringMatcherDataSources.cs +++ b/AgileMapper.UnitTests/Configuration/DataSources/WhenConfiguringMatcherDataSources.cs @@ -3,7 +3,6 @@ using System; using Common; using Common.TestClasses; - using TestClasses; #if !NET35 using Xunit; #else diff --git a/AgileMapper.UnitTests/Configuration/DataSources/WhenConfiguringMatcherDataSourcesIncorrectly.cs b/AgileMapper.UnitTests/Configuration/DataSources/WhenConfiguringMatcherDataSourcesIncorrectly.cs index 66fd8281a..d25eabbdd 100644 --- a/AgileMapper.UnitTests/Configuration/DataSources/WhenConfiguringMatcherDataSourcesIncorrectly.cs +++ b/AgileMapper.UnitTests/Configuration/DataSources/WhenConfiguringMatcherDataSourcesIncorrectly.cs @@ -3,7 +3,6 @@ using AgileMapper.Configuration; using Common; using Common.TestClasses; - using TestClasses; #if !NET35 using Xunit; #else diff --git a/AgileMapper.UnitTests/Configuration/DataSources/WhenConfiguringSequentialDataSources.cs b/AgileMapper.UnitTests/Configuration/DataSources/WhenConfiguringSequentialDataSources.cs index dd1521c2d..91e556e24 100644 --- a/AgileMapper.UnitTests/Configuration/DataSources/WhenConfiguringSequentialDataSources.cs +++ b/AgileMapper.UnitTests/Configuration/DataSources/WhenConfiguringSequentialDataSources.cs @@ -3,7 +3,6 @@ using AgileMapper.Extensions.Internal; using Common; using Common.TestClasses; - using TestClasses; #if !NET35 using Xunit; #else diff --git a/AgileMapper.UnitTests/Configuration/DataSources/WhenConfiguringSequentialDataSourcesIncorrectly.cs b/AgileMapper.UnitTests/Configuration/DataSources/WhenConfiguringSequentialDataSourcesIncorrectly.cs index 83000be87..744df6e15 100644 --- a/AgileMapper.UnitTests/Configuration/DataSources/WhenConfiguringSequentialDataSourcesIncorrectly.cs +++ b/AgileMapper.UnitTests/Configuration/DataSources/WhenConfiguringSequentialDataSourcesIncorrectly.cs @@ -4,7 +4,6 @@ using AgileMapper.Configuration; using Common; using Common.TestClasses; - using TestClasses; #if !NET35 using Xunit; using static WhenConfiguringSequentialDataSources; diff --git a/AgileMapper.UnitTests/Configuration/DataSources/WhenConfiguringToTargetInsteadDataSources.cs b/AgileMapper.UnitTests/Configuration/DataSources/WhenConfiguringToTargetInsteadDataSources.cs index 835bdff6a..5ee982755 100644 --- a/AgileMapper.UnitTests/Configuration/DataSources/WhenConfiguringToTargetInsteadDataSources.cs +++ b/AgileMapper.UnitTests/Configuration/DataSources/WhenConfiguringToTargetInsteadDataSources.cs @@ -6,7 +6,6 @@ using AgileMapper.Extensions; using Common; using Common.TestClasses; - using TestClasses; #if !NET35 using Xunit; #else diff --git a/AgileMapper.UnitTests/Configuration/Inline/WhenConfiguringNameMatchingInline.cs b/AgileMapper.UnitTests/Configuration/Inline/WhenConfiguringNameMatchingInline.cs index 282fb1386..75221393f 100644 --- a/AgileMapper.UnitTests/Configuration/Inline/WhenConfiguringNameMatchingInline.cs +++ b/AgileMapper.UnitTests/Configuration/Inline/WhenConfiguringNameMatchingInline.cs @@ -3,7 +3,6 @@ using AgileMapper.Configuration; using Common; using Common.TestClasses; - using TestClasses; #if !NET35 using Xunit; #else diff --git a/AgileMapper.UnitTests/Configuration/Inline/WhenConfiguringObjectMappingInline.cs b/AgileMapper.UnitTests/Configuration/Inline/WhenConfiguringObjectMappingInline.cs index 5e2689857..586954197 100644 --- a/AgileMapper.UnitTests/Configuration/Inline/WhenConfiguringObjectMappingInline.cs +++ b/AgileMapper.UnitTests/Configuration/Inline/WhenConfiguringObjectMappingInline.cs @@ -8,7 +8,6 @@ using Api.Configuration; using Common; using Common.TestClasses; - using TestClasses; #if !NET35 using Xunit; #else diff --git a/AgileMapper.UnitTests/Configuration/Inline/WhenConfiguringStringFormattingInline.cs b/AgileMapper.UnitTests/Configuration/Inline/WhenConfiguringStringFormattingInline.cs index dd3af2b2b..fbf8e879b 100644 --- a/AgileMapper.UnitTests/Configuration/Inline/WhenConfiguringStringFormattingInline.cs +++ b/AgileMapper.UnitTests/Configuration/Inline/WhenConfiguringStringFormattingInline.cs @@ -4,7 +4,6 @@ using AgileMapper.Configuration; using Common; using Common.TestClasses; - using TestClasses; #if !NET35 using Xunit; #else diff --git a/AgileMapper.UnitTests/Configuration/Inline/WhenIgnoringMembersInlineIncorrectly.cs b/AgileMapper.UnitTests/Configuration/Inline/WhenIgnoringMembersInlineIncorrectly.cs index 37e37f12b..7e3f96da5 100644 --- a/AgileMapper.UnitTests/Configuration/Inline/WhenIgnoringMembersInlineIncorrectly.cs +++ b/AgileMapper.UnitTests/Configuration/Inline/WhenIgnoringMembersInlineIncorrectly.cs @@ -3,7 +3,6 @@ using AgileMapper.Configuration; using Common; using Common.TestClasses; - using TestClasses; #if !NET35 using Xunit; #else diff --git a/AgileMapper.UnitTests/Configuration/Inline/WhenIgnoringSourceMemberInlineIncorrectly.cs b/AgileMapper.UnitTests/Configuration/Inline/WhenIgnoringSourceMemberInlineIncorrectly.cs index f0df0a2db..ce130565e 100644 --- a/AgileMapper.UnitTests/Configuration/Inline/WhenIgnoringSourceMemberInlineIncorrectly.cs +++ b/AgileMapper.UnitTests/Configuration/Inline/WhenIgnoringSourceMemberInlineIncorrectly.cs @@ -3,7 +3,6 @@ using AgileMapper.Configuration; using Common; using Common.TestClasses; - using TestClasses; #if !NET35 using Xunit; #else diff --git a/AgileMapper.UnitTests/Configuration/WhenConfiguringNameMatching.cs b/AgileMapper.UnitTests/Configuration/WhenConfiguringNameMatching.cs index 5f1f9cc85..cb5e1397a 100644 --- a/AgileMapper.UnitTests/Configuration/WhenConfiguringNameMatching.cs +++ b/AgileMapper.UnitTests/Configuration/WhenConfiguringNameMatching.cs @@ -6,7 +6,6 @@ using AgileMapper.Extensions.Internal; using Common; using Common.TestClasses; - using TestClasses; using static System.Environment; #if !NET35 using Xunit; diff --git a/AgileMapper.UnitTests/Configuration/WhenConfiguringStringFormatting.cs b/AgileMapper.UnitTests/Configuration/WhenConfiguringStringFormatting.cs index 5e19945a0..3f9f943ed 100644 --- a/AgileMapper.UnitTests/Configuration/WhenConfiguringStringFormatting.cs +++ b/AgileMapper.UnitTests/Configuration/WhenConfiguringStringFormatting.cs @@ -4,7 +4,6 @@ using AgileMapper.Configuration; using Common; using Common.TestClasses; - using TestClasses; #if !NET35 using Xunit; #else diff --git a/AgileMapper.UnitTests/Configuration/WhenResolvingServices.cs b/AgileMapper.UnitTests/Configuration/WhenResolvingServices.cs index ca4320804..d74eb11e5 100644 --- a/AgileMapper.UnitTests/Configuration/WhenResolvingServices.cs +++ b/AgileMapper.UnitTests/Configuration/WhenResolvingServices.cs @@ -6,7 +6,6 @@ using AgileMapper.Extensions; using Common; using Common.TestClasses; - using TestClasses; #if NETCOREAPP using Microsoft.Extensions.DependencyInjection; #endif diff --git a/AgileMapper.UnitTests/Dictionaries/WhenFlatteningToDictionaries.cs b/AgileMapper.UnitTests/Dictionaries/WhenFlatteningToDictionaries.cs index 08951714f..e8ad031e8 100644 --- a/AgileMapper.UnitTests/Dictionaries/WhenFlatteningToDictionaries.cs +++ b/AgileMapper.UnitTests/Dictionaries/WhenFlatteningToDictionaries.cs @@ -4,7 +4,6 @@ using System.Collections.Generic; using Common; using Common.TestClasses; - using TestClasses; #if !NET35 using Xunit; #else diff --git a/AgileMapper.UnitTests/Dictionaries/WhenMappingFromDictionaryMembers.cs b/AgileMapper.UnitTests/Dictionaries/WhenMappingFromDictionaryMembers.cs index fadc87cac..900fa8581 100644 --- a/AgileMapper.UnitTests/Dictionaries/WhenMappingFromDictionaryMembers.cs +++ b/AgileMapper.UnitTests/Dictionaries/WhenMappingFromDictionaryMembers.cs @@ -4,7 +4,6 @@ using System.Collections.Generic; using Common; using Common.TestClasses; - using TestClasses; #if !NET35 using Xunit; #else diff --git a/AgileMapper.UnitTests/Dictionaries/WhenUnflatteningFromDictionaries.cs b/AgileMapper.UnitTests/Dictionaries/WhenUnflatteningFromDictionaries.cs index d8416270b..c47fa76ab 100644 --- a/AgileMapper.UnitTests/Dictionaries/WhenUnflatteningFromDictionaries.cs +++ b/AgileMapper.UnitTests/Dictionaries/WhenUnflatteningFromDictionaries.cs @@ -4,7 +4,6 @@ using System.Collections.Generic; using Common; using Common.TestClasses; - using TestClasses; #if !NET35 using Xunit; #else diff --git a/AgileMapper.UnitTests/Dynamics/WhenFlatteningToDynamics.cs b/AgileMapper.UnitTests/Dynamics/WhenFlatteningToDynamics.cs index ff97e62c3..0458e2226 100644 --- a/AgileMapper.UnitTests/Dynamics/WhenFlatteningToDynamics.cs +++ b/AgileMapper.UnitTests/Dynamics/WhenFlatteningToDynamics.cs @@ -4,7 +4,6 @@ using Common; using Common.TestClasses; using Microsoft.CSharp.RuntimeBinder; - using TestClasses; using Xunit; public class WhenFlatteningToDynamics diff --git a/AgileMapper.UnitTests/Dynamics/WhenMappingFromDynamicsOverEnumerableMembers.cs b/AgileMapper.UnitTests/Dynamics/WhenMappingFromDynamicsOverEnumerableMembers.cs index 8431e76f1..1a7322365 100644 --- a/AgileMapper.UnitTests/Dynamics/WhenMappingFromDynamicsOverEnumerableMembers.cs +++ b/AgileMapper.UnitTests/Dynamics/WhenMappingFromDynamicsOverEnumerableMembers.cs @@ -7,7 +7,6 @@ namespace AgileObjects.AgileMapper.UnitTests.Dynamics using System.Linq; using Common; using Common.TestClasses; - using TestClasses; using Xunit; public class WhenMappingFromDynamicsOverEnumerableMembers diff --git a/AgileMapper.UnitTests/Dynamics/WhenMappingOnToDynamicMembers.cs b/AgileMapper.UnitTests/Dynamics/WhenMappingOnToDynamicMembers.cs index a5555d687..e27d3dfa7 100644 --- a/AgileMapper.UnitTests/Dynamics/WhenMappingOnToDynamicMembers.cs +++ b/AgileMapper.UnitTests/Dynamics/WhenMappingOnToDynamicMembers.cs @@ -6,7 +6,6 @@ using Common; using Common.TestClasses; using Microsoft.CSharp.RuntimeBinder; - using TestClasses; using Xunit; public class WhenMappingOnToDynamicMembers diff --git a/AgileMapper.UnitTests/Dynamics/WhenMappingOverDynamicMembers.cs b/AgileMapper.UnitTests/Dynamics/WhenMappingOverDynamicMembers.cs index 1b9e752a8..1072f863b 100644 --- a/AgileMapper.UnitTests/Dynamics/WhenMappingOverDynamicMembers.cs +++ b/AgileMapper.UnitTests/Dynamics/WhenMappingOverDynamicMembers.cs @@ -2,7 +2,6 @@ { using System.Dynamic; using Common.TestClasses; - using TestClasses; using Xunit; public class WhenMappingOverDynamicMembers diff --git a/AgileMapper.UnitTests/Dynamics/WhenMappingToNewDynamicMembers.cs b/AgileMapper.UnitTests/Dynamics/WhenMappingToNewDynamicMembers.cs index 0a0202c71..28328f949 100644 --- a/AgileMapper.UnitTests/Dynamics/WhenMappingToNewDynamicMembers.cs +++ b/AgileMapper.UnitTests/Dynamics/WhenMappingToNewDynamicMembers.cs @@ -2,7 +2,6 @@ { using System.Dynamic; using Common.TestClasses; - using TestClasses; using Xunit; public class WhenMappingToNewDynamicMembers diff --git a/AgileMapper.UnitTests/Extensions/WhenUnflatteningViaExtensionMethods.cs b/AgileMapper.UnitTests/Extensions/WhenUnflatteningViaExtensionMethods.cs index 9928631a3..ecd82867f 100644 --- a/AgileMapper.UnitTests/Extensions/WhenUnflatteningViaExtensionMethods.cs +++ b/AgileMapper.UnitTests/Extensions/WhenUnflatteningViaExtensionMethods.cs @@ -4,7 +4,6 @@ using AgileMapper.Extensions; using Common; using Common.TestClasses; - using TestClasses; #if !NET35 using Xunit; #else diff --git a/AgileMapper.UnitTests/MapperCloning/WhenCloningStringFormatting.cs b/AgileMapper.UnitTests/MapperCloning/WhenCloningStringFormatting.cs index 55b821a66..2100516d2 100644 --- a/AgileMapper.UnitTests/MapperCloning/WhenCloningStringFormatting.cs +++ b/AgileMapper.UnitTests/MapperCloning/WhenCloningStringFormatting.cs @@ -2,7 +2,6 @@ { using Common; using Common.TestClasses; - using TestClasses; #if !NET35 using Xunit; #else diff --git a/AgileMapper.UnitTests/WhenMappingToMetaMembers.cs b/AgileMapper.UnitTests/WhenMappingToMetaMembers.cs index abdd553f8..c7b0067be 100644 --- a/AgileMapper.UnitTests/WhenMappingToMetaMembers.cs +++ b/AgileMapper.UnitTests/WhenMappingToMetaMembers.cs @@ -7,7 +7,6 @@ using AgileMapper.Extensions; using Common; using Common.TestClasses; - using TestClasses; #if !NET35 using Xunit; #else diff --git a/AgileMapper.UnitTests/WhenUnflatteningFromQueryStrings.cs b/AgileMapper.UnitTests/WhenUnflatteningFromQueryStrings.cs index b0e0abf45..dd4dd9364 100644 --- a/AgileMapper.UnitTests/WhenUnflatteningFromQueryStrings.cs +++ b/AgileMapper.UnitTests/WhenUnflatteningFromQueryStrings.cs @@ -5,7 +5,6 @@ using AgileMapper.Extensions; using Common; using Common.TestClasses; - using TestClasses; #if !NET35 using Xunit; #else diff --git a/AgileMapper/Api/Configuration/FactorySpecifier.cs b/AgileMapper/Api/Configuration/FactorySpecifier.cs index 5f8ca8409..761d3f2e0 100644 --- a/AgileMapper/Api/Configuration/FactorySpecifier.cs +++ b/AgileMapper/Api/Configuration/FactorySpecifier.cs @@ -16,7 +16,6 @@ namespace AgileObjects.AgileMapper.Api.Configuration #else using LambdaExpr = System.Linq.Expressions.LambdaExpression; #endif - using static ObjectPopulation.InvocationPosition; internal class FactorySpecifier : IMappingFactorySpecifier, diff --git a/AgileMapper/Configuration/EnumComparisonFixer.cs b/AgileMapper/Configuration/EnumComparisonFixer.cs index 60bb522da..f85cea55f 100644 --- a/AgileMapper/Configuration/EnumComparisonFixer.cs +++ b/AgileMapper/Configuration/EnumComparisonFixer.cs @@ -9,7 +9,6 @@ using Caching.Dictionaries; using Extensions.Internal; using NetStandardPolyfills; - using ReadableExpressions.Extensions; internal class EnumComparisonFixer : ExpressionVisitor { diff --git a/AgileMapper/Configuration/MapToNullCondition.cs b/AgileMapper/Configuration/MapToNullCondition.cs index 93272e394..503cf8a12 100644 --- a/AgileMapper/Configuration/MapToNullCondition.cs +++ b/AgileMapper/Configuration/MapToNullCondition.cs @@ -1,13 +1,12 @@ namespace AgileObjects.AgileMapper.Configuration { using System; - using Members; - using ObjectPopulation; #if NET35 using Microsoft.Scripting.Ast; #else using System.Linq.Expressions; #endif + using Members; internal class MapToNullCondition : UserConfiguredItemBase diff --git a/AgileMapper/DataSources/Factories/Mapping/RootEnumMappingDataSourceFactory.cs b/AgileMapper/DataSources/Factories/Mapping/RootEnumMappingDataSourceFactory.cs index 27fb5c4a0..0ffe574d2 100644 --- a/AgileMapper/DataSources/Factories/Mapping/RootEnumMappingDataSourceFactory.cs +++ b/AgileMapper/DataSources/Factories/Mapping/RootEnumMappingDataSourceFactory.cs @@ -2,7 +2,6 @@ { using NetStandardPolyfills; using ObjectPopulation; - using ReadableExpressions.Extensions; internal class RootEnumMappingDataSourceFactory : MappingDataSourceFactoryBase { diff --git a/AgileMapper/Extensions/PublicTypeExtensions.cs b/AgileMapper/Extensions/PublicTypeExtensions.cs index 08406207a..b542ee8c6 100644 --- a/AgileMapper/Extensions/PublicTypeExtensions.cs +++ b/AgileMapper/Extensions/PublicTypeExtensions.cs @@ -4,7 +4,6 @@ using System.Reflection; using Internal; using NetStandardPolyfills; - using ReadableExpressions.Extensions; /// /// Provides mapping-related extension methods for Types. These methods support mapping, and are diff --git a/AgileMapper/ObjectPopulation/ComplexTypes/ShortCircuits/AlreadyMappedObjectShortCircuitFactory.cs b/AgileMapper/ObjectPopulation/ComplexTypes/ShortCircuits/AlreadyMappedObjectShortCircuitFactory.cs index 5c2df199f..cc8fe1827 100644 --- a/AgileMapper/ObjectPopulation/ComplexTypes/ShortCircuits/AlreadyMappedObjectShortCircuitFactory.cs +++ b/AgileMapper/ObjectPopulation/ComplexTypes/ShortCircuits/AlreadyMappedObjectShortCircuitFactory.cs @@ -5,7 +5,6 @@ namespace AgileObjects.AgileMapper.ObjectPopulation.ComplexTypes.ShortCircuits #else using System.Linq.Expressions; #endif - using Members; using NetStandardPolyfills; using ReadableExpressions.Extensions; diff --git a/AgileMapper/ObjectPopulation/Enumerables/Dictionaries/SourceAdapters/SourceObjectDictionaryAdapter.cs b/AgileMapper/ObjectPopulation/Enumerables/Dictionaries/SourceAdapters/SourceObjectDictionaryAdapter.cs index 820af2d92..43d7dce18 100644 --- a/AgileMapper/ObjectPopulation/Enumerables/Dictionaries/SourceAdapters/SourceObjectDictionaryAdapter.cs +++ b/AgileMapper/ObjectPopulation/Enumerables/Dictionaries/SourceAdapters/SourceObjectDictionaryAdapter.cs @@ -11,7 +11,6 @@ namespace AgileObjects.AgileMapper.ObjectPopulation.Enumerables.Dictionaries.Sou using Enumerables.SourceAdapters; using Extensions.Internal; using Looping; - using Members; using Members.Dictionaries; using NetStandardPolyfills; diff --git a/AgileMapper/Queryables/Converters/ToStringConverter.cs b/AgileMapper/Queryables/Converters/ToStringConverter.cs index b3d50f34e..e71eb345f 100644 --- a/AgileMapper/Queryables/Converters/ToStringConverter.cs +++ b/AgileMapper/Queryables/Converters/ToStringConverter.cs @@ -1,14 +1,13 @@ namespace AgileObjects.AgileMapper.Queryables.Converters { using System; - using Extensions.Internal; - using NetStandardPolyfills; - using ReadableExpressions.Extensions; #if NET35 using Microsoft.Scripting.Ast; #else using System.Linq.Expressions; #endif + using Extensions.Internal; + using NetStandardPolyfills; internal static class ToStringConverter { diff --git a/AgileMapper/Queryables/Settings/EntityFramework/Ef5QueryProviderSettings.cs b/AgileMapper/Queryables/Settings/EntityFramework/Ef5QueryProviderSettings.cs index de51dd668..8725a2b48 100644 --- a/AgileMapper/Queryables/Settings/EntityFramework/Ef5QueryProviderSettings.cs +++ b/AgileMapper/Queryables/Settings/EntityFramework/Ef5QueryProviderSettings.cs @@ -10,7 +10,6 @@ using System.Reflection; using Extensions.Internal; using NetStandardPolyfills; - using ReadableExpressions.Extensions; internal class Ef5QueryProviderSettings : LegacyEfQueryProviderSettings { diff --git a/AgileMapper/TypeConversion/NumericValueIsInRangeComparison.cs b/AgileMapper/TypeConversion/NumericValueIsInRangeComparison.cs index 6b854a565..3b6816c48 100644 --- a/AgileMapper/TypeConversion/NumericValueIsInRangeComparison.cs +++ b/AgileMapper/TypeConversion/NumericValueIsInRangeComparison.cs @@ -1,14 +1,13 @@ namespace AgileObjects.AgileMapper.TypeConversion { using System; - using Extensions.Internal; - using NetStandardPolyfills; - using ReadableExpressions.Extensions; #if NET35 using Microsoft.Scripting.Ast; #else using System.Linq.Expressions; #endif + using Extensions.Internal; + using NetStandardPolyfills; internal static class NumericValueIsInRangeComparison { diff --git a/AgileMapper/TypeConversion/OperatorConverter.cs b/AgileMapper/TypeConversion/OperatorConverter.cs index c73efdf06..f6b656776 100644 --- a/AgileMapper/TypeConversion/OperatorConverter.cs +++ b/AgileMapper/TypeConversion/OperatorConverter.cs @@ -9,7 +9,6 @@ namespace AgileObjects.AgileMapper.TypeConversion #endif using System.Reflection; using NetStandardPolyfills; - using ReadableExpressions.Extensions; internal struct OperatorConverter : IValueConverter { diff --git a/AgileMapper/TypeConversion/ToNumericConverter.cs b/AgileMapper/TypeConversion/ToNumericConverter.cs index 9915ef9a9..e87dc56d1 100644 --- a/AgileMapper/TypeConversion/ToNumericConverter.cs +++ b/AgileMapper/TypeConversion/ToNumericConverter.cs @@ -9,7 +9,6 @@ #endif using Extensions.Internal; using NetStandardPolyfills; - using ReadableExpressions.Extensions; internal class ToNumericConverter : TryParseConverter { diff --git a/AgileMapper/TypeConversion/ToStringConverter.cs b/AgileMapper/TypeConversion/ToStringConverter.cs index 2dbe76ac9..f2d169335 100644 --- a/AgileMapper/TypeConversion/ToStringConverter.cs +++ b/AgileMapper/TypeConversion/ToStringConverter.cs @@ -3,16 +3,15 @@ using System; using System.Globalization; using System.Linq; - using System.Reflection; - using Extensions; - using Extensions.Internal; - using NetStandardPolyfills; - using ReadableExpressions.Extensions; #if NET35 using Microsoft.Scripting.Ast; #else using System.Linq.Expressions; #endif + using System.Reflection; + using Extensions; + using Extensions.Internal; + using NetStandardPolyfills; internal struct ToStringConverter : IValueConverter { diff --git a/AgileMapper/TypeConversion/TryParseConverter.cs b/AgileMapper/TypeConversion/TryParseConverter.cs index f9a19f9e4..bfb4be672 100644 --- a/AgileMapper/TypeConversion/TryParseConverter.cs +++ b/AgileMapper/TypeConversion/TryParseConverter.cs @@ -1,15 +1,14 @@ namespace AgileObjects.AgileMapper.TypeConversion { using System; - using System.Reflection; - using Extensions.Internal; - using NetStandardPolyfills; - using ReadableExpressions.Extensions; #if NET35 using Microsoft.Scripting.Ast; #else using System.Linq.Expressions; #endif + using System.Reflection; + using Extensions.Internal; + using NetStandardPolyfills; internal class TryParseConverter : IValueConverter { From e1cc38b64ee52028f7af61f98a339914df3a8b74 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Thu, 13 May 2021 20:04:37 +0100 Subject: [PATCH 24/65] Support for building root enum mappers --- .../WhenBuildingRootEnumMappers.cs | 49 +++++++++++++++++++ .../BuildableMapperExtensions.cs | 19 +++++-- 2 files changed, 63 insertions(+), 5 deletions(-) create mode 100644 AgileMapper.Buildable.UnitTests/WhenBuildingRootEnumMappers.cs diff --git a/AgileMapper.Buildable.UnitTests/WhenBuildingRootEnumMappers.cs b/AgileMapper.Buildable.UnitTests/WhenBuildingRootEnumMappers.cs new file mode 100644 index 000000000..035a11fcc --- /dev/null +++ b/AgileMapper.Buildable.UnitTests/WhenBuildingRootEnumMappers.cs @@ -0,0 +1,49 @@ +namespace AgileObjects.AgileMapper.Buildable.UnitTests +{ + using AgileMapper.UnitTests.Common; + using AgileMapper.UnitTests.Common.TestClasses; + using Xunit; + + public class WhenBuildingRootEnumMappers + { + [Fact] + public void ShouldBuildARootEnumMapper() + { + using (var mapper = Mapper.CreateNew()) + { + mapper.GetPlanFor().ToANew(); + + var sourceCodeExpressions = mapper.GetPlanSourceCodeInCache(); + + var staticMapperClass = sourceCodeExpressions + .ShouldCompileAStaticMapperClass(); + + var staticMapMethod = staticMapperClass + .GetMapMethods() + .ShouldHaveSingleItem(); + + var enumIdSource = ((int)Title.Mrs).ToString(); + + var enumIdExecutor = staticMapMethod + .ShouldCreateMappingExecutor(enumIdSource); + + var enumIdResult = enumIdExecutor + .ShouldHaveACreateNewMethod() + .ShouldExecuteACreateNewMapping<Title>(enumIdExecutor); + + enumIdResult.ShouldBe(Title.Mrs); + + var enumLabelSource = Title.Master.ToString(); + + var enumLabelIdExecutor = staticMapMethod + .ShouldCreateMappingExecutor(enumLabelSource); + + var enumLabelResult = enumLabelIdExecutor + .ShouldHaveACreateNewMethod() + .ShouldExecuteACreateNewMapping<Title>(enumLabelIdExecutor); + + enumLabelResult.ShouldBe(Title.Master); + } + } + } +} diff --git a/AgileMapper.Buildable/BuildableMapperExtensions.cs b/AgileMapper.Buildable/BuildableMapperExtensions.cs index 8b773faad..1373097dd 100644 --- a/AgileMapper.Buildable/BuildableMapperExtensions.cs +++ b/AgileMapper.Buildable/BuildableMapperExtensions.cs @@ -159,9 +159,7 @@ private static void AddCreateNewMapMethod( { mapperClass.AddMethod("ToANew", mapNewMethod => { - var useTypeConstraint = - mapMethodInfos.Count == 1 && - !mapMethodInfos[0].TargetType.IsArray; + var useTypeConstraint = UseTypeConstraint(mapMethodInfos); var targetGenericParameter = mapNewMethod.AddGenericParameter("TTarget", param => { @@ -186,9 +184,8 @@ private static void AddCreateNewMapMethod( foreach (var mapMethodInfo in mapMethodInfos) { var targetType = mapMethodInfo.TargetType; - var typeofTargetType = BuildableExpression.TypeOf(targetType); - + var typesAssignable = targetType.IsSealed() ? (Expression)Equal(typeofTarget, typeofTargetType) : Call(IsAssignableToMethod, typeofTarget, typeofTargetType); @@ -209,6 +206,18 @@ private static void AddCreateNewMapMethod( }); } + private static bool UseTypeConstraint(IList<MapMethodInfo> mapMethodInfos) + { + if (mapMethodInfos.Count != 1) + { + return false; + } + + var targetType = mapMethodInfos[0].TargetType; + + return !(targetType.IsArray || targetType.IsEnum()); + } + private static Expression GetThrowTargetNotSupportedException( MapMethodInfo mapMethodInfo, TypeExpression targetGenericParameter) From 8b9b9656248363aa88514fb54d188c8e69b797a2 Mon Sep 17 00:00:00 2001 From: Steve Wilkes <steve@agileobjects.co.uk> Date: Fri, 14 May 2021 09:45:08 +0100 Subject: [PATCH 25/65] Improving buildable test API --- .../WhenBuildingDictionaryCreateNewMappers.cs | 2 +- .../FluentAssertionExtensions.cs | 96 +++++++++++-------- .../WhenBuildingCircularReferenceMappers.cs | 2 +- ...WhenBuildingComplexTypeCreateNewMappers.cs | 8 +- .../WhenBuildingComplexTypeMergeMappers.cs | 2 +- ...WhenBuildingComplexTypeOverwriteMappers.cs | 2 +- .../WhenBuildingDerivedTypeMappers.cs | 6 +- .../WhenBuildingEnumerableCreateNewMappers.cs | 8 +- .../WhenBuildingRootEnumMappers.cs | 4 +- .../WhenMappingOverEnumerables.cs | 17 ++-- 10 files changed, 79 insertions(+), 68 deletions(-) diff --git a/AgileMapper.Buildable.UnitTests/Dictionaries/WhenBuildingDictionaryCreateNewMappers.cs b/AgileMapper.Buildable.UnitTests/Dictionaries/WhenBuildingDictionaryCreateNewMappers.cs index e661f7e61..be6cdf63f 100644 --- a/AgileMapper.Buildable.UnitTests/Dictionaries/WhenBuildingDictionaryCreateNewMappers.cs +++ b/AgileMapper.Buildable.UnitTests/Dictionaries/WhenBuildingDictionaryCreateNewMappers.cs @@ -34,7 +34,7 @@ public void ShouldBuildANestedComplexTypeToStringDictionaryMapper() var result = executor .ShouldHaveACreateNewMethod() - .ShouldExecuteACreateNewMapping<Dictionary<string, string>>(executor); + .ShouldExecuteACreateNewMapping<Dictionary<string, string>>(); result.ShouldNotBeNull(); result.ShouldContainKeyAndValue("Value1", "12345"); diff --git a/AgileMapper.Buildable.UnitTests/FluentAssertionExtensions.cs b/AgileMapper.Buildable.UnitTests/FluentAssertionExtensions.cs index 7fbb38dcd..7157f98c6 100644 --- a/AgileMapper.Buildable.UnitTests/FluentAssertionExtensions.cs +++ b/AgileMapper.Buildable.UnitTests/FluentAssertionExtensions.cs @@ -39,63 +39,75 @@ public static MappingExecutionContextBase<TSource> ShouldCreateMappingExecutor<T .ShouldBeOfType<MappingExecutionContextBase<TSource>>(); } - public static MethodInfo ShouldHaveACreateNewMethod<TSource>( + public static ExecutorTester<TSource> ShouldHaveACreateNewMethod<TSource>( this MappingExecutionContextBase<TSource> executor) { - return executor.GetType() - .GetPublicInstanceMethods("ToANew") - .ShouldHaveSingleItem(); + return new ExecutorTester<TSource>( + executor, + executor.GetType() + .GetPublicInstanceMethods("ToANew") + .ShouldHaveSingleItem()); } - public static MethodInfo ShouldHaveAMergeMethod<TSource>( + public static ExecutorTester<TSource> ShouldHaveAMergeMethod<TSource>( this MappingExecutionContextBase<TSource> executor) { - return executor.GetType() - .GetPublicInstanceMethods("OnTo") - .ShouldHaveSingleItem(); + return new ExecutorTester<TSource>( + executor, + executor.GetType() + .GetPublicInstanceMethods("OnTo") + .ShouldHaveSingleItem()); } - public static MethodInfo ShouldHaveAnOverwriteMethod<TSource>( + public static ExecutorTester<TSource> ShouldHaveAnOverwriteMethod<TSource>( this MappingExecutionContextBase<TSource> executor) { - return executor.GetType() - .GetPublicInstanceMethods("Over") - .ShouldHaveSingleItem(); + return new ExecutorTester<TSource>( + executor, + executor.GetType() + .GetPublicInstanceMethods("Over") + .ShouldHaveSingleItem()); } - public static TResult ShouldExecuteACreateNewMapping<TResult>( - this MethodInfo createNewMethod, - object executor) + public class ExecutorTester<TSource> { - return createNewMethod - .MakeGenericMethod(typeof(TResult)) - .Invoke(executor, Array.Empty<object>()) - .ShouldNotBeNull() - .ShouldBeOfType<TResult>(); - } + private readonly MappingExecutionContextBase<TSource> _executor; + private readonly MethodInfo _mappingMethod; - public static TTarget ShouldExecuteAMergeMapping<TTarget>( - this MethodInfo mergeMethod, - object executor, - TTarget target) - { - return mergeMethod - .Invoke(executor, new object[] { target }) - .ShouldNotBeNull() - .ShouldBeSameAs(target) - .ShouldBeOfType<TTarget>(); - } + public ExecutorTester( + MappingExecutionContextBase<TSource> executor, + MethodInfo mappingMethod) + { + _executor = executor; + _mappingMethod = mappingMethod; + } - public static TTarget ShouldExecuteAnOverwriteMapping<TTarget>( - this MethodInfo overwriteMethod, - object executor, - TTarget target) - { - return overwriteMethod - .Invoke(executor, new object[] { target }) - .ShouldNotBeNull() - .ShouldBeSameAs(target) - .ShouldBeOfType<TTarget>(); + public TResult ShouldExecuteACreateNewMapping<TResult>() + { + return _mappingMethod + .MakeGenericMethod(typeof(TResult)) + .Invoke(_executor, Array.Empty<object>()) + .ShouldNotBeNull() + .ShouldBeOfType<TResult>(); + } + + public TTarget ShouldExecuteAMergeMapping<TTarget>(TTarget target) + { + return _mappingMethod + .Invoke(_executor, new object[] { target }) + .ShouldNotBeNull() + .ShouldBeSameAs(target) + .ShouldBeOfType<TTarget>(); + } + + public TTarget ShouldExecuteAnOverwriteMapping<TTarget>(TTarget target) + { + return _mappingMethod + .Invoke(_executor, new object[] { target }) + .ShouldNotBeNull() + .ShouldBeSameAs(target) + .ShouldBeOfType<TTarget>(); + } } } } diff --git a/AgileMapper.Buildable.UnitTests/WhenBuildingCircularReferenceMappers.cs b/AgileMapper.Buildable.UnitTests/WhenBuildingCircularReferenceMappers.cs index cf118bd67..4cf49788d 100644 --- a/AgileMapper.Buildable.UnitTests/WhenBuildingCircularReferenceMappers.cs +++ b/AgileMapper.Buildable.UnitTests/WhenBuildingCircularReferenceMappers.cs @@ -46,7 +46,7 @@ public void ShouldBuildACircularReferenceMapper() var result = executor .ShouldHaveACreateNewMethod() - .ShouldExecuteACreateNewMapping<Child>(executor); + .ShouldExecuteACreateNewMapping<Child>(); result.ShouldNotBeNull().ShouldNotBeSameAs(source); diff --git a/AgileMapper.Buildable.UnitTests/WhenBuildingComplexTypeCreateNewMappers.cs b/AgileMapper.Buildable.UnitTests/WhenBuildingComplexTypeCreateNewMappers.cs index 4faf934ce..09763319d 100644 --- a/AgileMapper.Buildable.UnitTests/WhenBuildingComplexTypeCreateNewMappers.cs +++ b/AgileMapper.Buildable.UnitTests/WhenBuildingComplexTypeCreateNewMappers.cs @@ -31,7 +31,7 @@ public void ShouldBuildSingleSourceSingleTargetMapper() var result = executor .ShouldHaveACreateNewMethod() - .ShouldExecuteACreateNewMapping<PublicField<int>>(executor); + .ShouldExecuteACreateNewMapping<PublicField<int>>(); result.Value.ShouldBe(123); } @@ -62,19 +62,19 @@ public void ShouldBuildSingleSourceMultipleTargetMapper() var createNewMethod = executor.ShouldHaveACreateNewMethod(); var publicFieldResult = createNewMethod - .ShouldExecuteACreateNewMapping<PublicField<int>>(executor); + .ShouldExecuteACreateNewMapping<PublicField<int>>(); publicFieldResult.Value.ShouldBe(456); var publicPropertyResult = createNewMethod - .ShouldExecuteACreateNewMapping<PublicProperty<string>>(executor); + .ShouldExecuteACreateNewMapping<PublicProperty<string>>(); publicPropertyResult.Value.ShouldBe("456"); var configEx = Should.Throw<TargetInvocationException>(() => { createNewMethod - .ShouldExecuteACreateNewMapping<PublicField<DateTime>>(executor); + .ShouldExecuteACreateNewMapping<PublicField<DateTime>>(); }); var notSupportedMessage = configEx diff --git a/AgileMapper.Buildable.UnitTests/WhenBuildingComplexTypeMergeMappers.cs b/AgileMapper.Buildable.UnitTests/WhenBuildingComplexTypeMergeMappers.cs index 16ee82801..af21a9e24 100644 --- a/AgileMapper.Buildable.UnitTests/WhenBuildingComplexTypeMergeMappers.cs +++ b/AgileMapper.Buildable.UnitTests/WhenBuildingComplexTypeMergeMappers.cs @@ -30,7 +30,7 @@ public void ShouldBuildASingleSourceSingleTargetMapper() executor .ShouldHaveAMergeMethod() - .ShouldExecuteAMergeMapping(executor, target); + .ShouldExecuteAMergeMapping(target); target.Line1.ShouldBe("Line 1!"); target.Line2.ShouldBe("Line 2!"); diff --git a/AgileMapper.Buildable.UnitTests/WhenBuildingComplexTypeOverwriteMappers.cs b/AgileMapper.Buildable.UnitTests/WhenBuildingComplexTypeOverwriteMappers.cs index c28218bbb..763d20aa1 100644 --- a/AgileMapper.Buildable.UnitTests/WhenBuildingComplexTypeOverwriteMappers.cs +++ b/AgileMapper.Buildable.UnitTests/WhenBuildingComplexTypeOverwriteMappers.cs @@ -30,7 +30,7 @@ public void ShouldBuildASingleSourceSingleTargetMapper() executor .ShouldHaveAnOverwriteMethod() - .ShouldExecuteAnOverwriteMapping(executor, target); + .ShouldExecuteAnOverwriteMapping(target); target.Line1.ShouldBe("1.1"); target.Line2.ShouldBe("1.2"); diff --git a/AgileMapper.Buildable.UnitTests/WhenBuildingDerivedTypeMappers.cs b/AgileMapper.Buildable.UnitTests/WhenBuildingDerivedTypeMappers.cs index 7c4e4f294..2e9d6df54 100644 --- a/AgileMapper.Buildable.UnitTests/WhenBuildingDerivedTypeMappers.cs +++ b/AgileMapper.Buildable.UnitTests/WhenBuildingDerivedTypeMappers.cs @@ -32,7 +32,7 @@ public void ShouldBuildADerivedTypeCreateNewMapper() var baseTypeResult = baseTypeExecutor .ShouldHaveACreateNewMethod() - .ShouldExecuteACreateNewMapping<ProductDto>(baseTypeExecutor); + .ShouldExecuteACreateNewMapping<ProductDto>(); baseTypeResult.ProductId.ShouldBe("111"); baseTypeResult.Price.ShouldBe(19.99m); @@ -49,7 +49,7 @@ public void ShouldBuildADerivedTypeCreateNewMapper() var derivedTypeBaseTypeResult = derivedTypeExecutor .ShouldHaveACreateNewMethod() - .ShouldExecuteACreateNewMapping<ProductDto>(derivedTypeExecutor) + .ShouldExecuteACreateNewMapping<ProductDto>() .ShouldBeOfType<ProductDtoMega>(); derivedTypeBaseTypeResult.ProductId.ShouldBe("222"); @@ -58,7 +58,7 @@ public void ShouldBuildADerivedTypeCreateNewMapper() var derivedTypeDerivedTypeResult = derivedTypeExecutor .ShouldHaveACreateNewMethod() - .ShouldExecuteACreateNewMapping<ProductDtoMega>(derivedTypeExecutor); + .ShouldExecuteACreateNewMapping<ProductDtoMega>(); derivedTypeDerivedTypeResult.ProductId.ShouldBe("222"); derivedTypeDerivedTypeResult.Price.ShouldBe(119.99m); diff --git a/AgileMapper.Buildable.UnitTests/WhenBuildingEnumerableCreateNewMappers.cs b/AgileMapper.Buildable.UnitTests/WhenBuildingEnumerableCreateNewMappers.cs index d4997d694..1bfb06184 100644 --- a/AgileMapper.Buildable.UnitTests/WhenBuildingEnumerableCreateNewMappers.cs +++ b/AgileMapper.Buildable.UnitTests/WhenBuildingEnumerableCreateNewMappers.cs @@ -37,7 +37,7 @@ public void ShouldBuildASimpleTypeListToCollectionMapper() var result = executor .ShouldHaveACreateNewMethod() - .ShouldExecuteACreateNewMapping<Collection<byte?>>(executor); + .ShouldExecuteACreateNewMapping<Collection<byte?>>(); result.ShouldNotBeNull(); result.ShouldBe<byte?>(3, 2, 1, null); @@ -70,7 +70,7 @@ public void ShouldBuildASimpleTypeArrayToReadOnlyCollectionMapper() var result = executor .ShouldHaveACreateNewMethod() - .ShouldExecuteACreateNewMapping<ReadOnlyCollection<int>>(executor); + .ShouldExecuteACreateNewMapping<ReadOnlyCollection<int>>(); result.ShouldNotBeNull(); result.ShouldBe(1, 2, 3); @@ -107,7 +107,7 @@ public void ShouldBuildASimpleTypeHashSetToArrayMapper() var result = executor .ShouldHaveACreateNewMethod() - .ShouldExecuteACreateNewMapping<DateTime[]>(executor); + .ShouldExecuteACreateNewMapping<DateTime[]>(); result.ShouldNotBeNull().ShouldBe(yesterday, today, tomorrow); } @@ -144,7 +144,7 @@ public void ShouldBuildAComplexTypeListToIListMapper() var result = executor .ShouldHaveACreateNewMethod() - .ShouldExecuteACreateNewMapping<List<ProductDto>>(executor); + .ShouldExecuteACreateNewMapping<List<ProductDto>>(); result.ShouldNotBeNull(); result.ShouldNotBeSameAs(source); diff --git a/AgileMapper.Buildable.UnitTests/WhenBuildingRootEnumMappers.cs b/AgileMapper.Buildable.UnitTests/WhenBuildingRootEnumMappers.cs index 035a11fcc..14964b727 100644 --- a/AgileMapper.Buildable.UnitTests/WhenBuildingRootEnumMappers.cs +++ b/AgileMapper.Buildable.UnitTests/WhenBuildingRootEnumMappers.cs @@ -29,7 +29,7 @@ public void ShouldBuildARootEnumMapper() var enumIdResult = enumIdExecutor .ShouldHaveACreateNewMethod() - .ShouldExecuteACreateNewMapping<Title>(enumIdExecutor); + .ShouldExecuteACreateNewMapping<Title>(); enumIdResult.ShouldBe(Title.Mrs); @@ -40,7 +40,7 @@ public void ShouldBuildARootEnumMapper() var enumLabelResult = enumLabelIdExecutor .ShouldHaveACreateNewMethod() - .ShouldExecuteACreateNewMapping<Title>(enumLabelIdExecutor); + .ShouldExecuteACreateNewMapping<Title>(); enumLabelResult.ShouldBe(Title.Master); } diff --git a/AgileMapper.UnitTests/WhenMappingOverEnumerables.cs b/AgileMapper.UnitTests/WhenMappingOverEnumerables.cs index 696d32b7b..9b22e0876 100644 --- a/AgileMapper.UnitTests/WhenMappingOverEnumerables.cs +++ b/AgileMapper.UnitTests/WhenMappingOverEnumerables.cs @@ -17,29 +17,28 @@ public class WhenMappingOverEnumerables { [Fact] - public void ShouldOverwriteARootSimpleTypeArray() + public void ShouldOverwriteASimpleTypeArray() { var source = new[] { '5', '4', '3', '2' }; var target = new[] { 9, 8, 7, 1 }; var result = Mapper.Map(source).Over(target); - result.ShouldNotBeNull(); + result.ShouldNotBeNull().ShouldNotBeSameAs(target); result.ShouldBe(5, 4, 3, 2); } [Fact] - public void ShouldOverwriteARootSimpleTypeReadOnlyCollection() + public void ShouldOverwriteASimpleTypeReadOnlyCollection() { var source = new[] { '2', '3' }; var target = new ReadOnlyCollection<char>(new List<char> { '5', '4' }); var result = Mapper.Map(source).Over(target); - result.ShouldNotBeNull(); - result.ShouldBe('2', '3'); + result.ShouldNotBeNull().ShouldBe('2', '3'); } [Fact] - public void ShouldOverwriteARootSimpleTypeList() + public void ShouldOverwriteASimpleTypeList() { var source = new List<string> { "I", "Will" }; var target = new List<string> { "You", "Might" }; @@ -50,7 +49,7 @@ public void ShouldOverwriteARootSimpleTypeList() } [Fact] - public void ShouldOverwriteARootSimpleTypeEnumerable() + public void ShouldOverwriteASimpleTypeEnumerable() { var source = new List<long> { 234, 567 }; IEnumerable<long> target = new List<long> { 654, 321 }; @@ -61,7 +60,7 @@ public void ShouldOverwriteARootSimpleTypeEnumerable() } [Fact] - public void ShouldOverwriteARootComplexTypeList() + public void ShouldOverwriteAComplexTypeList() { var source = new[] { @@ -82,7 +81,7 @@ public void ShouldOverwriteARootComplexTypeList() } [Fact] - public void ShouldOverwriteARootComplexTypeCollectionElementByRuntimeType() + public void ShouldOverwriteAComplexTypeCollectionElementByRuntimeType() { var id = Guid.NewGuid(); From 854e25bb395ead448f817268359e0715824b1e99 Mon Sep 17 00:00:00 2001 From: Steve Wilkes <steve@agileobjects.co.uk> Date: Fri, 14 May 2021 21:12:56 +0100 Subject: [PATCH 26/65] Test coverage for built overwrite mappers --- .../FluentAssertionExtensions.cs | 2 - .../WhenBuildingEnumerableOverwriteMappers.cs | 117 ++++++++++++++++++ .../WhenViewingMappingPlans.cs | 10 +- .../EnumerablePopulationBuilder.cs | 19 ++- .../EnumerablePopulationContext.cs | 31 +++-- .../Enumerables/EnumerableTypeHelper.cs | 4 +- 6 files changed, 156 insertions(+), 27 deletions(-) create mode 100644 AgileMapper.Buildable.UnitTests/WhenBuildingEnumerableOverwriteMappers.cs diff --git a/AgileMapper.Buildable.UnitTests/FluentAssertionExtensions.cs b/AgileMapper.Buildable.UnitTests/FluentAssertionExtensions.cs index 7157f98c6..86cd544ca 100644 --- a/AgileMapper.Buildable.UnitTests/FluentAssertionExtensions.cs +++ b/AgileMapper.Buildable.UnitTests/FluentAssertionExtensions.cs @@ -96,7 +96,6 @@ public TTarget ShouldExecuteAMergeMapping<TTarget>(TTarget target) return _mappingMethod .Invoke(_executor, new object[] { target }) .ShouldNotBeNull() - .ShouldBeSameAs(target) .ShouldBeOfType<TTarget>(); } @@ -105,7 +104,6 @@ public TTarget ShouldExecuteAnOverwriteMapping<TTarget>(TTarget target) return _mappingMethod .Invoke(_executor, new object[] { target }) .ShouldNotBeNull() - .ShouldBeSameAs(target) .ShouldBeOfType<TTarget>(); } } diff --git a/AgileMapper.Buildable.UnitTests/WhenBuildingEnumerableOverwriteMappers.cs b/AgileMapper.Buildable.UnitTests/WhenBuildingEnumerableOverwriteMappers.cs new file mode 100644 index 000000000..402eb6f49 --- /dev/null +++ b/AgileMapper.Buildable.UnitTests/WhenBuildingEnumerableOverwriteMappers.cs @@ -0,0 +1,117 @@ +namespace AgileObjects.AgileMapper.Buildable.UnitTests +{ + using System.Collections.Generic; + using System.Collections.ObjectModel; + using System.Linq; + using AgileMapper.UnitTests.Common; + using AgileMapper.UnitTests.Common.TestClasses; + using Xunit; + + public class WhenBuildingEnumerableOverwriteMappers + { + [Fact] + public void ShouldBuildASimpleTypeArrayToArrayMapper() + { + using (var mapper = Mapper.CreateNew()) + { + mapper.GetPlanFor<char[]>().Over<int[]>(); + + var sourceCodeExpressions = mapper.GetPlanSourceCodeInCache(); + + var staticMapperClass = sourceCodeExpressions + .ShouldCompileAStaticMapperClass(); + + var staticMapMethod = staticMapperClass + .GetMapMethods() + .ShouldHaveSingleItem(); + + var source = new[] { '5', '5', '5' }; + var target = new[] { 3, 3, 3, 3 }; + + var executor = staticMapMethod + .ShouldCreateMappingExecutor(source); + + var result = executor + .ShouldHaveAnOverwriteMethod() + .ShouldExecuteAnOverwriteMapping(target); + + result.ShouldNotBeNull().ShouldNotBeSameAs(target); + result.ShouldBe(5, 5, 5); + } + } + + [Fact] + public void ShouldBuildASimpleTypeCollectionToListMapper() + { + using (var mapper = Mapper.CreateNew()) + { + mapper.GetPlanFor<Collection<string>>().Over<List<string>>(); + + var sourceCodeExpressions = mapper.GetPlanSourceCodeInCache(); + + var staticMapperClass = sourceCodeExpressions + .ShouldCompileAStaticMapperClass(); + + var staticMapMethod = staticMapperClass + .GetMapMethods() + .ShouldHaveSingleItem(); + + var source = new Collection<string> { "I", "Will" }; + var target = new List<string> { "You", "Might" }; + + var executor = staticMapMethod + .ShouldCreateMappingExecutor(source); + + var result = executor + .ShouldHaveAnOverwriteMethod() + .ShouldExecuteAnOverwriteMapping(target); + + result.ShouldNotBeNull().ShouldBeSameAs(target); + result.SequenceEqual(source).ShouldBeTrue(); + } + } + + [Fact] + public void ShouldBuildAComplexTypeArrayToReadOnlyCollectionMapper() + { + using (var mapper = Mapper.CreateNew()) + { + mapper.GetPlanFor<ProductDto[]>().Over<ReadOnlyCollection<Product>>(); + + var sourceCodeExpressions = mapper.GetPlanSourceCodeInCache(); + + var staticMapperClass = sourceCodeExpressions + .ShouldCompileAStaticMapperClass(); + + var staticMapMethod = staticMapperClass + .GetMapMethods() + .ShouldHaveSingleItem(); + + var source = new[] + { + new ProductDto { ProductId = "1", Price = 1.99m }, + new ProductDto { ProductId = "2", Price = 2.99m }, + }; + + var target = new ReadOnlyCollection<Product>(new List<Product> + { + new Product { ProductId = "2", Price = 4.99 } + }); + + var executor = staticMapMethod + .ShouldCreateMappingExecutor(source); + + var result = executor + .ShouldHaveAnOverwriteMethod() + .ShouldExecuteAnOverwriteMapping(target); + + result.ShouldNotBeNull(); + result.Count.ShouldBe(2); + result.First().ProductId.ShouldBe("2"); + result.First().Price.ShouldBe(2.99); + result.Second().ProductId.ShouldBe("1"); + result.Second().Price.ShouldBe(1.99); + } + } + } +} diff --git a/AgileMapper.UnitTests/WhenViewingMappingPlans.cs b/AgileMapper.UnitTests/WhenViewingMappingPlans.cs index 219eed3e9..9749a6d7e 100644 --- a/AgileMapper.UnitTests/WhenViewingMappingPlans.cs +++ b/AgileMapper.UnitTests/WhenViewingMappingPlans.cs @@ -158,7 +158,7 @@ public void ShouldShowARootComplexTypeEnumerableMapping() .GetPlanFor<IEnumerable<Person>>() .OnTo<IEnumerable<PersonViewModel>>(); - plan.ShouldContain("collectionData.Intersection.ForEach((person, personViewModel, i) =>"); + plan.ShouldContain("collectionData.Intersection.ForEach((existingPerson, existingPersonViewModel, idx) =>"); plan.ShouldContain("persons = collectionData.NewSourceItems"); } @@ -170,12 +170,12 @@ public void ShouldShowAComplexTypeEnumerableMemberMapping() .Over<IEnumerable<Person>>(); plan.ShouldContain("personViewModels = collectionData.NewSourceItems"); - plan.ShouldContain("collectionData.Intersection.ForEach((personViewModel, person, i) =>"); - plan.ShouldContain("collectionData.AbsentTargetItems.ForEach(persons.Remove)"); + plan.ShouldContain("collectionData.Intersection.ForEach((existingPersonViewModel, existingPerson, idx) =>"); + plan.ShouldContain("collectionData.AbsentTargetItems.ForEach(p => persons.Remove(p)"); plan.ShouldContain("IList<PersonViewModel> -> IEnumerable<Person>"); - plan.ShouldNotContain("PersonViewModel -> Person"); // <- because the mapping is inlined - plan.ShouldNotContain("PersonViewModel -> Address"); // <- because the mapping is inlined + plan.ShouldNotContain("PersonViewModel -> Person"); + plan.ShouldNotContain("PersonViewModel -> Address"); } [Fact] diff --git a/AgileMapper/ObjectPopulation/Enumerables/EnumerablePopulationBuilder.cs b/AgileMapper/ObjectPopulation/Enumerables/EnumerablePopulationBuilder.cs index 1d0d60a80..f328d5c88 100644 --- a/AgileMapper/ObjectPopulation/Enumerables/EnumerablePopulationBuilder.cs +++ b/AgileMapper/ObjectPopulation/Enumerables/EnumerablePopulationBuilder.cs @@ -263,13 +263,7 @@ private static LambdaExpression GetTargetElementIdLambda(ParameterExpression tar public ParameterExpression TargetVariable { get => _targetVariable; - set - { - if (_targetVariable == null) - { - _targetVariable = value; - } - } + set => _targetVariable ??= value; } public void AssignSourceVariableToSourceObject() @@ -711,8 +705,11 @@ public void CreateCollectionData() public void MapIntersection(IObjectMappingData enumerableMappingData) { - var sourceElementParameter = Context.GetSourceParameterFor(Context.SourceElementType); - var targetElementParameter = Context.GetTargetParameterFor(Context.TargetElementType); + var sourceElementParameter = Context.GetSourceParameterFor(Context.SourceElementType, prefix: "existing"); + var targetElementParameter = Context.GetTargetParameterFor(Context.TargetElementType, prefix: "existing"); + + var defaultLoopCounter = _counterVariable; + _counterVariable = Parameters.Create<int>("idx"); var forEachActionType = Expression.GetActionType(Context.SourceElementType, Context.TargetElementType, typeof(int)); var forEachAction = GetElementMapping(sourceElementParameter, targetElementParameter, enumerableMappingData); @@ -722,7 +719,9 @@ public void MapIntersection(IObjectMappingData enumerableMappingData) forEachAction, sourceElementParameter, targetElementParameter, - Counter); + _counterVariable); + + _counterVariable = defaultLoopCounter; var forEachCall = Expression.Call( _forEachTupleMethod.MakeGenericMethod(Context.ElementTypes), diff --git a/AgileMapper/ObjectPopulation/Enumerables/EnumerablePopulationContext.cs b/AgileMapper/ObjectPopulation/Enumerables/EnumerablePopulationContext.cs index c7e72bca8..2d1ee179e 100644 --- a/AgileMapper/ObjectPopulation/Enumerables/EnumerablePopulationContext.cs +++ b/AgileMapper/ObjectPopulation/Enumerables/EnumerablePopulationContext.cs @@ -40,19 +40,34 @@ public EnumerablePopulationContext(IQualifiedMemberContext context) public bool TargetElementsAreSimple { get; } - public ParameterExpression GetSourceParameterFor(Type type) => GetParameterFor(type, "source"); + public ParameterExpression GetSourceParameterFor(Type type, string prefix = null) + => GetParameterFor(type, prefix, "source"); - public ParameterExpression GetTargetParameterFor(Type type) => GetParameterFor(type, "target"); + public ParameterExpression GetTargetParameterFor(Type type, string prefix = null) + => GetParameterFor(type, prefix, "target"); - private ParameterExpression GetParameterFor(Type type, string sameTypesPrefix) + private ParameterExpression GetParameterFor( + Type type, + string prefix, + string sameTypesPrefix) { - var parameterName = ElementTypesAreTheSame - ? sameTypesPrefix + type.GetVariableNameInPascalCase() - : type.GetVariableNameInCamelCase(); + string parameterName; - var parameter = Expression.Parameter(type, parameterName); + if (ElementTypesAreTheSame) + { + parameterName = + prefix + + sameTypesPrefix + + type.GetVariableNameInPascalCase(); + } + else + { + parameterName = prefix != null + ? prefix + type.GetVariableNameInPascalCase() + : type.GetVariableNameInCamelCase(); + } - return parameter; + return Parameters.Create(type, parameterName); } } } \ No newline at end of file diff --git a/AgileMapper/ObjectPopulation/Enumerables/EnumerableTypeHelper.cs b/AgileMapper/ObjectPopulation/Enumerables/EnumerableTypeHelper.cs index 43fa84730..61f68ab54 100644 --- a/AgileMapper/ObjectPopulation/Enumerables/EnumerableTypeHelper.cs +++ b/AgileMapper/ObjectPopulation/Enumerables/EnumerableTypeHelper.cs @@ -125,7 +125,7 @@ private bool IsReadOnlyCollectionInterface() private Type SetInterfaceType => GetEnumerableType(ref _setInterfaceType, typeof(ISet<>)); #endif private Type GetEnumerableType(ref Type typeField, Type openGenericEnumerableType) - => typeField ?? (typeField = openGenericEnumerableType.MakeGenericType(ElementType)); + => typeField ??= openGenericEnumerableType.MakeGenericType(ElementType); public Type WrapperType => typeof(ReadOnlyCollectionWrapper<>).MakeGenericType(ElementType); @@ -198,7 +198,7 @@ private static bool ValueIsNotEnumerableInterface(Expression instance) => instance.Type != typeof(IEnumerable<>).MakeGenericType(instance.Type.GetEnumerableElementType()); public Expression GetCountFor(Expression instance, Type countType = null) - => instance.GetCount(countType, exp => CollectionInterfaceType); + => instance.GetCount(countType, _ => CollectionInterfaceType); public Expression GetNonZeroCountCheck(Expression enumerableAccess) { From a1b45c5d4902705309b582b11b3af877b55874cc Mon Sep 17 00:00:00 2001 From: Steve Wilkes <steve@agileobjects.co.uk> Date: Sun, 16 May 2021 14:11:31 +0100 Subject: [PATCH 27/65] Using switch statements for non-projection numeric-, char- and string-to-enum mapping --- .../WhenConfiguringStringFormattingInline.cs | 2 +- .../WhenConfiguringStringFormatting.cs | 2 +- .../CustomDataSourceTargetMemberSpecifier.cs | 2 +- .../Api/Configuration/InstanceConfigurator.cs | 3 +- .../Dictionaries/ElementKeyPartFactory.cs | 3 +- .../Configuration/MappingConfigInfo.cs | 2 +- .../Internal/EnumerableExtensions.cs | 2 +- AgileMapper/MapperContextExtensions.cs | 3 - AgileMapper/TypeConversion/ConverterSet.cs | 20 +- .../FallbackNonSimpleTypeValueConverter.cs | 8 +- AgileMapper/TypeConversion/IValueConverter.cs | 7 +- .../TypeConversion/OperatorConverter.cs | 2 +- AgileMapper/TypeConversion/ToBoolConverter.cs | 5 +- .../TypeConversion/ToCharacterConverter.cs | 5 +- AgileMapper/TypeConversion/ToEnumConverter.cs | 213 +++++++++++++----- .../ToFormattedStringConverter.cs | 8 +- AgileMapper/TypeConversion/ToGuidConverter.cs | 2 +- .../TypeConversion/ToNumericConverter.cs | 9 +- .../TypeConversion/ToStringConverter.cs | 2 +- .../TypeConversion/TryParseConverter.cs | 14 +- .../TypeConversionExtensions.cs | 10 +- 21 files changed, 235 insertions(+), 89 deletions(-) diff --git a/AgileMapper.UnitTests/Configuration/Inline/WhenConfiguringStringFormattingInline.cs b/AgileMapper.UnitTests/Configuration/Inline/WhenConfiguringStringFormattingInline.cs index fbf8e879b..6980390eb 100644 --- a/AgileMapper.UnitTests/Configuration/Inline/WhenConfiguringStringFormattingInline.cs +++ b/AgileMapper.UnitTests/Configuration/Inline/WhenConfiguringStringFormattingInline.cs @@ -82,7 +82,7 @@ public void ShouldErrorIfUnformattableTypeSpecifiedInline() } }); - noFormatEx.Message.ShouldContain("No ToString method"); + noFormatEx.Message.ShouldContain("No ToString() method"); } } } diff --git a/AgileMapper.UnitTests/Configuration/WhenConfiguringStringFormatting.cs b/AgileMapper.UnitTests/Configuration/WhenConfiguringStringFormatting.cs index 3f9f943ed..c0cbc2c09 100644 --- a/AgileMapper.UnitTests/Configuration/WhenConfiguringStringFormatting.cs +++ b/AgileMapper.UnitTests/Configuration/WhenConfiguringStringFormatting.cs @@ -86,7 +86,7 @@ public void ShouldErrorIfUnformattableTypeSpecified() } }); - noFormatEx.Message.ShouldContain("No ToString method"); + noFormatEx.Message.ShouldContain("No ToString() method"); } } } diff --git a/AgileMapper/Api/Configuration/CustomDataSourceTargetMemberSpecifier.cs b/AgileMapper/Api/Configuration/CustomDataSourceTargetMemberSpecifier.cs index 91e854af4..08179c257 100644 --- a/AgileMapper/Api/Configuration/CustomDataSourceTargetMemberSpecifier.cs +++ b/AgileMapper/Api/Configuration/CustomDataSourceTargetMemberSpecifier.cs @@ -251,7 +251,7 @@ private ConfiguredLambdaInfo GetValueLambdaInfo(Type targetValueType) return _customValueLambdaInfo = ConfiguredLambdaInfo.For(customValueLambda, _configInfo); } - var convertedConstantValue = MapperContext + var convertedConstantValue = _configInfo .GetValueConversion(customValueLambda.Body, targetValueType); var funcType = Expr.GetFuncType(targetValueType); diff --git a/AgileMapper/Api/Configuration/InstanceConfigurator.cs b/AgileMapper/Api/Configuration/InstanceConfigurator.cs index 519401416..6b3f26d49 100644 --- a/AgileMapper/Api/Configuration/InstanceConfigurator.cs +++ b/AgileMapper/Api/Configuration/InstanceConfigurator.cs @@ -9,6 +9,7 @@ namespace AgileObjects.AgileMapper.Api.Configuration using System.Linq.Expressions; using ReadableExpressions; using ReadableExpressions.Extensions; + using TypeConversion; #if NET35 using static Microsoft.Scripting.Ast.Expression; using Expression = Microsoft.Scripting.Ast.Expression; @@ -140,7 +141,7 @@ private Expression GetStringIdPart(Expression idPart) { if (idPart.Type != typeof(string)) { - idPart = _configInfo.MapperContext.GetValueConversion(idPart, typeof(string)); + idPart = _configInfo.GetValueConversion(idPart, typeof(string)); } var idPartNestedAccessesChecks = _configInfo diff --git a/AgileMapper/Configuration/Dictionaries/ElementKeyPartFactory.cs b/AgileMapper/Configuration/Dictionaries/ElementKeyPartFactory.cs index 16d2df72c..eb2c9c211 100644 --- a/AgileMapper/Configuration/Dictionaries/ElementKeyPartFactory.cs +++ b/AgileMapper/Configuration/Dictionaries/ElementKeyPartFactory.cs @@ -15,6 +15,7 @@ using Members; using NetStandardPolyfills; using ReadableExpressions.Extensions; + using TypeConversion; internal class ElementKeyPartFactory : DictionaryKeyPartFactoryBase { @@ -163,7 +164,7 @@ public IEnumerable<Expression> GetElementKeyParts(Expression index) yield return _prefix; } - yield return ConfigInfo.MapperContext.GetValueConversion(index, typeof(string)); + yield return ConfigInfo.GetValueConversion(index, typeof(string)); if (_suffix != null) { diff --git a/AgileMapper/Configuration/MappingConfigInfo.cs b/AgileMapper/Configuration/MappingConfigInfo.cs index 68e926281..28621a75b 100644 --- a/AgileMapper/Configuration/MappingConfigInfo.cs +++ b/AgileMapper/Configuration/MappingConfigInfo.cs @@ -24,7 +24,7 @@ internal delegate bool TargetTypeComparer(ITypePair typePair, ITypePair otherTypePair); - internal class MappingConfigInfo : ITypePair + internal class MappingConfigInfo : IMapperContextOwner, IRuleSetOwner, ITypePair { public static readonly MappingConfigInfo AllRuleSetsSourceTypesAndTargetTypes = AllRuleSetsAndSourceTypes(null).ForAllTargetTypes(); diff --git a/AgileMapper/Extensions/Internal/EnumerableExtensions.cs b/AgileMapper/Extensions/Internal/EnumerableExtensions.cs index 88a4a030a..5f55aa281 100644 --- a/AgileMapper/Extensions/Internal/EnumerableExtensions.cs +++ b/AgileMapper/Extensions/Internal/EnumerableExtensions.cs @@ -59,7 +59,7 @@ public static T First<TArg, T>(this IList<T> items, TArg argument, Func<TArg, T, [DebuggerStepThrough] public static T FirstOrDefault<T>(this IList<T> items, Func<T, bool> predicate) - => FirstOrDefault(items, predicate, (p, item) => predicate.Invoke(item)); + => FirstOrDefault(items, predicate, (_, item) => predicate.Invoke(item)); [DebuggerStepThrough] public static T FirstOrDefault<TArg, T>(this IList<T> items, TArg argument, Func<TArg, T, bool> predicate) diff --git a/AgileMapper/MapperContextExtensions.cs b/AgileMapper/MapperContextExtensions.cs index 433facbcb..ad82955b4 100644 --- a/AgileMapper/MapperContextExtensions.cs +++ b/AgileMapper/MapperContextExtensions.cs @@ -48,8 +48,5 @@ public static Expression GetIdentifierOrNull( public static Member GetIdentifierOrNull(this MapperContext context, Type type) => context.Naming.GetIdentifierOrNull(type); - - public static Expression GetValueConversion(this MapperContext context, Expression value, Type targetType) - => context.ValueConverters.GetConversion(value, targetType); } } diff --git a/AgileMapper/TypeConversion/ConverterSet.cs b/AgileMapper/TypeConversion/ConverterSet.cs index 6cc45f31b..6762e0ba6 100644 --- a/AgileMapper/TypeConversion/ConverterSet.cs +++ b/AgileMapper/TypeConversion/ConverterSet.cs @@ -79,7 +79,10 @@ private IValueConverter GetConverterOrNull(Type sourceType, Type targetType) return _converters.FirstOrDefault(c => c.CanConvert(sourceType, targetType)); } - public Expression GetConversion(Expression sourceValue, Type targetType) + public Expression GetConversion( + Expression sourceValue, + Type targetType, + MappingRuleSet ruleSet) { if (sourceValue.Type == targetType) { @@ -94,7 +97,20 @@ public Expression GetConversion(Expression sourceValue, Type targetType) } var converter = GetConverterOrNull(sourceValue.Type, targetType); - var conversion = converter?.GetConversion(sourceValue, targetType); + + if (converter == null) + { + return null; + } + + var useSingleStatement = + ruleSet == MappingRuleSet.All || + ruleSet.Settings.UseSingleRootMappingExpression; + + var conversion = converter.GetConversion( + sourceValue, + targetType, + useSingleStatement); return conversion; } diff --git a/AgileMapper/TypeConversion/FallbackNonSimpleTypeValueConverter.cs b/AgileMapper/TypeConversion/FallbackNonSimpleTypeValueConverter.cs index b6b0a74f6..d1aa74783 100644 --- a/AgileMapper/TypeConversion/FallbackNonSimpleTypeValueConverter.cs +++ b/AgileMapper/TypeConversion/FallbackNonSimpleTypeValueConverter.cs @@ -32,6 +32,12 @@ public bool CanConvert(Type nonNullableSourceType, Type nonNullableTargetType) return true; } - public Expression GetConversion(Expression sourceValue, Type targetType) => sourceValue; + public Expression GetConversion( + Expression sourceValue, + Type targetType, + bool useSingleStatement) + { + return sourceValue; + } } } \ No newline at end of file diff --git a/AgileMapper/TypeConversion/IValueConverter.cs b/AgileMapper/TypeConversion/IValueConverter.cs index c8d79d45a..fa2090acd 100644 --- a/AgileMapper/TypeConversion/IValueConverter.cs +++ b/AgileMapper/TypeConversion/IValueConverter.cs @@ -30,10 +30,15 @@ public interface IValueConverter /// </summary> /// <param name="sourceValue">The source value to convert.</param> /// <param name="targetType">The target type to which to convert to <paramref name="sourceValue"/>.</param> + /// <param name="useSingleStatement"> + /// Whether the conversion Expression to create should convert the values using a single + /// statement, or if the context supports multiple statements, <i>e.g</i>. query projection + /// vs in-memory mapping. + /// </param> /// <returns> /// An Expression converting the given <paramref name="sourceValue"/> to the given /// <paramref name="targetType"/>. /// </returns> - Expression GetConversion(Expression sourceValue, Type targetType); + Expression GetConversion(Expression sourceValue, Type targetType, bool useSingleStatement); } } \ No newline at end of file diff --git a/AgileMapper/TypeConversion/OperatorConverter.cs b/AgileMapper/TypeConversion/OperatorConverter.cs index f6b656776..0f838444d 100644 --- a/AgileMapper/TypeConversion/OperatorConverter.cs +++ b/AgileMapper/TypeConversion/OperatorConverter.cs @@ -44,7 +44,7 @@ public static MethodInfo GetOperatorOrNull( private static MethodInfo GetOperatorOrNull(Type subjectType, Func<MethodInfo, bool> matcher) => subjectType.GetOperators().FirstOrDefault(matcher.Invoke); - public Expression GetConversion(Expression sourceValue, Type targetType) + public Expression GetConversion(Expression sourceValue, Type targetType, bool useSingleStatement) { var nonNullableSourceType = sourceValue.Type.GetNonNullableType(); var nonNullableTargetType = targetType.GetNonNullableType(); diff --git a/AgileMapper/TypeConversion/ToBoolConverter.cs b/AgileMapper/TypeConversion/ToBoolConverter.cs index 61a66f2cf..4a002a7b8 100644 --- a/AgileMapper/TypeConversion/ToBoolConverter.cs +++ b/AgileMapper/TypeConversion/ToBoolConverter.cs @@ -22,7 +22,10 @@ public bool CanConvert(Type nonNullableSourceType, Type nonNullableTargetType) ToStringConverter.HasNativeStringRepresentation(nonNullableSourceType)); } - public Expression GetConversion(Expression sourceValue, Type targetType) + public Expression GetConversion( + Expression sourceValue, + Type targetType, + bool useSingleStatement) { if (sourceValue.Type == typeof(bool?)) { diff --git a/AgileMapper/TypeConversion/ToCharacterConverter.cs b/AgileMapper/TypeConversion/ToCharacterConverter.cs index 4affd8823..2b8b563b4 100644 --- a/AgileMapper/TypeConversion/ToCharacterConverter.cs +++ b/AgileMapper/TypeConversion/ToCharacterConverter.cs @@ -21,7 +21,10 @@ public bool CanConvert(Type nonNullableSourceType, Type nonNullableTargetType) (Array.IndexOf(Constants.NumericTypes, nonNullableSourceType) != -1)); } - public Expression GetConversion(Expression sourceValue, Type targetType) + public Expression GetConversion( + Expression sourceValue, + Type targetType, + bool useSingleStatement) { if (sourceValue.Type == typeof(char?)) { diff --git a/AgileMapper/TypeConversion/ToEnumConverter.cs b/AgileMapper/TypeConversion/ToEnumConverter.cs index 556d0b117..1fe6b1e36 100644 --- a/AgileMapper/TypeConversion/ToEnumConverter.cs +++ b/AgileMapper/TypeConversion/ToEnumConverter.cs @@ -14,6 +14,12 @@ using Extensions.Internal; using NetStandardPolyfills; using ReadableExpressions.Extensions; + using static System.Convert; +#if NET35 + using static Microsoft.Scripting.Ast.Expression; +#else + using static System.Linq.Expressions.Expression; +#endif internal class ToEnumConverter : IValueConverter { @@ -35,7 +41,10 @@ public bool CanConvert(Type nonNullableSourceType, Type nonNullableTargetType) ToStringConverter.HasNativeStringRepresentation(nonNullableSourceType); } - public Expression GetConversion(Expression sourceValue, Type targetEnumType) + public Expression GetConversion( + Expression sourceValue, + Type targetEnumType, + bool useSingleStatement) { var fallbackValue = targetEnumType.ToDefaultExpression(); var nonNullableSourceType = sourceValue.Type.GetNonNullableType(); @@ -65,13 +74,15 @@ public Expression GetConversion(Expression sourceValue, Type targetEnumType) sourceValue, fallbackValue, nonNullableSourceType, - nonNullableTargetEnumType); + nonNullableTargetEnumType, + useSingleStatement); } return GetStringValueConversion( sourceValue, fallbackValue, - nonNullableTargetEnumType); + nonNullableTargetEnumType, + useSingleStatement); } private static Expression GetFlagsEnumConversion( @@ -83,7 +94,7 @@ private static Expression GetFlagsEnumConversion( var enumTypeName = GetVariableNameFor(nonNullableTargetEnumType); var underlyingEnumType = Enum.GetUnderlyingType(nonNullableTargetEnumType); - var enumValueVariable = Expression.Variable(underlyingEnumType, enumTypeName + "Value"); + var enumValueVariable = Variable(underlyingEnumType, enumTypeName + "Value"); var underlyingTypeDefault = underlyingEnumType.ToDefaultExpression(); var assignEnumValue = enumValueVariable.AssignTo(underlyingTypeDefault); @@ -105,25 +116,25 @@ private static Expression GetFlagsEnumConversion( var sourceValuesVariable = GetEnumValuesVariable(enumTypeName, typeof(string)); - var splitSourceValueCall = Expression.Call( + var splitSourceValueCall = Call( sourceValue, typeof(string).GetPublicInstanceMethod("Split", typeof(char[])), - Expression.NewArrayInit(typeof(char), ','.ToConstantExpression())); + NewArrayInit(typeof(char), ','.ToConstantExpression())); var assignSourceValues = GetValuesEnumeratorAssignment(sourceValuesVariable, splitSourceValueCall); var ifNotMoveNextBreak = GetLoopExitCheck(sourceValuesVariable, out var loopBreakTarget); - var localSourceValueVariable = Expression.Variable(typeof(string), enumTypeName); - var enumeratorCurrent = Expression.Property(sourceValuesVariable, "Current"); + var localSourceValueVariable = Variable(typeof(string), enumTypeName); + var enumeratorCurrent = Property(sourceValuesVariable, "Current"); var stringTrimMethod = typeof(string).GetPublicInstanceMethod("Trim", parameterCount: 0); - var currentTrimmed = Expression.Call(enumeratorCurrent, stringTrimMethod); + var currentTrimmed = Call(enumeratorCurrent, stringTrimMethod); var assignLocalVariable = localSourceValueVariable.AssignTo(currentTrimmed); var isNumericTest = GetIsNumericTest(localSourceValueVariable); var sourceNumericValueVariableName = enumTypeName + underlyingEnumType.Name + "Value"; - var sourceNumericValueVariable = Expression.Variable(underlyingEnumType, sourceNumericValueVariableName); + var sourceNumericValueVariable = Variable(underlyingEnumType, sourceNumericValueVariableName); var parsedString = GetStringParseCall(localSourceValueVariable, underlyingEnumType); var assignNumericVariable = sourceNumericValueVariable.AssignTo(parsedString); @@ -135,7 +146,7 @@ private static Expression GetFlagsEnumConversion( out var enumValuesVariable, out var assignEnumValues); - var numericValuePopulationBlock = Expression.Block( + var numericValuePopulationBlock = Block( new[] { enumValuesVariable }, assignNumericVariable, assignEnumValues, @@ -146,24 +157,24 @@ private static Expression GetFlagsEnumConversion( underlyingTypeDefault, nonNullableTargetEnumType); - var assignParsedEnumValue = Expression.OrAssign(enumValueVariable, stringValueConversion); + var assignParsedEnumValue = OrAssign(enumValueVariable, stringValueConversion); - var assignValidValuesIfPossible = Expression.IfThenElse( + var assignValidValuesIfPossible = IfThenElse( isNumericTest, numericValuePopulationBlock, assignParsedEnumValue); - var loopBody = Expression.Block( + var loopBody = Block( new[] { localSourceValueVariable, sourceNumericValueVariable }, ifNotMoveNextBreak, assignLocalVariable, assignValidValuesIfPossible); - var populationBlock = Expression.Block( + var populationBlock = Block( new[] { sourceValuesVariable, enumValueVariable }, assignEnumValue, assignSourceValues, - Expression.Loop(loopBody, loopBreakTarget), + Loop(loopBody, loopBreakTarget), enumValueVariable.GetConversionTo(fallbackValue.Type)); return populationBlock; @@ -182,11 +193,11 @@ private static Expression GetNumericToFlagsEnumConversion( { var underlyingEnumType = enumValueVariable.Type; - var sourceValueVariable = Expression.Variable(underlyingEnumType, enumTypeName + "Source"); + var sourceValueVariable = Variable(underlyingEnumType, enumTypeName + "Source"); if (sourceValue.Type != underlyingEnumType) { - sourceValue = Expression.Convert(sourceValue, underlyingEnumType); + sourceValue = Convert(sourceValue, underlyingEnumType); } var assignSourceVariable = sourceValueVariable.AssignTo(sourceValue); @@ -199,7 +210,7 @@ private static Expression GetNumericToFlagsEnumConversion( out var enumValuesVariable, out var assignEnumValues); - var populationBlock = Expression.Block( + var populationBlock = Block( new[] { sourceValueVariable, enumValueVariable, enumValuesVariable }, assignSourceVariable, assignEnumValue, @@ -227,28 +238,28 @@ private static Expression GetNumericToFlagsEnumPopulationLoop( var ifNotMoveNextBreak = GetLoopExitCheck(enumValuesVariable, out var loopBreakTarget); - var localEnumValueVariable = Expression.Variable(underlyingEnumType, enumTypeName); - var enumeratorCurrent = Expression.Property(enumValuesVariable, "Current"); + var localEnumValueVariable = Variable(underlyingEnumType, enumTypeName); + var enumeratorCurrent = Property(enumValuesVariable, "Current"); var assignLocalVariable = localEnumValueVariable.AssignTo(enumeratorCurrent); - var localVariableAndSourceValue = Expression.And(localEnumValueVariable, sourceValueVariable); - var andResultEqualsEnumValue = Expression.Equal(localVariableAndSourceValue, localEnumValueVariable); + var localVariableAndSourceValue = And(localEnumValueVariable, sourceValueVariable); + var andResultEqualsEnumValue = Equal(localVariableAndSourceValue, localEnumValueVariable); - var ifAndResultMatchesAssign = Expression.IfThen( + var ifAndResultMatchesAssign = IfThen( andResultEqualsEnumValue, - Expression.OrAssign(enumValueVariable, localEnumValueVariable)); + OrAssign(enumValueVariable, localEnumValueVariable)); - var loopBody = Expression.Block( + var loopBody = Block( new[] { localEnumValueVariable }, ifNotMoveNextBreak, assignLocalVariable, ifAndResultMatchesAssign); - return Expression.Loop(loopBody, loopBreakTarget); + return Loop(loopBody, loopBreakTarget); } private static ParameterExpression GetEnumValuesVariable(string enumTypeName, Type elementType) - => Expression.Variable(typeof(IEnumerator<>).MakeGenericType(elementType), enumTypeName + "Values"); + => Variable(typeof(IEnumerator<>).MakeGenericType(elementType), enumTypeName + "Values"); private static Expression GetEnumValuesConstant(Type enumType, Type underlyingType) { @@ -280,7 +291,7 @@ private static Expression GetValuesEnumeratorAssignment( ? enumeratedValues.Type : typeof(IEnumerable<>).MakeGenericType(enumeratedValues.Type.GetEnumerableElementType()); - var getValuesEnumeratorCall = Expression.Call( + var getValuesEnumeratorCall = Call( enumeratedValues, enumerableType.GetPublicInstanceMethod("GetEnumerator")); @@ -291,18 +302,18 @@ private static Expression GetLoopExitCheck( Expression valuesEnumerator, out LabelTarget loopBreakTarget) { - var enumeratorMoveNext = Expression.Call( + var enumeratorMoveNext = Call( valuesEnumerator, typeof(IEnumerator).GetPublicInstanceMethod("MoveNext")); - loopBreakTarget = Expression.Label(); + loopBreakTarget = Label(); - return Expression.IfThen(Expression.Not(enumeratorMoveNext), Expression.Break(loopBreakTarget)); + return IfThen(Not(enumeratorMoveNext), Break(loopBreakTarget)); } private static Expression GetIsNumericTest(Expression stringValue) { - return Expression.Call( + return Call( typeof(char).GetPublicStaticMethod("IsDigit", typeof(string), typeof(int)), stringValue, ToNumericConverter<int>.Zero); @@ -310,7 +321,7 @@ private static Expression GetIsNumericTest(Expression stringValue) public static Expression GetStringParseCall(Expression sourceValue, Type underlyingEnumType) { - return Expression.Call( + return Call( underlyingEnumType.GetPublicStaticMethod("Parse", typeof(string)), sourceValue); } @@ -336,7 +347,7 @@ private static Expression GetStringToEnumConversion( return targetEnumValues.Reverse().Aggregate( fallbackValue, - (valueSoFar, enumValue) => Expression.Condition( + (valueSoFar, enumValue) => Condition( sourceValue.GetCaseInsensitiveEquals(enumValue.Member.Name.ToConstantExpression()), enumValue.GetConversionTo(fallbackValue.Type), valueSoFar)); @@ -352,7 +363,7 @@ private static Expression GetStringToEnumValueConversion<TResult>( return enumValues.Reverse().Aggregate( fallbackValue, - (valueSoFar, enumValue) => Expression.Condition( + (valueSoFar, enumValue) => Condition( sourceValue.GetCaseInsensitiveEquals(enumValue.ToString().ToConstantExpression()), ((TResult)enumValue).ToConstantExpression(fallbackValue.Type), valueSoFar)); @@ -410,8 +421,8 @@ private Expression GetEnumToEnumConversion( return valueSoFar; } - return Expression.Condition( - Expression.Equal(sourceValue, enumData.SourceValue.GetConversionTo(sourceValue.Type)), + return Condition( + Equal(sourceValue, enumData.SourceValue.GetConversionTo(sourceValue.Type)), enumData.PairedValue.Value.GetConversionTo(fallbackValue.Type), valueSoFar); }); @@ -420,20 +431,33 @@ private Expression GetEnumToEnumConversion( } private static IList<MemberExpression> GetEnumValues(Type enumType) - => enumType.GetPublicStaticFields().Project(f => Expression.Field(null, f)).ToList(); + => enumType.GetPublicStaticFields().Project(f => Field(null, f)).ToList(); private static Expression GetNumericToEnumConversion( Expression sourceValue, Expression fallbackValue, Type nonNullableSourceType, - Type nonNullableTargetEnumType) + Type nonNullableTargetEnumType, + bool useSingleStatement) { + if (!useSingleStatement) + { + return GetEnumMappingSwitchBlock( + sourceValue, + fallbackValue, + nonNullableTargetEnumType, + (enumValue, enumNumericType) => new[] { ChangeType(enumValue, enumNumericType) }, + (switchValue, enumNumericType) => switchValue.GetConversionTo(enumNumericType)); + } + + var targetEnumType = fallbackValue.Type; var underlyingEnumType = Enum.GetUnderlyingType(nonNullableTargetEnumType); var convertedNumericValue = sourceValue.GetConversionTo(underlyingEnumType); + var validEnumValues = GetEnumValuesConstant(targetEnumType, underlyingEnumType); - var validValueOrFallback = Expression.Condition( - GetIsValidEnumValueCheck(nonNullableTargetEnumType, convertedNumericValue), - sourceValue.GetConversionTo(nonNullableTargetEnumType).GetConversionTo(fallbackValue.Type), + var validValueOrFallback = Condition( + GetIsValidEnumValueCheck(convertedNumericValue, validEnumValues), + sourceValue.GetConversionTo(nonNullableTargetEnumType).GetConversionTo(targetEnumType), fallbackValue); if (sourceValue.Type == nonNullableSourceType) @@ -441,7 +465,7 @@ private static Expression GetNumericToEnumConversion( return validValueOrFallback; } - var nonNullValidValueOrFallback = Expression.Condition( + var nonNullValidValueOrFallback = Condition( sourceValue.GetIsNotDefaultComparison(), validValueOrFallback, fallbackValue); @@ -449,32 +473,103 @@ private static Expression GetNumericToEnumConversion( return nonNullValidValueOrFallback; } - private static Expression GetIsValidEnumValueCheck( - Type enumType, - Expression value, - Expression validEnumValues = null) + private static Expression GetEnumMappingSwitchBlock( + Expression sourceValue, + Expression fallbackValue, + Type nonNullableTargetEnumType, + Func<object, Type, object[]> caseTestValuesFactory, + Func<Expression, Type, Expression> switchValueFactory = null) { - if (validEnumValues == null) + var targetEnumType = fallbackValue.Type; + var enumNumericType = Enum.GetUnderlyingType(nonNullableTargetEnumType); + + var returnTarget = Label(targetEnumType, "Return"); + + var enumSwitchCases = nonNullableTargetEnumType.GetEnumValuesArray(v => { - validEnumValues = GetEnumValuesConstant(enumType, value.Type); + var enumValue = ChangeType(v, nonNullableTargetEnumType); + + var caseTestValues = caseTestValuesFactory + .Invoke(v, enumNumericType) + .ProjectToArray<object, Expression>(Constant); + + return SwitchCase( + Return(returnTarget, Constant(enumValue).GetConversionTo(targetEnumType)), + caseTestValues); + }); + + var mappingExpressions = new List<Expression>(); + + Expression switchValue; + + if (sourceValue.Type.CanBeNull()) + { + mappingExpressions.Add(IfThen( + sourceValue.GetIsDefaultComparison(), + Return(returnTarget, fallbackValue))); + + switchValue = sourceValue.Type.IsNullableType() + ? sourceValue.GetNullableValueAccess() + : sourceValue; + } + else + { + switchValue = sourceValue; + } + + if (switchValueFactory != null) + { + switchValue = switchValueFactory.Invoke(switchValue, enumNumericType); } - var containsMethod = validEnumValues.Type.GetPublicInstanceMethod("Contains"); - var containsCall = Expression.Call(validEnumValues, containsMethod, value); + mappingExpressions.Add(Switch(switchValue, enumSwitchCases)); + mappingExpressions.Add(Label(returnTarget, fallbackValue)); - return containsCall; + return Block(mappingExpressions); } + private static Expression GetIsValidEnumValueCheck(Expression value, Expression validEnumValues) + => Call(validEnumValues, validEnumValues.Type.GetPublicInstanceMethod("Contains"), value); + private static Expression GetStringValueConversion( Expression sourceValue, Expression fallbackValue, - Type nonNullableTargetEnumType) + Type nonNullableTargetEnumType, + bool useSingleStatement) { + if (!useSingleStatement && + sourceValue.Type.GetNonNullableType() == typeof(char)) + { + return GetEnumMappingSwitchBlock( + sourceValue, + fallbackValue, + nonNullableTargetEnumType, + (enumValue, enumNumericType) => new object[] + { + ChangeType(enumValue, enumNumericType).ToString()[0] + }); + } + if (sourceValue.Type != typeof(string)) { sourceValue = ToStringConverter.GetConversion(sourceValue); } + if (!useSingleStatement) + { + return GetEnumMappingSwitchBlock( + sourceValue, + fallbackValue, + nonNullableTargetEnumType, + (enumValue, enumNumericType) => new object[] + { + ChangeType(enumValue, enumNumericType).ToString(), + enumValue.ToString().ToUpperInvariant() + }, + (switchValue, _) => Call(switchValue, typeof(string) + .GetPublicInstanceMethod("ToUpperInvariant"))); + } + var underlyingEnumType = Enum.GetUnderlyingType(nonNullableTargetEnumType); var isNumericTest = GetIsNumericTest(sourceValue); @@ -490,12 +585,12 @@ private static Expression GetStringValueConversion( fallbackValue, nonNullableTargetEnumType); - var numericOrNameConversion = Expression.Condition( + var numericOrNameConversion = Condition( isNumericTest, numericConversion, nameMatchingConversion); - var convertedValueOrDefault = Expression.Condition( + var convertedValueOrDefault = Condition( StringExpressionExtensions.GetIsNullOrWhiteSpaceCall(sourceValue), fallbackValue, numericOrNameConversion); @@ -510,13 +605,13 @@ private static Expression GetNumericStringToEnumConversion( Type underlyingEnumType) { var validEnumValues = nonNullableTargetEnumType - .GetEnumValuesArray(v => Convert.ChangeType(v, underlyingEnumType).ToString()) + .GetEnumValuesArray(v => ChangeType(v, underlyingEnumType).ToString()) .ToConstantExpression(typeof(ICollection<string>)); var parsedString = GetStringParseCall(sourceValue, underlyingEnumType); - var validValueOrFallback = Expression.Condition( - GetIsValidEnumValueCheck(nonNullableTargetEnumType, sourceValue, validEnumValues), + var validValueOrFallback = Condition( + GetIsValidEnumValueCheck(sourceValue, validEnumValues), parsedString.GetConversionTo(fallbackValue.Type), fallbackValue); diff --git a/AgileMapper/TypeConversion/ToFormattedStringConverter.cs b/AgileMapper/TypeConversion/ToFormattedStringConverter.cs index 052f9d6ba..d86a2d0ac 100644 --- a/AgileMapper/TypeConversion/ToFormattedStringConverter.cs +++ b/AgileMapper/TypeConversion/ToFormattedStringConverter.cs @@ -25,7 +25,8 @@ public ToFormattedStringConverter(Type sourceValueType, string formattingString) if (_toStringMethod == null) { throw new MappingConfigurationException( - "No ToString method taking a formatting string exists on type " + sourceValueType.GetFriendlyName()); + "No ToString() method taking a formatting string exists " + + "on type " + sourceValueType.GetFriendlyName()); } _sourceValueType = sourceValueType; @@ -35,7 +36,10 @@ public ToFormattedStringConverter(Type sourceValueType, string formattingString) public bool CanConvert(Type nonNullableSourceType, Type nonNullableTargetType) => (nonNullableTargetType == typeof(string)) && (_sourceValueType == nonNullableSourceType); - public Expression GetConversion(Expression sourceValue, Type targetType) + public Expression GetConversion( + Expression sourceValue, + Type targetType, + bool useSingleStatement) { if (sourceValue.Type.IsNullableType()) { diff --git a/AgileMapper/TypeConversion/ToGuidConverter.cs b/AgileMapper/TypeConversion/ToGuidConverter.cs index 348ec6aaa..9a678b569 100644 --- a/AgileMapper/TypeConversion/ToGuidConverter.cs +++ b/AgileMapper/TypeConversion/ToGuidConverter.cs @@ -22,7 +22,7 @@ public bool CanConvert(Type nonNullableSourceType, Type nonNullableTargetType) ToStringConverter.HasNativeStringRepresentation(nonNullableSourceType)); } - public Expression GetConversion(Expression sourceValue, Type targetType) + public Expression GetConversion(Expression sourceValue, Type targetType, bool useSingleStatement) { if (sourceValue.Type == typeof(Guid?)) { diff --git a/AgileMapper/TypeConversion/ToNumericConverter.cs b/AgileMapper/TypeConversion/ToNumericConverter.cs index e87dc56d1..a3ab7fb23 100644 --- a/AgileMapper/TypeConversion/ToNumericConverter.cs +++ b/AgileMapper/TypeConversion/ToNumericConverter.cs @@ -14,7 +14,7 @@ internal class ToNumericConverter<TNumeric> : TryParseConverter<TNumeric> { #region Cached Items - public new static readonly ToNumericConverter<TNumeric> Instance = new ToNumericConverter<TNumeric>(); + public new static readonly ToNumericConverter<TNumeric> Instance = new(); private static readonly Type[] _coercibleNumericTypes = typeof(TNumeric).GetCoercibleNumericTypes(); @@ -44,7 +44,10 @@ protected override bool CanConvert(Type nonNullableSourceType) Constants.NumericTypes.Contains(nonNullableSourceType); } - public override Expression GetConversion(Expression sourceValue, Type targetType) + public override Expression GetConversion( + Expression sourceValue, + Type targetType, + bool useSingleStatement) { var sourceType = GetNonEnumSourceType(sourceValue); var nonNullableSourceType = sourceType.GetNonNullableType(); @@ -66,7 +69,7 @@ public override Expression GetConversion(Expression sourceValue, Type targetType return IsNumericType(nonNullableSourceType) ? GetCheckedNumericConversion(sourceValue, targetType) - : base.GetConversion(sourceValue, targetType); + : base.GetConversion(sourceValue, targetType, useSingleStatement); } private static Type GetNonEnumSourceType(Expression sourceValue) diff --git a/AgileMapper/TypeConversion/ToStringConverter.cs b/AgileMapper/TypeConversion/ToStringConverter.cs index f2d169335..2e5cf2482 100644 --- a/AgileMapper/TypeConversion/ToStringConverter.cs +++ b/AgileMapper/TypeConversion/ToStringConverter.cs @@ -35,7 +35,7 @@ private static bool HasToStringOperator(Type nonNullableSourceType, out MethodIn return operatorMethod != null; } - public Expression GetConversion(Expression sourceValue, Type targetType) + public Expression GetConversion(Expression sourceValue, Type targetType, bool useSingleStatement) { // Target type is always 'string': return GetConversion(sourceValue); diff --git a/AgileMapper/TypeConversion/TryParseConverter.cs b/AgileMapper/TypeConversion/TryParseConverter.cs index bfb4be672..a19be8f8a 100644 --- a/AgileMapper/TypeConversion/TryParseConverter.cs +++ b/AgileMapper/TypeConversion/TryParseConverter.cs @@ -12,7 +12,7 @@ namespace AgileObjects.AgileMapper.TypeConversion internal class TryParseConverter : IValueConverter { - public static readonly TryParseConverter Instance = new TryParseConverter(); + public static readonly TryParseConverter Instance = new(); public virtual bool CanConvert(Type nonNullableSourceType, Type nonNullableTargetType) { @@ -20,7 +20,10 @@ public virtual bool CanConvert(Type nonNullableSourceType, Type nonNullableTarge ToStringConverter.HasNativeStringRepresentation(nonNullableSourceType); } - public virtual Expression GetConversion(Expression sourceValue, Type targetType) + public virtual Expression GetConversion( + Expression sourceValue, + Type targetType, + bool useSingleStatement) { var nonNullableTargetType = targetType.GetNonNullableType(); var tryParseMethod = GetTryParseMethod(nonNullableTargetType); @@ -67,7 +70,7 @@ protected static MethodInfo GetTryParseMethod(Type nonNullableTargetType) internal class TryParseConverter<T> : TryParseConverter { - public new static readonly TryParseConverter<T> Instance = new TryParseConverter<T>(); + public new static readonly TryParseConverter<T> Instance = new(); private readonly Type _nonNullableTargetType; private readonly Type _nullableTargetType; @@ -92,7 +95,10 @@ protected virtual bool CanConvert(Type nonNullableSourceType) ToStringConverter.HasNativeStringRepresentation(nonNullableSourceType); } - public override Expression GetConversion(Expression sourceValue, Type targetType) + public override Expression GetConversion( + Expression sourceValue, + Type targetType, + bool useSingleStatement) { if (sourceValue.Type == _nullableTargetType) { diff --git a/AgileMapper/TypeConversion/TypeConversionExtensions.cs b/AgileMapper/TypeConversion/TypeConversionExtensions.cs index 95e7699d2..6f43ac549 100644 --- a/AgileMapper/TypeConversion/TypeConversionExtensions.cs +++ b/AgileMapper/TypeConversion/TypeConversionExtensions.cs @@ -48,8 +48,14 @@ public static Expression GetValueConversionOrCreation( return mapperData.GetConversionOrCreationExpression(value, targetType, valueFactories); } - public static Expression GetValueConversion(this IMemberMapperData mapperData, Expression value, Type targetType) - => mapperData.MapperContext.GetValueConversion(value, targetType); + public static Expression GetValueConversion<TContext>( + this TContext context, + Expression value, + Type targetType) + where TContext : IMapperContextOwner, IRuleSetOwner + { + return context.MapperContext.ValueConverters.GetConversion(value, targetType, context.RuleSet); + } private static bool HasConfiguredSimpleTypeValueFactories(this IMapperContextOwner mapperContextOwner) => mapperContextOwner.MapperContext.UserConfigurations.HasSimpleTypeValueFactories; From dfd396dbec32a9087b4a12780725684d121ef8a5 Mon Sep 17 00:00:00 2001 From: Steve Wilkes <steve@agileobjects.co.uk> Date: Mon, 17 May 2021 15:00:51 +0100 Subject: [PATCH 28/65] Optimising merge and overwrite mappings --- .../WhenBuildingEnumerableMergeMappers.cs | 58 +++++++++++++++++++ .../WhenBuildingEnumerableOverwriteMappers.cs | 48 +++++---------- .../WhenMappingOnToEnumerables.cs | 27 +++------ .../EnumerablePopulationBuilder.cs | 29 ++++------ .../EnumerablePopulationContext.cs | 25 +++++++- 5 files changed, 117 insertions(+), 70 deletions(-) create mode 100644 AgileMapper.Buildable.UnitTests/WhenBuildingEnumerableMergeMappers.cs diff --git a/AgileMapper.Buildable.UnitTests/WhenBuildingEnumerableMergeMappers.cs b/AgileMapper.Buildable.UnitTests/WhenBuildingEnumerableMergeMappers.cs new file mode 100644 index 000000000..13512f0be --- /dev/null +++ b/AgileMapper.Buildable.UnitTests/WhenBuildingEnumerableMergeMappers.cs @@ -0,0 +1,58 @@ +namespace AgileObjects.AgileMapper.Buildable.UnitTests +{ + using System.Collections.Generic; + using AgileMapper.UnitTests.Common; + using Xunit; + + public class WhenBuildingEnumerableMergeMappers + { + [Fact] + public void ShouldBuildASimpleTypeIEnumerableToICollectionMapper() + { + using (var mapper = Mapper.CreateNew()) + { + mapper.GetPlanFor<IEnumerable<int>>().OnTo<ICollection<int>>(); + + var sourceCodeExpressions = mapper.GetPlanSourceCodeInCache(); + + IEnumerable<int> source = new[] { 4, 5, 6 }; + ICollection<int> target = new[] { 1, 2, 3 }; + + var result = sourceCodeExpressions + .ShouldCompileAStaticMapperClass() + .GetMapMethods() + .ShouldHaveSingleItem() + .ShouldCreateMappingExecutor(source) + .ShouldHaveAMergeMethod() + .ShouldExecuteAMergeMapping(target); + + result.ShouldNotBeNull().ShouldNotBeSameAs(target); + result.ShouldBe(1, 2, 3, 4, 5, 6); + } + } + + [Fact] + public void ShouldBuildASimpleTypeArrayToHashSetMapper() + { + using (var mapper = Mapper.CreateNew()) + { + mapper.GetPlanFor<decimal[]>().OnTo<HashSet<double>>(); + + var sourceCodeExpressions = mapper.GetPlanSourceCodeInCache(); + + var source = new[] { 1.0m, 2.0m, 3.0m }; + var target = new HashSet<double> { 2.0, 3.0, 4.0 }; + + var result = sourceCodeExpressions + .ShouldCompileAStaticMapperClass() + .GetMapMethods() + .ShouldHaveSingleItem() + .ShouldCreateMappingExecutor(source) + .ShouldHaveAMergeMethod() + .ShouldExecuteAMergeMapping(target); + + result.ShouldNotBeNull().ShouldBe(2.0, 3.0, 4.0, 1.0); + } + } + } +} diff --git a/AgileMapper.Buildable.UnitTests/WhenBuildingEnumerableOverwriteMappers.cs b/AgileMapper.Buildable.UnitTests/WhenBuildingEnumerableOverwriteMappers.cs index 402eb6f49..770958933 100644 --- a/AgileMapper.Buildable.UnitTests/WhenBuildingEnumerableOverwriteMappers.cs +++ b/AgileMapper.Buildable.UnitTests/WhenBuildingEnumerableOverwriteMappers.cs @@ -18,20 +18,14 @@ public void ShouldBuildASimpleTypeArrayToArrayMapper() var sourceCodeExpressions = mapper.GetPlanSourceCodeInCache(); - var staticMapperClass = sourceCodeExpressions - .ShouldCompileAStaticMapperClass(); - - var staticMapMethod = staticMapperClass - .GetMapMethods() - .ShouldHaveSingleItem(); - var source = new[] { '5', '5', '5' }; var target = new[] { 3, 3, 3, 3 }; - var executor = staticMapMethod - .ShouldCreateMappingExecutor(source); - - var result = executor + var result = sourceCodeExpressions + .ShouldCompileAStaticMapperClass() + .GetMapMethods() + .ShouldHaveSingleItem() + .ShouldCreateMappingExecutor(source) .ShouldHaveAnOverwriteMethod() .ShouldExecuteAnOverwriteMapping(target); @@ -49,20 +43,14 @@ public void ShouldBuildASimpleTypeCollectionToListMapper() var sourceCodeExpressions = mapper.GetPlanSourceCodeInCache(); - var staticMapperClass = sourceCodeExpressions - .ShouldCompileAStaticMapperClass(); - - var staticMapMethod = staticMapperClass - .GetMapMethods() - .ShouldHaveSingleItem(); - var source = new Collection<string> { "I", "Will" }; var target = new List<string> { "You", "Might" }; - var executor = staticMapMethod - .ShouldCreateMappingExecutor(source); - - var result = executor + var result = sourceCodeExpressions + .ShouldCompileAStaticMapperClass() + .GetMapMethods() + .ShouldHaveSingleItem() + .ShouldCreateMappingExecutor(source) .ShouldHaveAnOverwriteMethod() .ShouldExecuteAnOverwriteMapping(target); @@ -80,13 +68,6 @@ public void ShouldBuildAComplexTypeArrayToReadOnlyCollectionMapper() var sourceCodeExpressions = mapper.GetPlanSourceCodeInCache(); - var staticMapperClass = sourceCodeExpressions - .ShouldCompileAStaticMapperClass(); - - var staticMapMethod = staticMapperClass - .GetMapMethods() - .ShouldHaveSingleItem(); - var source = new[] { new ProductDto { ProductId = "1", Price = 1.99m }, @@ -98,10 +79,11 @@ public void ShouldBuildAComplexTypeArrayToReadOnlyCollectionMapper() new Product { ProductId = "2", Price = 4.99 } }); - var executor = staticMapMethod - .ShouldCreateMappingExecutor(source); - - var result = executor + var result = sourceCodeExpressions + .ShouldCompileAStaticMapperClass() + .GetMapMethods() + .ShouldHaveSingleItem() + .ShouldCreateMappingExecutor(source) .ShouldHaveAnOverwriteMethod() .ShouldExecuteAnOverwriteMapping(target); diff --git a/AgileMapper.UnitTests/WhenMappingOnToEnumerables.cs b/AgileMapper.UnitTests/WhenMappingOnToEnumerables.cs index fa5985aec..646b40786 100644 --- a/AgileMapper.UnitTests/WhenMappingOnToEnumerables.cs +++ b/AgileMapper.UnitTests/WhenMappingOnToEnumerables.cs @@ -23,8 +23,7 @@ public void ShouldMergeARootSimpleTypeArray() var target = new[] { 1, 2, 3 }; var result = Mapper.Map(source).OnTo(target); - result.ShouldNotBeNull(); - result.ShouldBe(1, 2, 3, 4, 5, 6); + result.ShouldNotBeNull().ShouldBe(1, 2, 3, 4, 5, 6); } [Fact] @@ -34,8 +33,7 @@ public void ShouldMergeARootSimpleTypeReadOnlyCollection() var target = new ReadOnlyCollection<string>(new[] { "1", "2" }); var result = Mapper.Map(source).OnTo(target); - result.ShouldNotBeNull(); - result.ShouldBe("1", "2", "3", "4"); + result.ShouldNotBeNull().ShouldBe("1", "2", "3", "4"); } [Fact] @@ -45,8 +43,7 @@ public void ShouldMergeARootSimpleTypeList() var target = new List<string> { "Oh", "Heck", "Yes" }; var result = Mapper.Map(source).OnTo(target); - result.ShouldBeSameAs(target); - result.ShouldBe("Oh", "Heck", "Yes", "I", "Will"); + result.ShouldBeSameAs(target).ShouldBe("Oh", "Heck", "Yes", "I", "Will"); } [Fact] @@ -56,8 +53,7 @@ public void ShouldMergeARootSimpleTypeCollection() ICollection<string> target = new List<string> { "Four", "Three" }; var result = Mapper.Map(source).OnTo(target); - result.ShouldBeSameAs(target); - result.ShouldBe("Four", "Three", "Two", "One"); + result.ShouldBeSameAs(target).ShouldBe("Four", "Three", "Two", "One"); } [Fact] @@ -67,8 +63,7 @@ public void ShouldExcludeExistingTargetElementsInMerge() var target = new List<string> { "One", "Two" }; var result = Mapper.Map(source).OnTo(target); - result.ShouldBeSameAs(target); - result.ShouldBe("One", "Two", "Three"); + result.ShouldBeSameAs(target).ShouldBe("One", "Two", "Three"); } [Fact] @@ -78,8 +73,7 @@ public void ShouldNotExcludeAllExistingTargetElementsInMerge() var target = new List<string> { "One", "Two" }; var result = Mapper.Map(source).OnTo(target); - result.ShouldBeSameAs(target); - result.ShouldBe("One", "Two", "Two", "Three"); + result.ShouldBeSameAs(target).ShouldBe("One", "Two", "Two", "Three"); } [Fact] @@ -97,8 +91,7 @@ public void ShouldMergeARootComplexTypeList() var result = Mapper.Map(source).OnTo(target); - result.ShouldBeSameAs(target); - result.ShouldBe(p => p.Name, "Kate", "Pete"); + result.ShouldBeSameAs(target).ShouldBe(p => p.Name, "Kate", "Pete"); } [Fact] @@ -145,8 +138,7 @@ public void ShouldUpdateAnExistingObject() var result = Mapper.Map(source).OnTo(target); - result.ShouldBeSameAs(target); - result.First().Name.ShouldBe(source.First().Name); + result.ShouldBeSameAs(target).ShouldHaveSingleItem().Name.ShouldBe("Pete"); } [Fact] @@ -167,8 +159,7 @@ public void ShouldUpdateAnExistingObjectByConvertedId() var originalObject = target.First(); var result = Mapper.Map(source).OnTo(target); - result.ShouldBeSameAs(target); - result.ShouldContain(originalObject); + result.ShouldBeSameAs(target).ShouldContain(originalObject); result.First().Name.ShouldBe(source.First().Name); } diff --git a/AgileMapper/ObjectPopulation/Enumerables/EnumerablePopulationBuilder.cs b/AgileMapper/ObjectPopulation/Enumerables/EnumerablePopulationBuilder.cs index f328d5c88..7fdd5ddf0 100644 --- a/AgileMapper/ObjectPopulation/Enumerables/EnumerablePopulationBuilder.cs +++ b/AgileMapper/ObjectPopulation/Enumerables/EnumerablePopulationBuilder.cs @@ -48,7 +48,6 @@ public EnumerablePopulationBuilder(ObjectMapperData mapperData) Context = new EnumerablePopulationContext(mapperData); _sourceItemsSelector = new SourceItemsSelector(this); _sourceElementParameter = Context.SourceElementType.GetOrCreateParameter(); - TargetTypeHelper = new EnumerableTypeHelper(mapperData.TargetMember); _sourceAdapter = SourceEnumerableAdapterFactory.GetAdapterFor(this); _populationExpressions = new List<Expression>(); @@ -250,7 +249,7 @@ private static LambdaExpression GetTargetElementIdLambda(ParameterExpression tar public bool TargetElementsAreSimple => Context.TargetElementsAreSimple; - public EnumerableTypeHelper SourceTypeHelper { get; private set; } + public EnumerableTypeHelper SourceTypeHelper => Context.SourceTypeHelper; public Expression SourceValue { get; private set; } @@ -258,7 +257,7 @@ private static LambdaExpression GetTargetElementIdLambda(ParameterExpression tar public Expression GetSourceIndexAccess() => SourceValue.GetIndexAccess(Counter); - public EnumerableTypeHelper TargetTypeHelper { get; } + public EnumerableTypeHelper TargetTypeHelper => Context.TargetTypeHelper; public ParameterExpression TargetVariable { @@ -272,7 +271,7 @@ public void AssignSourceVariableToSourceObject() if (SourceVariableAlreadyAssignedTo(SourceValue)) { - CreateSourceTypeHelper(SourceValue); + Context.CreateSourceTypeHelper(SourceValue); return; } @@ -294,24 +293,17 @@ private bool SourceVariableAlreadyAssignedTo(Expression value) } public void AssignSourceVariableTo(Func<SourceItemsSelector, SourceItemsSelector> sourceItemsSelection) - => AssignSourceVariableTo(sourceItemsSelection.Invoke(_sourceItemsSelector).GetResult()); + => AssignSourceVariableTo(sourceItemsSelection.Invoke(_sourceItemsSelector)); private void AssignSourceVariableTo(Expression sourceValue) { - CreateSourceTypeHelper(sourceValue); + Context.CreateSourceTypeHelper(sourceValue); SourceValue = _sourceVariable = Context.GetSourceParameterFor(sourceValue.Type); _populationExpressions.Add(_sourceVariable.AssignTo(sourceValue)); } - private void CreateSourceTypeHelper(Expression sourceValue) - { - SourceTypeHelper = new EnumerableTypeHelper( - sourceValue.Type, - Context.ElementTypesAreTheSame ? Context.TargetElementType : sourceValue.Type.GetEnumerableElementType()); - } - #region Target Variable Population public void PopulateTargetVariableFromSourceObjectOnly(IObjectMappingData enumerableMappingData = null) @@ -321,7 +313,7 @@ private Expression GetSourceOnlyReturnValue(IObjectMappingData enumerableMapping { var convertedSourceItems = _sourceItemsSelector .SourceItemsProjectedToTargetType(enumerableMappingData) - .GetResult(); + .ToTargetType(); var returnValue = ConvertForReturnValue(convertedSourceItems); @@ -567,7 +559,7 @@ private bool InsertSourceObjectElementNullCheck(IPopulationLoopData loopData, ou } public Expression GetElementConversion( - Expression sourceElement, + Expression sourceElement, IObjectMappingData enumerableMappingData) { if (TargetElementsAreSimple) @@ -707,7 +699,7 @@ public void MapIntersection(IObjectMappingData enumerableMappingData) { var sourceElementParameter = Context.GetSourceParameterFor(Context.SourceElementType, prefix: "existing"); var targetElementParameter = Context.GetTargetParameterFor(Context.TargetElementType, prefix: "existing"); - + var defaultLoopCounter = _counterVariable; _counterVariable = Parameters.Create<int>("idx"); @@ -802,6 +794,9 @@ internal SourceItemsSelector(EnumerablePopulationBuilder builder) _builder = builder; } + public static implicit operator Expression(SourceItemsSelector selector) + => selector._result; + public SourceItemsSelector SourceItemsProjectedToTargetType( IObjectMappingData enumerableMappingData = null) { @@ -842,7 +837,7 @@ public SourceItemsSelector CollectionDataNewSourceItems() return this; } - public Expression GetResult() + public Expression ToTargetType() { if (_result.NodeType == ExpressionType.MemberAccess) { diff --git a/AgileMapper/ObjectPopulation/Enumerables/EnumerablePopulationContext.cs b/AgileMapper/ObjectPopulation/Enumerables/EnumerablePopulationContext.cs index 2d1ee179e..97e033e07 100644 --- a/AgileMapper/ObjectPopulation/Enumerables/EnumerablePopulationContext.cs +++ b/AgileMapper/ObjectPopulation/Enumerables/EnumerablePopulationContext.cs @@ -10,12 +10,16 @@ using Extensions.Internal; using Members; using NetStandardPolyfills; + using ReadableExpressions.Extensions; internal class EnumerablePopulationContext { + private Type _mappingSourceElementType; + public EnumerablePopulationContext(IQualifiedMemberContext context) { - SourceElementType = context.SourceMember.ElementType; + _mappingSourceElementType = SourceElementType = context.SourceMember.ElementType; + TargetTypeHelper = new EnumerableTypeHelper(context.TargetMember); if (SourceElementType == null) { @@ -34,12 +38,29 @@ public EnumerablePopulationContext(IQualifiedMemberContext context) public Type[] ElementTypes { get; } + /// <summary> + /// Gets a value indicating whether the mapping is being performed between collections with + /// the same element Types. + /// </summary> public bool ElementTypesAreTheSame { get; } public bool ElementTypesAreAssignable => SourceElementType.IsAssignableTo(TargetElementType); public bool TargetElementsAreSimple { get; } + public EnumerableTypeHelper SourceTypeHelper { get; private set; } + + public void CreateSourceTypeHelper(Expression sourceValue) + { + _mappingSourceElementType = ElementTypesAreTheSame + ? TargetElementType + : sourceValue.Type.GetEnumerableElementType(); + + SourceTypeHelper = new EnumerableTypeHelper(sourceValue.Type, _mappingSourceElementType); + } + + public EnumerableTypeHelper TargetTypeHelper { get; } + public ParameterExpression GetSourceParameterFor(Type type, string prefix = null) => GetParameterFor(type, prefix, "source"); @@ -53,7 +74,7 @@ private ParameterExpression GetParameterFor( { string parameterName; - if (ElementTypesAreTheSame) + if (_mappingSourceElementType == TargetElementType) { parameterName = prefix + From 79d714047ea7f86bd31887156efed70317b07755 Mon Sep 17 00:00:00 2001 From: Steve Wilkes <steve@agileobjects.co.uk> Date: Mon, 17 May 2021 15:28:52 +0100 Subject: [PATCH 29/65] Extending test coverage --- .../WhenBuildingEnumerableMergeMappers.cs | 33 +++++++++++++++++++ .../WhenMappingOnToEnumerables.cs | 6 ++-- .../EnumerablePopulationContext.cs | 9 ++--- 3 files changed, 41 insertions(+), 7 deletions(-) diff --git a/AgileMapper.Buildable.UnitTests/WhenBuildingEnumerableMergeMappers.cs b/AgileMapper.Buildable.UnitTests/WhenBuildingEnumerableMergeMappers.cs index 13512f0be..952f78db2 100644 --- a/AgileMapper.Buildable.UnitTests/WhenBuildingEnumerableMergeMappers.cs +++ b/AgileMapper.Buildable.UnitTests/WhenBuildingEnumerableMergeMappers.cs @@ -1,7 +1,9 @@ namespace AgileObjects.AgileMapper.Buildable.UnitTests { using System.Collections.Generic; + using System.Collections.ObjectModel; using AgileMapper.UnitTests.Common; + using AgileMapper.UnitTests.Common.TestClasses; using Xunit; public class WhenBuildingEnumerableMergeMappers @@ -54,5 +56,36 @@ public void ShouldBuildASimpleTypeArrayToHashSetMapper() result.ShouldNotBeNull().ShouldBe(2.0, 3.0, 4.0, 1.0); } } + + [Fact] + public void ShouldBuildAComplexTypeArrayToIEnumerableMapper() + { + using (var mapper = Mapper.CreateNew()) + { + mapper.GetPlanFor<Product[]>().OnTo<IEnumerable<Product>>(); + + var sourceCodeExpressions = mapper.GetPlanSourceCodeInCache(); + + var source = new[] + { + new Product { ProductId = "Steve" } + }; + + IEnumerable<Product> target = new ReadOnlyCollection<Product>(new[] + { + new Product { ProductId = "Kate" } + }); + + var result = sourceCodeExpressions + .ShouldCompileAStaticMapperClass() + .GetMapMethods() + .ShouldHaveSingleItem() + .ShouldCreateMappingExecutor(source) + .ShouldHaveAMergeMethod() + .ShouldExecuteAMergeMapping(target); + + result.ShouldNotBeNull().ShouldBe(p => p.ProductId, "Kate", "Steve"); + } + } } } diff --git a/AgileMapper.UnitTests/WhenMappingOnToEnumerables.cs b/AgileMapper.UnitTests/WhenMappingOnToEnumerables.cs index 646b40786..6c141b9f2 100644 --- a/AgileMapper.UnitTests/WhenMappingOnToEnumerables.cs +++ b/AgileMapper.UnitTests/WhenMappingOnToEnumerables.cs @@ -77,7 +77,7 @@ public void ShouldNotExcludeAllExistingTargetElementsInMerge() } [Fact] - public void ShouldMergeARootComplexTypeList() + public void ShouldMergeAComplexTypeList() { var source = new[] { @@ -95,7 +95,7 @@ public void ShouldMergeARootComplexTypeList() } [Fact] - public void ShouldMergeARootSimpleTypeHashSet() + public void ShouldMergeASimpleTypeHashSet() { var source = new[] { 1.0m, 2.0m, 3.0m }; var target = new HashSet<double> { 2.0, 3.0, 4.0 }; @@ -106,7 +106,7 @@ public void ShouldMergeARootSimpleTypeHashSet() } [Fact] - public void ShouldMergeARootComplexTypeReadOnlyCollection() + public void ShouldMergeAComplexTypeReadOnlyCollection() { var source = new[] { diff --git a/AgileMapper/ObjectPopulation/Enumerables/EnumerablePopulationContext.cs b/AgileMapper/ObjectPopulation/Enumerables/EnumerablePopulationContext.cs index 97e033e07..60561ea86 100644 --- a/AgileMapper/ObjectPopulation/Enumerables/EnumerablePopulationContext.cs +++ b/AgileMapper/ObjectPopulation/Enumerables/EnumerablePopulationContext.cs @@ -76,10 +76,11 @@ private ParameterExpression GetParameterFor( if (_mappingSourceElementType == TargetElementType) { - parameterName = - prefix + - sameTypesPrefix + - type.GetVariableNameInPascalCase(); + parameterName = prefix != null + ? prefix + sameTypesPrefix.ToPascalCase() + : sameTypesPrefix; + + parameterName += type.GetVariableNameInPascalCase(); } else { From 48fcc0dc56d6695e65f9ad16841cce4f3a3c86e5 Mon Sep 17 00:00:00 2001 From: Steve Wilkes <steve@agileobjects.co.uk> Date: Mon, 17 May 2021 18:38:40 +0100 Subject: [PATCH 30/65] Surfacing default eager MappingPlanSettings --- .../WhenBuildingDerivedTypeMappers.cs | 6 +-- .../WhenBuildingEnumerableCreateNewMappers.cs | 21 ++--------- .../WhenApplyingMapperConfigurations.cs | 6 +-- .../Api/IPlanTargetAndRuleSetSelector.cs | 22 ----------- AgileMapper/Api/PlanTargetSelector.cs | 11 +----- AgileMapper/MappingExecutor.cs | 2 +- AgileMapper/Plans/MappingPlanSettings.cs | 37 ++++++++++++------- AgileMapper/SimpleMappingContext.cs | 2 +- 8 files changed, 35 insertions(+), 72 deletions(-) diff --git a/AgileMapper.Buildable.UnitTests/WhenBuildingDerivedTypeMappers.cs b/AgileMapper.Buildable.UnitTests/WhenBuildingDerivedTypeMappers.cs index 2e9d6df54..9a86626ce 100644 --- a/AgileMapper.Buildable.UnitTests/WhenBuildingDerivedTypeMappers.cs +++ b/AgileMapper.Buildable.UnitTests/WhenBuildingDerivedTypeMappers.cs @@ -2,7 +2,6 @@ { using AgileMapper.UnitTests.Common; using AgileMapper.UnitTests.Common.TestClasses; - using Plans; using Xunit; public class WhenBuildingDerivedTypeMappers @@ -12,9 +11,8 @@ public void ShouldBuildADerivedTypeCreateNewMapper() { using (var mapper = Mapper.CreateNew()) { - mapper.GetPlanFor<Product>().ToANew<ProductDto>( - new MappingPlanSettings { LazyCompile = true }, - cfg => cfg.Map<MegaProduct>().To<ProductDtoMega>()); + mapper.GetPlanFor<Product>().ToANew<ProductDto>(cfg => + cfg.Map<MegaProduct>().To<ProductDtoMega>()); var sourceCodeExpressions = mapper.GetPlanSourceCodeInCache(); diff --git a/AgileMapper.Buildable.UnitTests/WhenBuildingEnumerableCreateNewMappers.cs b/AgileMapper.Buildable.UnitTests/WhenBuildingEnumerableCreateNewMappers.cs index 1bfb06184..3c77d423c 100644 --- a/AgileMapper.Buildable.UnitTests/WhenBuildingEnumerableCreateNewMappers.cs +++ b/AgileMapper.Buildable.UnitTests/WhenBuildingEnumerableCreateNewMappers.cs @@ -6,7 +6,6 @@ using System.Linq; using AgileMapper.UnitTests.Common; using AgileMapper.UnitTests.Common.TestClasses; - using Plans; using Xunit; public class WhenBuildingEnumerableCreateNewMappers @@ -16,10 +15,7 @@ public void ShouldBuildASimpleTypeListToCollectionMapper() { using (var mapper = Mapper.CreateNew()) { - mapper.GetPlanFor<List<string>>().ToANew<Collection<byte?>>(new MappingPlanSettings - { - LazyCompile = true - }); + mapper.GetPlanFor<List<string>>().ToANew<Collection<byte?>>(); var sourceCodeExpressions = mapper.GetPlanSourceCodeInCache(); @@ -49,10 +45,7 @@ public void ShouldBuildASimpleTypeArrayToReadOnlyCollectionMapper() { using (var mapper = Mapper.CreateNew()) { - mapper.GetPlanFor<int[]>().ToANew<ReadOnlyCollection<int>>(new MappingPlanSettings - { - LazyCompile = true - }); + mapper.GetPlanFor<int[]>().ToANew<ReadOnlyCollection<int>>(); var sourceCodeExpressions = mapper.GetPlanSourceCodeInCache(); @@ -82,10 +75,7 @@ public void ShouldBuildASimpleTypeHashSetToArrayMapper() { using (var mapper = Mapper.CreateNew()) { - mapper.GetPlanFor<HashSet<DateTime>>().ToANew<DateTime[]>(new MappingPlanSettings - { - LazyCompile = true - }); + mapper.GetPlanFor<HashSet<DateTime>>().ToANew<DateTime[]>(); var sourceCodeExpressions = mapper.GetPlanSourceCodeInCache(); @@ -118,10 +108,7 @@ public void ShouldBuildAComplexTypeListToIListMapper() { using (var mapper = Mapper.CreateNew()) { - mapper.GetPlanFor<List<ProductDto>>().ToANew<IList<ProductDto>>(new MappingPlanSettings - { - LazyCompile = true - }); + mapper.GetPlanFor<List<ProductDto>>().ToANew<IList<ProductDto>>(); var sourceCodeExpressions = mapper.GetPlanSourceCodeInCache(); diff --git a/AgileMapper.UnitTests/Configuration/WhenApplyingMapperConfigurations.cs b/AgileMapper.UnitTests/Configuration/WhenApplyingMapperConfigurations.cs index 9ebb4de47..1833c4277 100644 --- a/AgileMapper.UnitTests/Configuration/WhenApplyingMapperConfigurations.cs +++ b/AgileMapper.UnitTests/Configuration/WhenApplyingMapperConfigurations.cs @@ -8,7 +8,6 @@ using Common.TestClasses; using MoreTestClasses; using NetStandardPolyfills; - using Plans; using Testing; #if !NET35 using Xunit; @@ -215,10 +214,7 @@ protected override void Configure() .Map(ctx => ctx.Source.Value * 2) .To(t => t.Value); - GetPlanFor<PublicField<int>>().ToANew<PublicField<string>>(new MappingPlanSettings - { - LazyCompile = true - }); + GetPlanFor<PublicField<int>>().ToANew<PublicField<string>>(); } public static void VerifyConfigured(IMapper mapper) diff --git a/AgileMapper/Api/IPlanTargetAndRuleSetSelector.cs b/AgileMapper/Api/IPlanTargetAndRuleSetSelector.cs index c4a50d5f7..3d9f00193 100644 --- a/AgileMapper/Api/IPlanTargetAndRuleSetSelector.cs +++ b/AgileMapper/Api/IPlanTargetAndRuleSetSelector.cs @@ -31,28 +31,6 @@ public interface IPlanTargetAndRuleSetSelector<TSource> MappingPlan ToANew<TResult>( params Expression<Action<IFullMappingInlineConfigurator<TSource, TResult>>>[] configurations); - /// <summary> - /// Create and compile mapping functions for a create new mapping from the source type being - /// configured to the type specified by the type argument. - /// </summary> - /// <typeparam name="TResult">The type of object for which to create the mapping plan.</typeparam> - /// <param name="settings"> - /// <see cref="MappingPlanSettings"/> to control how the mapping plan should be produced. - /// </param> - /// <param name="configurations"> - /// Zero or more mapping configurations. If supplied, the mapping functions will be configured by - /// combining these inline <paramref name="configurations"/> with any configuration already set up - /// via the Mapper.WhenMapping API. - /// </param> - /// <returns> - /// A <see cref="MappingPlan"/> object detailing the function to be executed during a mapping. - /// To see a string representation of the function assign the result to a string variable, - /// or call .ToString(). - /// </returns> - MappingPlan ToANew<TResult>( - MappingPlanSettings settings, - params Expression<Action<IFullMappingInlineConfigurator<TSource, TResult>>>[] configurations); - /// <summary> /// Create and compile mapping functions for an OnTo (merge) mapping from the source type /// being configured to the type specified by the type argument. diff --git a/AgileMapper/Api/PlanTargetSelector.cs b/AgileMapper/Api/PlanTargetSelector.cs index 087a55c64..a206b1812 100644 --- a/AgileMapper/Api/PlanTargetSelector.cs +++ b/AgileMapper/Api/PlanTargetSelector.cs @@ -11,7 +11,7 @@ using ObjectPopulation; using Plans; using Queryables.Api; - using static Plans.MappingPlanSettings; + using static Plans.MappingPlanSettings.Default; internal class PlanTargetSelector<TSource> : IPlanTargetSelector<TSource>, @@ -48,17 +48,10 @@ public MappingPlanSet To<TTarget>( public MappingPlan ToANew<TResult>( Expression<Action<IFullMappingInlineConfigurator<TSource, TResult>>>[] configurations) - { - return ToANew(EagerPlanned, configurations); - } - - public MappingPlan ToANew<TResult>( - MappingPlanSettings settings, - Expression<Action<IFullMappingInlineConfigurator<TSource, TResult>>>[] configurations) { return GetMappingPlan( _mapperContext.RuleSets.CreateNew, - settings, + EagerPlanned, configurations); } diff --git a/AgileMapper/MappingExecutor.cs b/AgileMapper/MappingExecutor.cs index ac919d566..bcce2d292 100644 --- a/AgileMapper/MappingExecutor.cs +++ b/AgileMapper/MappingExecutor.cs @@ -31,7 +31,7 @@ public MappingExecutor(MapperContext mapperContext, TSource source) public MappingRuleSet RuleSet { get; private set; } - MappingPlanSettings IMappingContext.PlanSettings => MappingPlanSettings.LazyPlanned; + MappingPlanSettings IMappingContext.PlanSettings => MappingPlanSettings.Default.LazyPlanned; #region ToANew Overloads diff --git a/AgileMapper/Plans/MappingPlanSettings.cs b/AgileMapper/Plans/MappingPlanSettings.cs index 5366cd0c2..e59cebd6a 100644 --- a/AgileMapper/Plans/MappingPlanSettings.cs +++ b/AgileMapper/Plans/MappingPlanSettings.cs @@ -5,21 +5,32 @@ /// </summary> public class MappingPlanSettings { - internal static readonly MappingPlanSettings EagerPlanned = new() + /// <summary> + /// Wrapper class for the default <see cref="MappingPlanSettings"/> instances used when + /// creating mapping plans. + /// </summary> + public static class Default { - LazyCompile = false, - CommentUnmappableMembers = true, - CommentUnmappedMembers = true, - LazyLoadRepeatMappingFuncs = false - }; + /// <summary> + /// Gets or sets the default <see cref="MappingPlanSettings"/> to use when creating a + /// mapping plan upfront with Mapper.GetPlanFor<TSource>().To<TTarget>(). + /// </summary> + public static readonly MappingPlanSettings EagerPlanned = new() + { + LazyCompile = false, + CommentUnmappableMembers = true, + CommentUnmappedMembers = true, + LazyLoadRepeatMappingFuncs = false + }; - internal static readonly MappingPlanSettings LazyPlanned = new() - { - LazyCompile = false, - CommentUnmappableMembers = false, - CommentUnmappedMembers = false, - LazyLoadRepeatMappingFuncs = true - }; + internal static readonly MappingPlanSettings LazyPlanned = new() + { + LazyCompile = false, + CommentUnmappableMembers = false, + CommentUnmappedMembers = false, + LazyLoadRepeatMappingFuncs = true + }; + } /// <summary> /// Gets or sets a value indicating whether the mapping plan should lazy-compile its diff --git a/AgileMapper/SimpleMappingContext.cs b/AgileMapper/SimpleMappingContext.cs index 31502f4f0..f7937750c 100644 --- a/AgileMapper/SimpleMappingContext.cs +++ b/AgileMapper/SimpleMappingContext.cs @@ -5,7 +5,7 @@ internal class SimpleMappingContext : IMappingContext { public SimpleMappingContext(MappingRuleSet ruleSet, MapperContext mapperContext) - : this(ruleSet, MappingPlanSettings.LazyPlanned, mapperContext) + : this(ruleSet, MappingPlanSettings.Default.LazyPlanned, mapperContext) { } From 9759950000bd7ef95c0064460b04bd8af4d21846 Mon Sep 17 00:00:00 2001 From: Steve Wilkes <steve@agileobjects.co.uk> Date: Thu, 3 Jun 2021 15:24:40 +0100 Subject: [PATCH 31/65] Fixing build --- .../AgileMapper.Buildable.UnitTests.csproj | 1 + .../ExpressionBuilder.cs | 36 +++++++++++++++++++ .../AgileMapper.Buildable.csproj | 2 +- .../BuildableMapperExtensions.cs | 2 +- .../AgileMapper.UnitTests.NetCore.csproj | 1 - .../AgileMapper.UnitTests.Orms.EfCore1.csproj | 1 - 6 files changed, 39 insertions(+), 4 deletions(-) create mode 100644 AgileMapper.Buildable.UnitTests/ExpressionBuilder.cs diff --git a/AgileMapper.Buildable.UnitTests/AgileMapper.Buildable.UnitTests.csproj b/AgileMapper.Buildable.UnitTests/AgileMapper.Buildable.UnitTests.csproj index 9a82333f9..e494bb1f8 100644 --- a/AgileMapper.Buildable.UnitTests/AgileMapper.Buildable.UnitTests.csproj +++ b/AgileMapper.Buildable.UnitTests/AgileMapper.Buildable.UnitTests.csproj @@ -13,6 +13,7 @@ </PropertyGroup> <ItemGroup> + <PackageReference Include="AgileObjects.BuildableExpressions" Version="0.1.0-preview1" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.7.1" /> <PackageReference Include="xunit" Version="2.4.1" /> <PackageReference Include="xunit.runner.visualstudio" Version="2.4.3"> diff --git a/AgileMapper.Buildable.UnitTests/ExpressionBuilder.cs b/AgileMapper.Buildable.UnitTests/ExpressionBuilder.cs new file mode 100644 index 000000000..3d37c0f3a --- /dev/null +++ b/AgileMapper.Buildable.UnitTests/ExpressionBuilder.cs @@ -0,0 +1,36 @@ +namespace AgileObjects.AgileMapper.Buildable.UnitTests +{ + using System.Collections.Generic; + using System.Linq.Expressions; + using AgileObjects.BuildableExpressions; + using AgileObjects.BuildableExpressions.SourceCode; + + /// <summary> + /// Supplies a set of input <see cref="SourceCodeExpression"/>s to compile to source code when + /// this project is built. + /// </summary> + public class ExpressionBuilder : ISourceCodeExpressionBuilder + { + /// <summary> + /// Builds one or more <see cref="SourceCodeExpression"/>s to compile to source code files + /// when this project is built. + /// </summary> + /// <returns><see cref="SourceCodeExpression"/>s to compile to source code files.</returns> + public IEnumerable<SourceCodeExpression> Build() + { + // Replace this code with your own, building SourceCodeExpression(s) + // to be compiled to one or more source code files: + var sourceCode = BuildableExpression.SourceCode(sc => + { + sc.AddClass(nameof(ExpressionBuilder) + "OutputClass", cls => + { + var doNothing = Expression.Empty(); + + cls.AddMethod("DoNothing", doNothing); + }); + }); + + yield return sourceCode; + } + } +} diff --git a/AgileMapper.Buildable/AgileMapper.Buildable.csproj b/AgileMapper.Buildable/AgileMapper.Buildable.csproj index ca1c36f97..563eac92c 100644 --- a/AgileMapper.Buildable/AgileMapper.Buildable.csproj +++ b/AgileMapper.Buildable/AgileMapper.Buildable.csproj @@ -1,7 +1,7 @@ <Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> - <TargetFrameworks>net461;netstandard1.5</TargetFrameworks> + <TargetFrameworks>net461;netstandard2.0</TargetFrameworks> <LangVersion>8.0</LangVersion> <AssemblyTitle>AgileObjects.AgileMapper.Buildable</AssemblyTitle> <AssemblyName>AgileObjects.AgileMapper.Buildable</AssemblyName> diff --git a/AgileMapper.Buildable/BuildableMapperExtensions.cs b/AgileMapper.Buildable/BuildableMapperExtensions.cs index 1373097dd..3c48f669d 100644 --- a/AgileMapper.Buildable/BuildableMapperExtensions.cs +++ b/AgileMapper.Buildable/BuildableMapperExtensions.cs @@ -58,7 +58,7 @@ public static IEnumerable<SourceCodeExpression> GetPlanSourceCodeInCache( { ctor.SetConstructorCall( mapperGroup.MapperBaseTypeConstructor, - ctor.AddParameter("source", mapperGroup.SourceType)); + ctor.AddParameter(mapperGroup.SourceType, "source")); ctor.SetBody(Empty()); }); diff --git a/AgileMapper.UnitTests.NetCore/AgileMapper.UnitTests.NetCore.csproj b/AgileMapper.UnitTests.NetCore/AgileMapper.UnitTests.NetCore.csproj index 347b99318..0d100e0e2 100644 --- a/AgileMapper.UnitTests.NetCore/AgileMapper.UnitTests.NetCore.csproj +++ b/AgileMapper.UnitTests.NetCore/AgileMapper.UnitTests.NetCore.csproj @@ -2,7 +2,6 @@ <PropertyGroup> <TargetFramework>netcoreapp1.0</TargetFramework> - <RuntimeFrameworkVersion>1.1.13</RuntimeFrameworkVersion> <LangVersion>8.0</LangVersion> <AssemblyName>AgileObjects.AgileMapper.UnitTests.NetCore</AssemblyName> <RootNamespace>AgileObjects.AgileMapper.UnitTests</RootNamespace> diff --git a/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj b/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj index 72b747f3d..850752dc3 100644 --- a/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj +++ b/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj @@ -2,7 +2,6 @@ <PropertyGroup> <TargetFramework>netcoreapp1.0</TargetFramework> - <RuntimeFrameworkVersion>1.1.13</RuntimeFrameworkVersion> <LangVersion>8.0</LangVersion> <AssemblyName>AgileObjects.AgileMapper.UnitTests.Orms.EfCore1</AssemblyName> <RootNamespace>AgileObjects.AgileMapper.UnitTests.Orms.EfCore1</RootNamespace> From 5cc4ab766009c0953163a7ae9e37aee9b3336008 Mon Sep 17 00:00:00 2001 From: Steve Wilkes <steve@agileobjects.co.uk> Date: Tue, 15 Jun 2021 16:00:41 +0100 Subject: [PATCH 32/65] Generating mapper source code files and a static Mapper class --- .../AgileMapper.Buildable.UnitTests.csproj | 7 +- .../WhenBuildingDictionaryCreateNewMappers.cs | 70 +++--- .../ExpressionBuilder.cs | 36 ---- .../Mappers/Mapper.cs | 13 ++ .../Mappers/StringMapper.cs | 118 +++++++++++ .../WhenBuildingCircularReferenceMappers.cs | 90 ++++---- ...WhenBuildingComplexTypeCreateNewMappers.cs | 118 +++++------ .../WhenBuildingComplexTypeMergeMappers.cs | 46 ++-- ...WhenBuildingComplexTypeOverwriteMappers.cs | 46 ++-- .../WhenBuildingDerivedTypeMappers.cs | 88 ++++---- .../WhenBuildingEnumerableCreateNewMappers.cs | 200 +++++++++--------- .../WhenBuildingEnumerableMergeMappers.cs | 158 +++++++------- .../WhenBuildingEnumerableOverwriteMappers.cs | 172 +++++++-------- .../WhenBuildingRootEnumMappers.cs | 48 ++--- .../AgileMapper.Buildable.csproj | 19 +- .../BuildableMapperExtensions.cs | 183 ++++++++-------- .../BuildableMapperConfiguration.cs | 12 ++ ...AgileObjects.AgileMapper.Buildable.targets | 4 + AgileMapper.Buildable/MapperBuilder.cs | 40 ++++ AgileMapper/AgileMapper.csproj | 4 + .../MapperConfigurationSpecifier.cs | 2 +- .../Configuration/MapperConfiguration.cs | 7 +- AgileMapper/Mapper.cs | 24 ++- Directory.Build.props | 3 - 24 files changed, 830 insertions(+), 678 deletions(-) delete mode 100644 AgileMapper.Buildable.UnitTests/ExpressionBuilder.cs create mode 100644 AgileMapper.Buildable.UnitTests/Mappers/Mapper.cs create mode 100644 AgileMapper.Buildable.UnitTests/Mappers/StringMapper.cs create mode 100644 AgileMapper.Buildable/Configuration/BuildableMapperConfiguration.cs create mode 100644 AgileMapper.Buildable/MSBuild/AgileObjects.AgileMapper.Buildable.targets create mode 100644 AgileMapper.Buildable/MapperBuilder.cs diff --git a/AgileMapper.Buildable.UnitTests/AgileMapper.Buildable.UnitTests.csproj b/AgileMapper.Buildable.UnitTests/AgileMapper.Buildable.UnitTests.csproj index e494bb1f8..89825936d 100644 --- a/AgileMapper.Buildable.UnitTests/AgileMapper.Buildable.UnitTests.csproj +++ b/AgileMapper.Buildable.UnitTests/AgileMapper.Buildable.UnitTests.csproj @@ -12,8 +12,12 @@ <IsPackable>false</IsPackable> </PropertyGroup> + <PropertyGroup> + <XprGeneratorDebug>true</XprGeneratorDebug> + </PropertyGroup> + <ItemGroup> - <PackageReference Include="AgileObjects.BuildableExpressions" Version="0.1.0-preview1" /> + <PackageReference Include="AgileObjects.AgileMapper.Buildable" Version="0.1.0-preview1" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.7.1" /> <PackageReference Include="xunit" Version="2.4.1" /> <PackageReference Include="xunit.runner.visualstudio" Version="2.4.3"> @@ -23,7 +27,6 @@ </ItemGroup> <ItemGroup> - <ProjectReference Include="..\AgileMapper.Buildable\AgileMapper.Buildable.csproj" /> <ProjectReference Include="..\AgileMapper.UnitTests.Common\AgileMapper.UnitTests.Common.csproj" /> </ItemGroup> diff --git a/AgileMapper.Buildable.UnitTests/Dictionaries/WhenBuildingDictionaryCreateNewMappers.cs b/AgileMapper.Buildable.UnitTests/Dictionaries/WhenBuildingDictionaryCreateNewMappers.cs index be6cdf63f..d97bd59b7 100644 --- a/AgileMapper.Buildable.UnitTests/Dictionaries/WhenBuildingDictionaryCreateNewMappers.cs +++ b/AgileMapper.Buildable.UnitTests/Dictionaries/WhenBuildingDictionaryCreateNewMappers.cs @@ -7,40 +7,40 @@ public class WhenBuildingDictionaryCreateNewMappers { - [Fact] - public void ShouldBuildANestedComplexTypeToStringDictionaryMapper() - { - using (var mapper = Mapper.CreateNew()) - { - mapper.GetPlanFor<PublicTwoFields<int, Address>>().ToANew<Dictionary<string, string>>(); - - var sourceCodeExpressions = mapper.GetPlanSourceCodeInCache(); - - var staticMapperClass = sourceCodeExpressions - .ShouldCompileAStaticMapperClass(); - - var staticMapMethod = staticMapperClass - .GetMapMethods() - .ShouldHaveSingleItem(); - - var source = new PublicTwoFields<int, Address> - { - Value1 = 12345, - Value2 = new Address { Line1 = "Line 1!", Line2 = "Line 2!" } - }; - - var executor = staticMapMethod - .ShouldCreateMappingExecutor(source); - - var result = executor - .ShouldHaveACreateNewMethod() - .ShouldExecuteACreateNewMapping<Dictionary<string, string>>(); - - result.ShouldNotBeNull(); - result.ShouldContainKeyAndValue("Value1", "12345"); - result.ShouldContainKeyAndValue("Value2.Line1", "Line 1!"); - result.ShouldContainKeyAndValue("Value2.Line2", "Line 2!"); - } - } + //[Fact] + //public void ShouldBuildANestedComplexTypeToStringDictionaryMapper() + //{ + // using (var mapper = Mapper.CreateNew()) + // { + // mapper.GetPlanFor<PublicTwoFields<int, Address>>().ToANew<Dictionary<string, string>>(); + + // var sourceCodeExpressions = mapper.GetPlanSourceCodeInCache(); + + // var staticMapperClass = sourceCodeExpressions + // .ShouldCompileAStaticMapperClass(); + + // var staticMapMethod = staticMapperClass + // .GetMapMethods() + // .ShouldHaveSingleItem(); + + // var source = new PublicTwoFields<int, Address> + // { + // Value1 = 12345, + // Value2 = new Address { Line1 = "Line 1!", Line2 = "Line 2!" } + // }; + + // var executor = staticMapMethod + // .ShouldCreateMappingExecutor(source); + + // var result = executor + // .ShouldHaveACreateNewMethod() + // .ShouldExecuteACreateNewMapping<Dictionary<string, string>>(); + + // result.ShouldNotBeNull(); + // result.ShouldContainKeyAndValue("Value1", "12345"); + // result.ShouldContainKeyAndValue("Value2.Line1", "Line 1!"); + // result.ShouldContainKeyAndValue("Value2.Line2", "Line 2!"); + // } + //} } } diff --git a/AgileMapper.Buildable.UnitTests/ExpressionBuilder.cs b/AgileMapper.Buildable.UnitTests/ExpressionBuilder.cs deleted file mode 100644 index 3d37c0f3a..000000000 --- a/AgileMapper.Buildable.UnitTests/ExpressionBuilder.cs +++ /dev/null @@ -1,36 +0,0 @@ -namespace AgileObjects.AgileMapper.Buildable.UnitTests -{ - using System.Collections.Generic; - using System.Linq.Expressions; - using AgileObjects.BuildableExpressions; - using AgileObjects.BuildableExpressions.SourceCode; - - /// <summary> - /// Supplies a set of input <see cref="SourceCodeExpression"/>s to compile to source code when - /// this project is built. - /// </summary> - public class ExpressionBuilder : ISourceCodeExpressionBuilder - { - /// <summary> - /// Builds one or more <see cref="SourceCodeExpression"/>s to compile to source code files - /// when this project is built. - /// </summary> - /// <returns><see cref="SourceCodeExpression"/>s to compile to source code files.</returns> - public IEnumerable<SourceCodeExpression> Build() - { - // Replace this code with your own, building SourceCodeExpression(s) - // to be compiled to one or more source code files: - var sourceCode = BuildableExpression.SourceCode(sc => - { - sc.AddClass(nameof(ExpressionBuilder) + "OutputClass", cls => - { - var doNothing = Expression.Empty(); - - cls.AddMethod("DoNothing", doNothing); - }); - }); - - yield return sourceCode; - } - } -} diff --git a/AgileMapper.Buildable.UnitTests/Mappers/Mapper.cs b/AgileMapper.Buildable.UnitTests/Mappers/Mapper.cs new file mode 100644 index 000000000..86465c486 --- /dev/null +++ b/AgileMapper.Buildable.UnitTests/Mappers/Mapper.cs @@ -0,0 +1,13 @@ +namespace AgileObjects.AgileMapper.Buildable.UnitTests.Mappers +{ + public static class Mapper + { + public static StringMapper Map + ( + string source + ) + { + return new StringMapper(source); + } + } +} \ No newline at end of file diff --git a/AgileMapper.Buildable.UnitTests/Mappers/StringMapper.cs b/AgileMapper.Buildable.UnitTests/Mappers/StringMapper.cs new file mode 100644 index 000000000..183c82829 --- /dev/null +++ b/AgileMapper.Buildable.UnitTests/Mappers/StringMapper.cs @@ -0,0 +1,118 @@ +using System; +using AgileObjects.AgileMapper; +using AgileObjects.AgileMapper.ObjectPopulation; +using AgileObjects.AgileMapper.UnitTests.Common.TestClasses; +using AgileObjects.ReadableExpressions; +using AgileObjects.ReadableExpressions.Extensions; + +namespace AgileObjects.AgileMapper.Buildable.UnitTests.Mappers +{ + public class StringMapper : MappingExecutionContextBase<string> + { + public StringMapper + ( + string source + ) + : base(source) + { + } + + public TTarget ToANew<TTarget>() + { + if (typeof(TTarget) == typeof(Title)) + { + return (TTarget)((object)StringMapper.CreateNew(this.CreateRootMappingData(default(Title)))); + } + + throw new NotSupportedException( + "Unable to perform a 'CreateNew' mapping from source type 'string' to target type '" + typeof(TTarget).GetFriendlyName(null) + "'"); + } + + private static Title CreateNew + ( + IObjectMappingData<string, Title> sToTData + ) + { + try + { + if (sToTData.Source == null) + { + return default(Title); + } + + switch (sToTData.Source.ToUpperInvariant()) + { + case "0": + case "UNKNOWN": + return Title.Unknown; + + case "1": + case "MR": + return Title.Mr; + + case "2": + case "MASTER": + return Title.Master; + + case "3": + case "MS": + return Title.Ms; + + case "4": + case "MISS": + return Title.Miss; + + case "5": + case "MRS": + return Title.Mrs; + + case "6": + case "DR": + return Title.Dr; + + case "7": + case "HON": + return Title.Hon; + + case "8": + case "DUKE": + return Title.Duke; + + case "9": + case "COUNT": + return Title.Count; + + case "10": + case "EARL": + return Title.Earl; + + case "11": + case "VISCOUNT": + return Title.Viscount; + + case "12": + case "LORD": + return Title.Lord; + + case "13": + case "LADY": + return Title.Lady; + + case "14": + case "OTHER": + return Title.Other; + } + + return default(Title); + } + catch (Exception ex) + { + throw MappingException.For( + "CreateNew", + "string", + "Title", + ex); + } + } + } +} \ No newline at end of file diff --git a/AgileMapper.Buildable.UnitTests/WhenBuildingCircularReferenceMappers.cs b/AgileMapper.Buildable.UnitTests/WhenBuildingCircularReferenceMappers.cs index 4cf49788d..b5234dfa6 100644 --- a/AgileMapper.Buildable.UnitTests/WhenBuildingCircularReferenceMappers.cs +++ b/AgileMapper.Buildable.UnitTests/WhenBuildingCircularReferenceMappers.cs @@ -6,62 +6,62 @@ namespace AgileObjects.AgileMapper.Buildable.UnitTests public class WhenBuildingCircularReferenceMappers { - [Fact] - public void ShouldBuildACircularReferenceMapper() - { - using (var mapper = Mapper.CreateNew()) - { - mapper.GetPlanFor<Child>().ToANew<Child>(); + //[Fact] + //public void ShouldBuildACircularReferenceMapper() + //{ + // using (var mapper = Mapper.CreateNew()) + // { + // mapper.GetPlanFor<Child>().ToANew<Child>(); - var sourceCodeExpressions = mapper.GetPlanSourceCodeInCache(); + // var sourceCodeExpressions = mapper.GetPlanSourceCodeInCache(); - var staticMapperClass = sourceCodeExpressions - .ShouldCompileAStaticMapperClass(); + // var staticMapperClass = sourceCodeExpressions + // .ShouldCompileAStaticMapperClass(); - var staticMapMethod = staticMapperClass - .GetMapMethods() - .ShouldHaveSingleItem(); + // var staticMapMethod = staticMapperClass + // .GetMapMethods() + // .ShouldHaveSingleItem(); - var source = new Child - { - Name = "Fred", - EldestParent = new Parent - { - Name = "Bonnie", - EldestChild = new Child - { - Name = "Samson", - EldestParent = new Parent - { - Name = "Franklin" - } - } - } - }; + // var source = new Child + // { + // Name = "Fred", + // EldestParent = new Parent + // { + // Name = "Bonnie", + // EldestChild = new Child + // { + // Name = "Samson", + // EldestParent = new Parent + // { + // Name = "Franklin" + // } + // } + // } + // }; - source.EldestParent.EldestChild.EldestParent.EldestChild = source; + // source.EldestParent.EldestChild.EldestParent.EldestChild = source; - var executor = staticMapMethod - .ShouldCreateMappingExecutor(source); + // var executor = staticMapMethod + // .ShouldCreateMappingExecutor(source); - var result = executor - .ShouldHaveACreateNewMethod() - .ShouldExecuteACreateNewMapping<Child>(); + // var result = executor + // .ShouldHaveACreateNewMethod() + // .ShouldExecuteACreateNewMapping<Child>(); - result.ShouldNotBeNull().ShouldNotBeSameAs(source); + // result.ShouldNotBeNull().ShouldNotBeSameAs(source); - result.Name.ShouldBe("Fred"); - result.EldestParent.ShouldNotBeNull(); + // result.Name.ShouldBe("Fred"); + // result.EldestParent.ShouldNotBeNull(); - result.EldestParent.Name.ShouldBe("Bonnie"); - result.EldestParent.EldestChild.ShouldNotBeNull(); + // result.EldestParent.Name.ShouldBe("Bonnie"); + // result.EldestParent.EldestChild.ShouldNotBeNull(); - result.EldestParent.EldestChild.Name.ShouldBe("Samson"); - result.EldestParent.EldestChild.EldestParent.ShouldNotBeNull(); + // result.EldestParent.EldestChild.Name.ShouldBe("Samson"); + // result.EldestParent.EldestChild.EldestParent.ShouldNotBeNull(); - result.EldestParent.EldestChild.EldestParent.Name.ShouldBe("Franklin"); - result.EldestParent.EldestChild.EldestParent.EldestChild.ShouldBeSameAs(result); - } - } + // result.EldestParent.EldestChild.EldestParent.Name.ShouldBe("Franklin"); + // result.EldestParent.EldestChild.EldestParent.EldestChild.ShouldBeSameAs(result); + // } + //} } } \ No newline at end of file diff --git a/AgileMapper.Buildable.UnitTests/WhenBuildingComplexTypeCreateNewMappers.cs b/AgileMapper.Buildable.UnitTests/WhenBuildingComplexTypeCreateNewMappers.cs index 09763319d..e2450ea53 100644 --- a/AgileMapper.Buildable.UnitTests/WhenBuildingComplexTypeCreateNewMappers.cs +++ b/AgileMapper.Buildable.UnitTests/WhenBuildingComplexTypeCreateNewMappers.cs @@ -8,85 +8,85 @@ namespace AgileObjects.AgileMapper.Buildable.UnitTests public class WhenBuildingComplexTypeCreateNewMappers { - [Fact] - public void ShouldBuildSingleSourceSingleTargetMapper() - { - using (var mapper = Mapper.CreateNew()) - { - mapper.GetPlanFor<PublicField<string>>().ToANew<PublicField<int>>(); + //[Fact] + //public void ShouldBuildSingleSourceSingleTargetMapper() + //{ + // using (var mapper = Mapper.CreateNew()) + // { + // mapper.GetPlanFor<PublicField<string>>().ToANew<PublicField<int>>(); - var sourceCodeExpressions = mapper.GetPlanSourceCodeInCache(); + // var sourceCodeExpressions = mapper.GetPlanSourceCodeInCache(); - var staticMapperClass = sourceCodeExpressions - .ShouldCompileAStaticMapperClass(); + // var staticMapperClass = sourceCodeExpressions + // .ShouldCompileAStaticMapperClass(); - var staticMapMethod = staticMapperClass - .GetMapMethods() - .ShouldHaveSingleItem(); + // var staticMapMethod = staticMapperClass + // .GetMapMethods() + // .ShouldHaveSingleItem(); - var source = new PublicField<string> { Value = "123" }; + // var source = new PublicField<string> { Value = "123" }; - var executor = staticMapMethod - .ShouldCreateMappingExecutor(source); + // var executor = staticMapMethod + // .ShouldCreateMappingExecutor(source); - var result = executor - .ShouldHaveACreateNewMethod() - .ShouldExecuteACreateNewMapping<PublicField<int>>(); + // var result = executor + // .ShouldHaveACreateNewMethod() + // .ShouldExecuteACreateNewMapping<PublicField<int>>(); - result.Value.ShouldBe(123); - } - } + // result.Value.ShouldBe(123); + // } + //} - [Fact] - public void ShouldBuildSingleSourceMultipleTargetMapper() - { - using (var mapper = Mapper.CreateNew()) - { - mapper.GetPlanFor<PublicField<string>>().ToANew<PublicField<int>>(); - mapper.GetPlanFor<PublicField<string>>().ToANew<PublicProperty<string>>(); + //[Fact] + //public void ShouldBuildSingleSourceMultipleTargetMapper() + //{ + // using (var mapper = Mapper.CreateNew()) + // { + // mapper.GetPlanFor<PublicField<string>>().ToANew<PublicField<int>>(); + // mapper.GetPlanFor<PublicField<string>>().ToANew<PublicProperty<string>>(); - var sourceCodeExpressions = mapper.GetPlanSourceCodeInCache(); + // var sourceCodeExpressions = mapper.GetPlanSourceCodeInCache(); - var staticMapperClass = sourceCodeExpressions - .ShouldCompileAStaticMapperClass(); + // var staticMapperClass = sourceCodeExpressions + // .ShouldCompileAStaticMapperClass(); - var staticMapMethod = staticMapperClass - .GetMapMethods() - .ShouldHaveSingleItem(); + // var staticMapMethod = staticMapperClass + // .GetMapMethods() + // .ShouldHaveSingleItem(); - var source = new PublicField<string> { Value = "456" }; + // var source = new PublicField<string> { Value = "456" }; - var executor = staticMapMethod - .ShouldCreateMappingExecutor(source); + // var executor = staticMapMethod + // .ShouldCreateMappingExecutor(source); - var createNewMethod = executor.ShouldHaveACreateNewMethod(); + // var createNewMethod = executor.ShouldHaveACreateNewMethod(); - var publicFieldResult = createNewMethod - .ShouldExecuteACreateNewMapping<PublicField<int>>(); + // var publicFieldResult = createNewMethod + // .ShouldExecuteACreateNewMapping<PublicField<int>>(); - publicFieldResult.Value.ShouldBe(456); + // publicFieldResult.Value.ShouldBe(456); - var publicPropertyResult = createNewMethod - .ShouldExecuteACreateNewMapping<PublicProperty<string>>(); + // var publicPropertyResult = createNewMethod + // .ShouldExecuteACreateNewMapping<PublicProperty<string>>(); - publicPropertyResult.Value.ShouldBe("456"); + // publicPropertyResult.Value.ShouldBe("456"); - var configEx = Should.Throw<TargetInvocationException>(() => - { - createNewMethod - .ShouldExecuteACreateNewMapping<PublicField<DateTime>>(); - }); + // var configEx = Should.Throw<TargetInvocationException>(() => + // { + // createNewMethod + // .ShouldExecuteACreateNewMapping<PublicField<DateTime>>(); + // }); - var notSupportedMessage = configEx - .InnerException - .ShouldBeOfType<NotSupportedException>() - .Message; + // var notSupportedMessage = configEx + // .InnerException + // .ShouldBeOfType<NotSupportedException>() + // .Message; - notSupportedMessage.ShouldContain("Unable"); - notSupportedMessage.ShouldContain("CreateNew"); - notSupportedMessage.ShouldContain("source type 'PublicField<string>'"); - notSupportedMessage.ShouldContain("target type 'PublicField<DateTime>'"); - } - } + // notSupportedMessage.ShouldContain("Unable"); + // notSupportedMessage.ShouldContain("CreateNew"); + // notSupportedMessage.ShouldContain("source type 'PublicField<string>'"); + // notSupportedMessage.ShouldContain("target type 'PublicField<DateTime>'"); + // } + //} } } \ No newline at end of file diff --git a/AgileMapper.Buildable.UnitTests/WhenBuildingComplexTypeMergeMappers.cs b/AgileMapper.Buildable.UnitTests/WhenBuildingComplexTypeMergeMappers.cs index af21a9e24..5f747487b 100644 --- a/AgileMapper.Buildable.UnitTests/WhenBuildingComplexTypeMergeMappers.cs +++ b/AgileMapper.Buildable.UnitTests/WhenBuildingComplexTypeMergeMappers.cs @@ -6,35 +6,35 @@ namespace AgileObjects.AgileMapper.Buildable.UnitTests public class WhenBuildingComplexTypeMergeMappers { - [Fact] - public void ShouldBuildASingleSourceSingleTargetMapper() - { - using (var mapper = Mapper.CreateNew()) - { - mapper.GetPlanFor<Address>().OnTo<Address>(); + //[Fact] + //public void ShouldBuildASingleSourceSingleTargetMapper() + //{ + // using (var mapper = Mapper.CreateNew()) + // { + // mapper.GetPlanFor<Address>().OnTo<Address>(); - var sourceCodeExpressions = mapper.GetPlanSourceCodeInCache(); + // var sourceCodeExpressions = mapper.GetPlanSourceCodeInCache(); - var staticMapperClass = sourceCodeExpressions - .ShouldCompileAStaticMapperClass(); + // var staticMapperClass = sourceCodeExpressions + // .ShouldCompileAStaticMapperClass(); - var staticMapMethod = staticMapperClass - .GetMapMethods() - .ShouldHaveSingleItem(); + // var staticMapMethod = staticMapperClass + // .GetMapMethods() + // .ShouldHaveSingleItem(); - var source = new Address { Line1 = "Line 1!" }; - var target = new Address { Line2 = "Line 2!" }; + // var source = new Address { Line1 = "Line 1!" }; + // var target = new Address { Line2 = "Line 2!" }; - var executor = staticMapMethod - .ShouldCreateMappingExecutor(source); + // var executor = staticMapMethod + // .ShouldCreateMappingExecutor(source); - executor - .ShouldHaveAMergeMethod() - .ShouldExecuteAMergeMapping(target); + // executor + // .ShouldHaveAMergeMethod() + // .ShouldExecuteAMergeMapping(target); - target.Line1.ShouldBe("Line 1!"); - target.Line2.ShouldBe("Line 2!"); - } - } + // target.Line1.ShouldBe("Line 1!"); + // target.Line2.ShouldBe("Line 2!"); + // } + //} } } diff --git a/AgileMapper.Buildable.UnitTests/WhenBuildingComplexTypeOverwriteMappers.cs b/AgileMapper.Buildable.UnitTests/WhenBuildingComplexTypeOverwriteMappers.cs index 763d20aa1..eefb19ccc 100644 --- a/AgileMapper.Buildable.UnitTests/WhenBuildingComplexTypeOverwriteMappers.cs +++ b/AgileMapper.Buildable.UnitTests/WhenBuildingComplexTypeOverwriteMappers.cs @@ -6,35 +6,35 @@ namespace AgileObjects.AgileMapper.Buildable.UnitTests public class WhenBuildingComplexTypeOverwriteMappers { - [Fact] - public void ShouldBuildASingleSourceSingleTargetMapper() - { - using (var mapper = Mapper.CreateNew()) - { - mapper.GetPlanFor<Address>().Over<Address>(); + //[Fact] + //public void ShouldBuildASingleSourceSingleTargetMapper() + //{ + // using (var mapper = Mapper.CreateNew()) + // { + // mapper.GetPlanFor<Address>().Over<Address>(); - var sourceCodeExpressions = mapper.GetPlanSourceCodeInCache(); + // var sourceCodeExpressions = mapper.GetPlanSourceCodeInCache(); - var staticMapperClass = sourceCodeExpressions - .ShouldCompileAStaticMapperClass(); + // var staticMapperClass = sourceCodeExpressions + // .ShouldCompileAStaticMapperClass(); - var staticMapMethod = staticMapperClass - .GetMapMethods() - .ShouldHaveSingleItem(); + // var staticMapMethod = staticMapperClass + // .GetMapMethods() + // .ShouldHaveSingleItem(); - var source = new Address { Line1 = "1.1", Line2 = "1.2" }; - var target = new Address { Line1 = "2.1", Line2 = "2.2" }; + // var source = new Address { Line1 = "1.1", Line2 = "1.2" }; + // var target = new Address { Line1 = "2.1", Line2 = "2.2" }; - var executor = staticMapMethod - .ShouldCreateMappingExecutor(source); + // var executor = staticMapMethod + // .ShouldCreateMappingExecutor(source); - executor - .ShouldHaveAnOverwriteMethod() - .ShouldExecuteAnOverwriteMapping(target); + // executor + // .ShouldHaveAnOverwriteMethod() + // .ShouldExecuteAnOverwriteMapping(target); - target.Line1.ShouldBe("1.1"); - target.Line2.ShouldBe("1.2"); - } - } + // target.Line1.ShouldBe("1.1"); + // target.Line2.ShouldBe("1.2"); + // } + //} } } \ No newline at end of file diff --git a/AgileMapper.Buildable.UnitTests/WhenBuildingDerivedTypeMappers.cs b/AgileMapper.Buildable.UnitTests/WhenBuildingDerivedTypeMappers.cs index 9a86626ce..4a65d620b 100644 --- a/AgileMapper.Buildable.UnitTests/WhenBuildingDerivedTypeMappers.cs +++ b/AgileMapper.Buildable.UnitTests/WhenBuildingDerivedTypeMappers.cs @@ -6,62 +6,62 @@ public class WhenBuildingDerivedTypeMappers { - [Fact] - public void ShouldBuildADerivedTypeCreateNewMapper() - { - using (var mapper = Mapper.CreateNew()) - { - mapper.GetPlanFor<Product>().ToANew<ProductDto>(cfg => - cfg.Map<MegaProduct>().To<ProductDtoMega>()); + //[Fact] + //public void ShouldBuildADerivedTypeCreateNewMapper() + //{ + // using (var mapper = Mapper.CreateNew()) + // { + // mapper.GetPlanFor<Product>().ToANew<ProductDto>(cfg => + // cfg.Map<MegaProduct>().To<ProductDtoMega>()); - var sourceCodeExpressions = mapper.GetPlanSourceCodeInCache(); + // var sourceCodeExpressions = mapper.GetPlanSourceCodeInCache(); - var staticMapperClass = sourceCodeExpressions - .ShouldCompileAStaticMapperClass(); + // var staticMapperClass = sourceCodeExpressions + // .ShouldCompileAStaticMapperClass(); - var staticMapMethod = staticMapperClass - .GetMapMethods() - .ShouldHaveSingleItem(); + // var staticMapMethod = staticMapperClass + // .GetMapMethods() + // .ShouldHaveSingleItem(); - var baseTypeSource = new Product { ProductId = "111", Price = 19.99 }; + // var baseTypeSource = new Product { ProductId = "111", Price = 19.99 }; - var baseTypeExecutor = staticMapMethod - .ShouldCreateMappingExecutor(baseTypeSource); + // var baseTypeExecutor = staticMapMethod + // .ShouldCreateMappingExecutor(baseTypeSource); - var baseTypeResult = baseTypeExecutor - .ShouldHaveACreateNewMethod() - .ShouldExecuteACreateNewMapping<ProductDto>(); + // var baseTypeResult = baseTypeExecutor + // .ShouldHaveACreateNewMethod() + // .ShouldExecuteACreateNewMapping<ProductDto>(); - baseTypeResult.ProductId.ShouldBe("111"); - baseTypeResult.Price.ShouldBe(19.99m); + // baseTypeResult.ProductId.ShouldBe("111"); + // baseTypeResult.Price.ShouldBe(19.99m); - var derivedTypeSource = new MegaProduct - { - ProductId = "222", - Price = 119.99, - HowMega = 1.0m - }; + // var derivedTypeSource = new MegaProduct + // { + // ProductId = "222", + // Price = 119.99, + // HowMega = 1.0m + // }; - var derivedTypeExecutor = staticMapMethod - .ShouldCreateMappingExecutor<Product>(derivedTypeSource); + // var derivedTypeExecutor = staticMapMethod + // .ShouldCreateMappingExecutor<Product>(derivedTypeSource); - var derivedTypeBaseTypeResult = derivedTypeExecutor - .ShouldHaveACreateNewMethod() - .ShouldExecuteACreateNewMapping<ProductDto>() - .ShouldBeOfType<ProductDtoMega>(); + // var derivedTypeBaseTypeResult = derivedTypeExecutor + // .ShouldHaveACreateNewMethod() + // .ShouldExecuteACreateNewMapping<ProductDto>() + // .ShouldBeOfType<ProductDtoMega>(); - derivedTypeBaseTypeResult.ProductId.ShouldBe("222"); - derivedTypeBaseTypeResult.Price.ShouldBe(119.99m); - derivedTypeBaseTypeResult.HowMega.ShouldBe("1.0"); + // derivedTypeBaseTypeResult.ProductId.ShouldBe("222"); + // derivedTypeBaseTypeResult.Price.ShouldBe(119.99m); + // derivedTypeBaseTypeResult.HowMega.ShouldBe("1.0"); - var derivedTypeDerivedTypeResult = derivedTypeExecutor - .ShouldHaveACreateNewMethod() - .ShouldExecuteACreateNewMapping<ProductDtoMega>(); + // var derivedTypeDerivedTypeResult = derivedTypeExecutor + // .ShouldHaveACreateNewMethod() + // .ShouldExecuteACreateNewMapping<ProductDtoMega>(); - derivedTypeDerivedTypeResult.ProductId.ShouldBe("222"); - derivedTypeDerivedTypeResult.Price.ShouldBe(119.99m); - derivedTypeDerivedTypeResult.HowMega.ShouldBe("1.0"); - } - } + // derivedTypeDerivedTypeResult.ProductId.ShouldBe("222"); + // derivedTypeDerivedTypeResult.Price.ShouldBe(119.99m); + // derivedTypeDerivedTypeResult.HowMega.ShouldBe("1.0"); + // } + //} } } diff --git a/AgileMapper.Buildable.UnitTests/WhenBuildingEnumerableCreateNewMappers.cs b/AgileMapper.Buildable.UnitTests/WhenBuildingEnumerableCreateNewMappers.cs index 3c77d423c..0a7801471 100644 --- a/AgileMapper.Buildable.UnitTests/WhenBuildingEnumerableCreateNewMappers.cs +++ b/AgileMapper.Buildable.UnitTests/WhenBuildingEnumerableCreateNewMappers.cs @@ -10,135 +10,135 @@ public class WhenBuildingEnumerableCreateNewMappers { - [Fact] - public void ShouldBuildASimpleTypeListToCollectionMapper() - { - using (var mapper = Mapper.CreateNew()) - { - mapper.GetPlanFor<List<string>>().ToANew<Collection<byte?>>(); + //[Fact] + //public void ShouldBuildASimpleTypeListToCollectionMapper() + //{ + // using (var mapper = Mapper.CreateNew()) + // { + // mapper.GetPlanFor<List<string>>().ToANew<Collection<byte?>>(); - var sourceCodeExpressions = mapper.GetPlanSourceCodeInCache(); + // var sourceCodeExpressions = mapper.GetPlanSourceCodeInCache(); - var staticMapperClass = sourceCodeExpressions - .ShouldCompileAStaticMapperClass(); + // var staticMapperClass = sourceCodeExpressions + // .ShouldCompileAStaticMapperClass(); - var staticMapMethod = staticMapperClass - .GetMapMethods() - .ShouldHaveSingleItem(); + // var staticMapMethod = staticMapperClass + // .GetMapMethods() + // .ShouldHaveSingleItem(); - var source = new List<string> { "3", "2", "1", "12345" }; + // var source = new List<string> { "3", "2", "1", "12345" }; - var executor = staticMapMethod - .ShouldCreateMappingExecutor(source); + // var executor = staticMapMethod + // .ShouldCreateMappingExecutor(source); - var result = executor - .ShouldHaveACreateNewMethod() - .ShouldExecuteACreateNewMapping<Collection<byte?>>(); + // var result = executor + // .ShouldHaveACreateNewMethod() + // .ShouldExecuteACreateNewMapping<Collection<byte?>>(); - result.ShouldNotBeNull(); - result.ShouldBe<byte?>(3, 2, 1, null); - } - } + // result.ShouldNotBeNull(); + // result.ShouldBe<byte?>(3, 2, 1, null); + // } + //} - [Fact] - public void ShouldBuildASimpleTypeArrayToReadOnlyCollectionMapper() - { - using (var mapper = Mapper.CreateNew()) - { - mapper.GetPlanFor<int[]>().ToANew<ReadOnlyCollection<int>>(); + //[Fact] + //public void ShouldBuildASimpleTypeArrayToReadOnlyCollectionMapper() + //{ + // using (var mapper = Mapper.CreateNew()) + // { + // mapper.GetPlanFor<int[]>().ToANew<ReadOnlyCollection<int>>(); - var sourceCodeExpressions = mapper.GetPlanSourceCodeInCache(); + // var sourceCodeExpressions = mapper.GetPlanSourceCodeInCache(); - var staticMapperClass = sourceCodeExpressions - .ShouldCompileAStaticMapperClass(); + // var staticMapperClass = sourceCodeExpressions + // .ShouldCompileAStaticMapperClass(); - var staticMapMethod = staticMapperClass - .GetMapMethods() - .ShouldHaveSingleItem(); + // var staticMapMethod = staticMapperClass + // .GetMapMethods() + // .ShouldHaveSingleItem(); - var source = new[] { 1, 2, 3 }; + // var source = new[] { 1, 2, 3 }; - var executor = staticMapMethod - .ShouldCreateMappingExecutor(source); + // var executor = staticMapMethod + // .ShouldCreateMappingExecutor(source); - var result = executor - .ShouldHaveACreateNewMethod() - .ShouldExecuteACreateNewMapping<ReadOnlyCollection<int>>(); + // var result = executor + // .ShouldHaveACreateNewMethod() + // .ShouldExecuteACreateNewMapping<ReadOnlyCollection<int>>(); - result.ShouldNotBeNull(); - result.ShouldBe(1, 2, 3); - } - } + // result.ShouldNotBeNull(); + // result.ShouldBe(1, 2, 3); + // } + //} - [Fact] - public void ShouldBuildASimpleTypeHashSetToArrayMapper() - { - using (var mapper = Mapper.CreateNew()) - { - mapper.GetPlanFor<HashSet<DateTime>>().ToANew<DateTime[]>(); + //[Fact] + //public void ShouldBuildASimpleTypeHashSetToArrayMapper() + //{ + // using (var mapper = Mapper.CreateNew()) + // { + // mapper.GetPlanFor<HashSet<DateTime>>().ToANew<DateTime[]>(); - var sourceCodeExpressions = mapper.GetPlanSourceCodeInCache(); + // var sourceCodeExpressions = mapper.GetPlanSourceCodeInCache(); - var staticMapperClass = sourceCodeExpressions - .ShouldCompileAStaticMapperClass(); + // var staticMapperClass = sourceCodeExpressions + // .ShouldCompileAStaticMapperClass(); - var staticMapMethod = staticMapperClass - .GetMapMethods() - .ShouldHaveSingleItem(); + // var staticMapMethod = staticMapperClass + // .GetMapMethods() + // .ShouldHaveSingleItem(); - var today = DateTime.Today; - var tomorrow = today.AddDays(+1); - var yesterday = today.AddDays(-1); + // var today = DateTime.Today; + // var tomorrow = today.AddDays(+1); + // var yesterday = today.AddDays(-1); - var source = new HashSet<DateTime> { yesterday, today, tomorrow }; + // var source = new HashSet<DateTime> { yesterday, today, tomorrow }; - var executor = staticMapMethod - .ShouldCreateMappingExecutor(source); + // var executor = staticMapMethod + // .ShouldCreateMappingExecutor(source); - var result = executor - .ShouldHaveACreateNewMethod() - .ShouldExecuteACreateNewMapping<DateTime[]>(); + // var result = executor + // .ShouldHaveACreateNewMethod() + // .ShouldExecuteACreateNewMapping<DateTime[]>(); - result.ShouldNotBeNull().ShouldBe(yesterday, today, tomorrow); - } - } + // result.ShouldNotBeNull().ShouldBe(yesterday, today, tomorrow); + // } + //} - [Fact] - public void ShouldBuildAComplexTypeListToIListMapper() - { - using (var mapper = Mapper.CreateNew()) - { - mapper.GetPlanFor<List<ProductDto>>().ToANew<IList<ProductDto>>(); + //[Fact] + //public void ShouldBuildAComplexTypeListToIListMapper() + //{ + // using (var mapper = Mapper.CreateNew()) + // { + // mapper.GetPlanFor<List<ProductDto>>().ToANew<IList<ProductDto>>(); - var sourceCodeExpressions = mapper.GetPlanSourceCodeInCache(); + // var sourceCodeExpressions = mapper.GetPlanSourceCodeInCache(); - var staticMapperClass = sourceCodeExpressions - .ShouldCompileAStaticMapperClass(); + // var staticMapperClass = sourceCodeExpressions + // .ShouldCompileAStaticMapperClass(); - var staticMapMethod = staticMapperClass - .GetMapMethods() - .ShouldHaveSingleItem(); + // var staticMapMethod = staticMapperClass + // .GetMapMethods() + // .ShouldHaveSingleItem(); - var source = new List<ProductDto> - { - new ProductDto { ProductId = "Surprise" }, - null, - new ProductDto { ProductId = "Boomstick" } - }; + // var source = new List<ProductDto> + // { + // new ProductDto { ProductId = "Surprise" }, + // null, + // new ProductDto { ProductId = "Boomstick" } + // }; - var executor = staticMapMethod - .ShouldCreateMappingExecutor(source); - - var result = executor - .ShouldHaveACreateNewMethod() - .ShouldExecuteACreateNewMapping<List<ProductDto>>(); - - result.ShouldNotBeNull(); - result.ShouldNotBeSameAs(source); - result.First().ShouldNotBeNull().ProductId.ShouldBe("Surprise"); - result.Second().ShouldBeNull(); - result.Third().ShouldNotBeNull().ProductId.ShouldBe("Boomstick"); - } - } + // var executor = staticMapMethod + // .ShouldCreateMappingExecutor(source); + + // var result = executor + // .ShouldHaveACreateNewMethod() + // .ShouldExecuteACreateNewMapping<List<ProductDto>>(); + + // result.ShouldNotBeNull(); + // result.ShouldNotBeSameAs(source); + // result.First().ShouldNotBeNull().ProductId.ShouldBe("Surprise"); + // result.Second().ShouldBeNull(); + // result.Third().ShouldNotBeNull().ProductId.ShouldBe("Boomstick"); + // } + //} } } diff --git a/AgileMapper.Buildable.UnitTests/WhenBuildingEnumerableMergeMappers.cs b/AgileMapper.Buildable.UnitTests/WhenBuildingEnumerableMergeMappers.cs index 952f78db2..c7dfbf375 100644 --- a/AgileMapper.Buildable.UnitTests/WhenBuildingEnumerableMergeMappers.cs +++ b/AgileMapper.Buildable.UnitTests/WhenBuildingEnumerableMergeMappers.cs @@ -8,84 +8,84 @@ public class WhenBuildingEnumerableMergeMappers { - [Fact] - public void ShouldBuildASimpleTypeIEnumerableToICollectionMapper() - { - using (var mapper = Mapper.CreateNew()) - { - mapper.GetPlanFor<IEnumerable<int>>().OnTo<ICollection<int>>(); - - var sourceCodeExpressions = mapper.GetPlanSourceCodeInCache(); - - IEnumerable<int> source = new[] { 4, 5, 6 }; - ICollection<int> target = new[] { 1, 2, 3 }; - - var result = sourceCodeExpressions - .ShouldCompileAStaticMapperClass() - .GetMapMethods() - .ShouldHaveSingleItem() - .ShouldCreateMappingExecutor(source) - .ShouldHaveAMergeMethod() - .ShouldExecuteAMergeMapping(target); - - result.ShouldNotBeNull().ShouldNotBeSameAs(target); - result.ShouldBe(1, 2, 3, 4, 5, 6); - } - } - - [Fact] - public void ShouldBuildASimpleTypeArrayToHashSetMapper() - { - using (var mapper = Mapper.CreateNew()) - { - mapper.GetPlanFor<decimal[]>().OnTo<HashSet<double>>(); - - var sourceCodeExpressions = mapper.GetPlanSourceCodeInCache(); - - var source = new[] { 1.0m, 2.0m, 3.0m }; - var target = new HashSet<double> { 2.0, 3.0, 4.0 }; - - var result = sourceCodeExpressions - .ShouldCompileAStaticMapperClass() - .GetMapMethods() - .ShouldHaveSingleItem() - .ShouldCreateMappingExecutor(source) - .ShouldHaveAMergeMethod() - .ShouldExecuteAMergeMapping(target); - - result.ShouldNotBeNull().ShouldBe(2.0, 3.0, 4.0, 1.0); - } - } - - [Fact] - public void ShouldBuildAComplexTypeArrayToIEnumerableMapper() - { - using (var mapper = Mapper.CreateNew()) - { - mapper.GetPlanFor<Product[]>().OnTo<IEnumerable<Product>>(); - - var sourceCodeExpressions = mapper.GetPlanSourceCodeInCache(); - - var source = new[] - { - new Product { ProductId = "Steve" } - }; - - IEnumerable<Product> target = new ReadOnlyCollection<Product>(new[] - { - new Product { ProductId = "Kate" } - }); - - var result = sourceCodeExpressions - .ShouldCompileAStaticMapperClass() - .GetMapMethods() - .ShouldHaveSingleItem() - .ShouldCreateMappingExecutor(source) - .ShouldHaveAMergeMethod() - .ShouldExecuteAMergeMapping(target); - - result.ShouldNotBeNull().ShouldBe(p => p.ProductId, "Kate", "Steve"); - } - } + //[Fact] + //public void ShouldBuildASimpleTypeIEnumerableToICollectionMapper() + //{ + // using (var mapper = Mapper.CreateNew()) + // { + // mapper.GetPlanFor<IEnumerable<int>>().OnTo<ICollection<int>>(); + + // var sourceCodeExpressions = mapper.GetPlanSourceCodeInCache(); + + // IEnumerable<int> source = new[] { 4, 5, 6 }; + // ICollection<int> target = new[] { 1, 2, 3 }; + + // var result = sourceCodeExpressions + // .ShouldCompileAStaticMapperClass() + // .GetMapMethods() + // .ShouldHaveSingleItem() + // .ShouldCreateMappingExecutor(source) + // .ShouldHaveAMergeMethod() + // .ShouldExecuteAMergeMapping(target); + + // result.ShouldNotBeNull().ShouldNotBeSameAs(target); + // result.ShouldBe(1, 2, 3, 4, 5, 6); + // } + //} + + //[Fact] + //public void ShouldBuildASimpleTypeArrayToHashSetMapper() + //{ + // using (var mapper = Mapper.CreateNew()) + // { + // mapper.GetPlanFor<decimal[]>().OnTo<HashSet<double>>(); + + // var sourceCodeExpressions = mapper.GetPlanSourceCodeInCache(); + + // var source = new[] { 1.0m, 2.0m, 3.0m }; + // var target = new HashSet<double> { 2.0, 3.0, 4.0 }; + + // var result = sourceCodeExpressions + // .ShouldCompileAStaticMapperClass() + // .GetMapMethods() + // .ShouldHaveSingleItem() + // .ShouldCreateMappingExecutor(source) + // .ShouldHaveAMergeMethod() + // .ShouldExecuteAMergeMapping(target); + + // result.ShouldNotBeNull().ShouldBe(2.0, 3.0, 4.0, 1.0); + // } + //} + + //[Fact] + //public void ShouldBuildAComplexTypeArrayToIEnumerableMapper() + //{ + // using (var mapper = Mapper.CreateNew()) + // { + // mapper.GetPlanFor<Product[]>().OnTo<IEnumerable<Product>>(); + + // var sourceCodeExpressions = mapper.GetPlanSourceCodeInCache(); + + // var source = new[] + // { + // new Product { ProductId = "Steve" } + // }; + + // IEnumerable<Product> target = new ReadOnlyCollection<Product>(new[] + // { + // new Product { ProductId = "Kate" } + // }); + + // var result = sourceCodeExpressions + // .ShouldCompileAStaticMapperClass() + // .GetMapMethods() + // .ShouldHaveSingleItem() + // .ShouldCreateMappingExecutor(source) + // .ShouldHaveAMergeMethod() + // .ShouldExecuteAMergeMapping(target); + + // result.ShouldNotBeNull().ShouldBe(p => p.ProductId, "Kate", "Steve"); + // } + //} } } diff --git a/AgileMapper.Buildable.UnitTests/WhenBuildingEnumerableOverwriteMappers.cs b/AgileMapper.Buildable.UnitTests/WhenBuildingEnumerableOverwriteMappers.cs index 770958933..1a40daed1 100644 --- a/AgileMapper.Buildable.UnitTests/WhenBuildingEnumerableOverwriteMappers.cs +++ b/AgileMapper.Buildable.UnitTests/WhenBuildingEnumerableOverwriteMappers.cs @@ -9,91 +9,91 @@ public class WhenBuildingEnumerableOverwriteMappers { - [Fact] - public void ShouldBuildASimpleTypeArrayToArrayMapper() - { - using (var mapper = Mapper.CreateNew()) - { - mapper.GetPlanFor<char[]>().Over<int[]>(); - - var sourceCodeExpressions = mapper.GetPlanSourceCodeInCache(); - - var source = new[] { '5', '5', '5' }; - var target = new[] { 3, 3, 3, 3 }; - - var result = sourceCodeExpressions - .ShouldCompileAStaticMapperClass() - .GetMapMethods() - .ShouldHaveSingleItem() - .ShouldCreateMappingExecutor(source) - .ShouldHaveAnOverwriteMethod() - .ShouldExecuteAnOverwriteMapping(target); - - result.ShouldNotBeNull().ShouldNotBeSameAs(target); - result.ShouldBe(5, 5, 5); - } - } - - [Fact] - public void ShouldBuildASimpleTypeCollectionToListMapper() - { - using (var mapper = Mapper.CreateNew()) - { - mapper.GetPlanFor<Collection<string>>().Over<List<string>>(); - - var sourceCodeExpressions = mapper.GetPlanSourceCodeInCache(); - - var source = new Collection<string> { "I", "Will" }; - var target = new List<string> { "You", "Might" }; - - var result = sourceCodeExpressions - .ShouldCompileAStaticMapperClass() - .GetMapMethods() - .ShouldHaveSingleItem() - .ShouldCreateMappingExecutor(source) - .ShouldHaveAnOverwriteMethod() - .ShouldExecuteAnOverwriteMapping(target); - - result.ShouldNotBeNull().ShouldBeSameAs(target); - result.SequenceEqual(source).ShouldBeTrue(); - } - } - - [Fact] - public void ShouldBuildAComplexTypeArrayToReadOnlyCollectionMapper() - { - using (var mapper = Mapper.CreateNew()) - { - mapper.GetPlanFor<ProductDto[]>().Over<ReadOnlyCollection<Product>>(); - - var sourceCodeExpressions = mapper.GetPlanSourceCodeInCache(); - - var source = new[] - { - new ProductDto { ProductId = "1", Price = 1.99m }, - new ProductDto { ProductId = "2", Price = 2.99m }, - }; - - var target = new ReadOnlyCollection<Product>(new List<Product> - { - new Product { ProductId = "2", Price = 4.99 } - }); - - var result = sourceCodeExpressions - .ShouldCompileAStaticMapperClass() - .GetMapMethods() - .ShouldHaveSingleItem() - .ShouldCreateMappingExecutor(source) - .ShouldHaveAnOverwriteMethod() - .ShouldExecuteAnOverwriteMapping(target); - - result.ShouldNotBeNull(); - result.Count.ShouldBe(2); - result.First().ProductId.ShouldBe("2"); - result.First().Price.ShouldBe(2.99); - result.Second().ProductId.ShouldBe("1"); - result.Second().Price.ShouldBe(1.99); - } - } + //[Fact] + //public void ShouldBuildASimpleTypeArrayToArrayMapper() + //{ + // using (var mapper = Mapper.CreateNew()) + // { + // mapper.GetPlanFor<char[]>().Over<int[]>(); + + // var sourceCodeExpressions = mapper.GetPlanSourceCodeInCache(); + + // var source = new[] { '5', '5', '5' }; + // var target = new[] { 3, 3, 3, 3 }; + + // var result = sourceCodeExpressions + // .ShouldCompileAStaticMapperClass() + // .GetMapMethods() + // .ShouldHaveSingleItem() + // .ShouldCreateMappingExecutor(source) + // .ShouldHaveAnOverwriteMethod() + // .ShouldExecuteAnOverwriteMapping(target); + + // result.ShouldNotBeNull().ShouldNotBeSameAs(target); + // result.ShouldBe(5, 5, 5); + // } + //} + + //[Fact] + //public void ShouldBuildASimpleTypeCollectionToListMapper() + //{ + // using (var mapper = Mapper.CreateNew()) + // { + // mapper.GetPlanFor<Collection<string>>().Over<List<string>>(); + + // var sourceCodeExpressions = mapper.GetPlanSourceCodeInCache(); + + // var source = new Collection<string> { "I", "Will" }; + // var target = new List<string> { "You", "Might" }; + + // var result = sourceCodeExpressions + // .ShouldCompileAStaticMapperClass() + // .GetMapMethods() + // .ShouldHaveSingleItem() + // .ShouldCreateMappingExecutor(source) + // .ShouldHaveAnOverwriteMethod() + // .ShouldExecuteAnOverwriteMapping(target); + + // result.ShouldNotBeNull().ShouldBeSameAs(target); + // result.SequenceEqual(source).ShouldBeTrue(); + // } + //} + + //[Fact] + //public void ShouldBuildAComplexTypeArrayToReadOnlyCollectionMapper() + //{ + // using (var mapper = Mapper.CreateNew()) + // { + // mapper.GetPlanFor<ProductDto[]>().Over<ReadOnlyCollection<Product>>(); + + // var sourceCodeExpressions = mapper.GetPlanSourceCodeInCache(); + + // var source = new[] + // { + // new ProductDto { ProductId = "1", Price = 1.99m }, + // new ProductDto { ProductId = "2", Price = 2.99m }, + // }; + + // var target = new ReadOnlyCollection<Product>(new List<Product> + // { + // new Product { ProductId = "2", Price = 4.99 } + // }); + + // var result = sourceCodeExpressions + // .ShouldCompileAStaticMapperClass() + // .GetMapMethods() + // .ShouldHaveSingleItem() + // .ShouldCreateMappingExecutor(source) + // .ShouldHaveAnOverwriteMethod() + // .ShouldExecuteAnOverwriteMapping(target); + + // result.ShouldNotBeNull(); + // result.Count.ShouldBe(2); + // result.First().ProductId.ShouldBe("2"); + // result.First().Price.ShouldBe(2.99); + // result.Second().ProductId.ShouldBe("1"); + // result.Second().Price.ShouldBe(1.99); + // } + //} } } diff --git a/AgileMapper.Buildable.UnitTests/WhenBuildingRootEnumMappers.cs b/AgileMapper.Buildable.UnitTests/WhenBuildingRootEnumMappers.cs index 14964b727..e62e56323 100644 --- a/AgileMapper.Buildable.UnitTests/WhenBuildingRootEnumMappers.cs +++ b/AgileMapper.Buildable.UnitTests/WhenBuildingRootEnumMappers.cs @@ -2,48 +2,34 @@ { using AgileMapper.UnitTests.Common; using AgileMapper.UnitTests.Common.TestClasses; + using Configuration; using Xunit; + using GeneratedMapper = Mappers.Mapper; public class WhenBuildingRootEnumMappers { [Fact] public void ShouldBuildARootEnumMapper() { - using (var mapper = Mapper.CreateNew()) - { - mapper.GetPlanFor<string>().ToANew<Title>(); - - var sourceCodeExpressions = mapper.GetPlanSourceCodeInCache(); - - var staticMapperClass = sourceCodeExpressions - .ShouldCompileAStaticMapperClass(); - - var staticMapMethod = staticMapperClass - .GetMapMethods() - .ShouldHaveSingleItem(); - - var enumIdSource = ((int)Title.Mrs).ToString(); - - var enumIdExecutor = staticMapMethod - .ShouldCreateMappingExecutor(enumIdSource); - - var enumIdResult = enumIdExecutor - .ShouldHaveACreateNewMethod() - .ShouldExecuteACreateNewMapping<Title>(); + var enumIdSource = ((int)Title.Mrs).ToString(); + var enumIdResult = GeneratedMapper.Map(enumIdSource).ToANew<Title>(); + enumIdResult.ShouldBe(Title.Mrs); - enumIdResult.ShouldBe(Title.Mrs); - - var enumLabelSource = Title.Master.ToString(); - - var enumLabelIdExecutor = staticMapMethod - .ShouldCreateMappingExecutor(enumLabelSource); + var enumLabelSource = Title.Master.ToString(); + var enumLabelResult = GeneratedMapper.Map(enumLabelSource).ToANew<Title>(); + enumLabelResult.ShouldBe(Title.Master); + } - var enumLabelResult = enumLabelIdExecutor - .ShouldHaveACreateNewMethod() - .ShouldExecuteACreateNewMapping<Title>(); + #region Configuration - enumLabelResult.ShouldBe(Title.Master); + public class RootEnumMapperConfiguration : BuildableMapperConfiguration + { + protected override void Configure() + { + GetPlanFor<string>().ToANew<Title>(); } } + + #endregion } } diff --git a/AgileMapper.Buildable/AgileMapper.Buildable.csproj b/AgileMapper.Buildable/AgileMapper.Buildable.csproj index 563eac92c..f9c9b8785 100644 --- a/AgileMapper.Buildable/AgileMapper.Buildable.csproj +++ b/AgileMapper.Buildable/AgileMapper.Buildable.csproj @@ -1,4 +1,4 @@ -<Project Sdk="Microsoft.NET.Sdk"> +<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <TargetFrameworks>net461;netstandard2.0</TargetFrameworks> @@ -18,18 +18,27 @@ <PackageTags>Mapper, Mapping, Mappings, ViewModel, DTO, NetStandard</PackageTags> <PackageProjectUrl>https://github.com/AgileObjects/AgileMapper</PackageProjectUrl> <PackageLicenseExpression>MIT</PackageLicenseExpression> - - <Version>0.1.0</Version> + <PackageOutputPath>../NuGet</PackageOutputPath> + + <Version>0.1.0-preview1</Version> <AssemblyVersion>0.1.0.0</AssemblyVersion> <FileVersion>0.1.0.0</FileVersion> </PropertyGroup> + <PropertyGroup> + <XprGeneratorDebug>false</XprGeneratorDebug> + </PropertyGroup> + <ItemGroup> - <PackageReference Include="AgileObjects.BuildableExpressions" Version="0.1.0-preview1" /> + <None Remove="MSBuild\*.targets" /> + <Content Include="MSBuild\*.targets"> + <PackagePath>build</PackagePath> + </Content> </ItemGroup> <ItemGroup> - <ProjectReference Include="..\AgileMapper\AgileMapper.csproj" /> + <PackageReference Include="AgileObjects.AgileMapper" Version="2.0.0-preview1" /> + <PackageReference Include="AgileObjects.BuildableExpressions.Generator" Version="0.1.0-preview1" /> </ItemGroup> </Project> diff --git a/AgileMapper.Buildable/BuildableMapperExtensions.cs b/AgileMapper.Buildable/BuildableMapperExtensions.cs index 3c48f669d..63dee5e5f 100644 --- a/AgileMapper.Buildable/BuildableMapperExtensions.cs +++ b/AgileMapper.Buildable/BuildableMapperExtensions.cs @@ -14,118 +14,115 @@ using static BuildableExpressions.SourceCode.MemberVisibility; using static BuildableMapperConstants; - /// <summary> - /// Provides extension methods for building AgileMapper mapper source files. - /// </summary> - public static class BuildableMapperExtensions + internal static class BuildableMapperExtensions { - /// <summary> - /// Builds <see cref="SourceCodeExpression"/>s for the configured mappers in this - /// <paramref name="mapper"/>. - /// </summary> - /// <param name="mapper"> - /// The <see cref="IMapper"/> for which to build <see cref="SourceCodeExpression"/>s. - /// </param> - /// <returns> - /// A <see cref="SourceCodeExpression"/> for each mapper configured in this <paramref name="mapper"/>. - /// </returns> public static IEnumerable<SourceCodeExpression> GetPlanSourceCodeInCache( - this IMapper mapper) + this IMapper mapper, + string rootNamespace) { - yield return BuildableExpression - .SourceCode(sourceCode => - { - sourceCode.SetNamespace("AgileObjects.AgileMapper.Buildable"); + var mappersNamespace = rootNamespace + ".Mappers"; - var mapperClassGroups = mapper - .GetPlansInCache() - .GroupBy(plan => plan.Root.SourceType) - .Project(grp => new BuildableMapperGroup(grp.Key, grp.AsEnumerable())) - .OrderBy(grp => grp.MapperName) - .ToList(); + var mapperClassGroups = mapper + .GetPlansInCache() + .GroupBy(plan => plan.Root.SourceType) + .Project(grp => new BuildableMapperGroup(grp.Key, grp.AsEnumerable())) + .OrderBy(grp => grp.MapperName) + .ToList(); - foreach (var mapperGroup in mapperClassGroups) - { - mapperGroup.MapperClass = sourceCode.AddClass( - mapperGroup.MapperName, - mapperClass => - { - mapperGroup.MapperInstance = mapperClass.ThisInstanceExpression; + if (mapperClassGroups.Count == 0) + { + yield break; + } - mapperClass.SetBaseType(mapperGroup.MapperBaseType); + foreach (var mapperGroup in mapperClassGroups) + { + yield return BuildableExpression.SourceCode(sourceCode => + { + sourceCode.SetNamespace(mappersNamespace); - mapperClass.AddConstructor(ctor => - { - ctor.SetConstructorCall( - mapperGroup.MapperBaseTypeConstructor, - ctor.AddParameter(mapperGroup.SourceType, "source")); + mapperGroup.MapperClass = sourceCode.AddClass(mapperGroup.MapperName, mapperClass => + { + mapperGroup.MapperInstance = mapperClass.ThisInstanceExpression; - ctor.SetBody(Empty()); - }); + mapperClass.SetBaseType(mapperGroup.MapperBaseType); - foreach (var planAndMappingMethods in mapperGroup.MappingMethodsByPlan) - { - var plan = planAndMappingMethods.Key; - var mappingMethods = planAndMappingMethods.Value; - - foreach (var repeatPlan in plan.Skip(1)) - { - mappingMethods.Add(mapperClass.AddMethod(MapRepeated, doMapping => - { - doMapping.SetVisibility(Private); - doMapping.SetStatic(); - doMapping.SetBody(repeatPlan.Mapping); - })); - } - - mappingMethods.Insert(0, mapperClass.AddMethod(plan.RuleSetName, doMapping => - { - doMapping.SetVisibility(Private); - doMapping.SetStatic(); - doMapping.SetBody(plan.Root.Mapping); - })); - } - - RepeatMappingCallReplacer.Replace(mapperGroup); - - var allRuleSetMapMethodInfos = mapperGroup - .MappingMethodsByPlan.Values - .SelectMany(methods => methods) - .Filter(method => method.Name != MapRepeated) - .Project(method => new MapMethodInfo(mapperGroup, method)) - .GroupBy(m => m.RuleSetName) - .Select(methodGroup => methodGroup.ToList()); - - foreach (var ruleSetMapMethodInfos in allRuleSetMapMethodInfos) - { - AddMapMethodsFor(mapperClass, ruleSetMapMethodInfos); - } - }); - } + mapperClass.AddConstructor(ctor => + { + ctor.SetConstructorCall( + mapperGroup.MapperBaseTypeConstructor, + ctor.AddParameter(mapperGroup.SourceType, "source")); - sourceCode.AddClass("Mapper", staticMapperClass => - { - staticMapperClass.SetStatic(); + ctor.SetBody(Empty()); + }); - foreach (var mapperClassGroup in mapperClassGroups) + foreach (var planAndMappingMethods in mapperGroup.MappingMethodsByPlan) { - var sourceType = mapperClassGroup.SourceType; - var mapperClass = mapperClassGroup.MapperClass; + var plan = planAndMappingMethods.Key; + var mappingMethods = planAndMappingMethods.Value; + + foreach (var repeatPlan in plan.Skip(1)) + { + mappingMethods.Add(mapperClass.AddMethod(MapRepeated, doMapping => + { + doMapping.SetVisibility(Private); + doMapping.SetStatic(); + doMapping.SetBody(repeatPlan.Mapping); + })); + } - staticMapperClass.AddMethod("Map", mapMethod => + mappingMethods.Insert(0, mapperClass.AddMethod(plan.RuleSetName, doMapping => { - var sourceParameter = mapMethod - .AddParameter(sourceType, "source"); + doMapping.SetVisibility(Private); + doMapping.SetStatic(); + doMapping.SetBody(plan.Root.Mapping); + })); + } + + RepeatMappingCallReplacer.Replace(mapperGroup); - var newMapper = New( - mapperClass.Type.GetPublicInstanceConstructor(sourceType), - sourceParameter); + var allRuleSetMapMethodInfos = mapperGroup + .MappingMethodsByPlan.Values + .SelectMany(methods => methods) + .Filter(method => method.Name != MapRepeated) + .Project(method => new MapMethodInfo(mapperGroup, method)) + .GroupBy(m => m.RuleSetName) + .Select(methodGroup => methodGroup.ToList()); - mapMethod.SetBody(newMapper); - }); + foreach (var ruleSetMapMethodInfos in allRuleSetMapMethodInfos) + { + AddMapMethodsFor(mapperClass, ruleSetMapMethodInfos); } }); }); + } + + yield return BuildableExpression.SourceCode(sourceCode => + { + sourceCode.SetNamespace(mappersNamespace); + + sourceCode.AddClass("Mapper", staticMapperClass => + { + staticMapperClass.SetStatic(); + + foreach (var mapperClassGroup in mapperClassGroups) + { + var sourceType = mapperClassGroup.SourceType; + var mapperClass = mapperClassGroup.MapperClass; + + staticMapperClass.AddMethod("Map", mapMethod => + { + var sourceParameter = mapMethod + .AddParameter(sourceType, "source"); + + var newMapper = New( + mapperClass.Type.GetPublicInstanceConstructor(sourceType), + sourceParameter); + + mapMethod.SetBody(newMapper); + }); + } + }); + }); } private static void AddMapMethodsFor( diff --git a/AgileMapper.Buildable/Configuration/BuildableMapperConfiguration.cs b/AgileMapper.Buildable/Configuration/BuildableMapperConfiguration.cs new file mode 100644 index 000000000..bb2dcbe91 --- /dev/null +++ b/AgileMapper.Buildable/Configuration/BuildableMapperConfiguration.cs @@ -0,0 +1,12 @@ +namespace AgileObjects.AgileMapper.Buildable.Configuration +{ + using AgileMapper.Configuration; + + /// <summary> + /// Base class for multiple, buildable mapper configuration. Derived classes will be executed + /// to set up mapping configurations for which Mapper source code should be generated. + /// </summary> + public abstract class BuildableMapperConfiguration : MapperConfiguration + { + } +} diff --git a/AgileMapper.Buildable/MSBuild/AgileObjects.AgileMapper.Buildable.targets b/AgileMapper.Buildable/MSBuild/AgileObjects.AgileMapper.Buildable.targets new file mode 100644 index 000000000..75e4782fb --- /dev/null +++ b/AgileMapper.Buildable/MSBuild/AgileObjects.AgileMapper.Buildable.targets @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <Import Project="$(NuGetPackageRoot)agileobjects.buildableexpressions.generator\*\build\AgileObjects.BuildableExpressions.Generator.targets" Condition="Exists('$(NuGetPackageRoot)agileobjects.buildableexpressions.generator\0.1.0-preview1\build\AgileObjects.BuildableExpressions.Generator.targets')" /> +</Project> \ No newline at end of file diff --git a/AgileMapper.Buildable/MapperBuilder.cs b/AgileMapper.Buildable/MapperBuilder.cs new file mode 100644 index 000000000..e90835fa4 --- /dev/null +++ b/AgileMapper.Buildable/MapperBuilder.cs @@ -0,0 +1,40 @@ +namespace AgileObjects.AgileMapper.Buildable +{ + using System; + using System.Collections.Generic; + using System.Linq; + using BuildableExpressions; + using BuildableExpressions.SourceCode; + using Configuration; + + /// <summary> + /// An <see cref="ISourceCodeExpressionBuilder"/> which generates source code for configured + /// mappers. + /// </summary> + public class MapperBuilder : ISourceCodeExpressionBuilder + { + /// <inheritdoc /> + public IEnumerable<SourceCodeExpression> Build(IExpressionBuildContext context) + { + using var mapper = Mapper.CreateNew(); + + mapper.WhenMapping + .UseConfigurations.From(context.ProjectAssemblies); + + var mapperSourceCode = mapper + .GetPlanSourceCodeInCache(context.RootNamespace) + .ToList(); + + if (mapperSourceCode.Any()) + { + return mapperSourceCode; + } + + context.Log( + $"no {nameof(BuildableMapperConfiguration)}-derived " + + "Mapper configurations found"); + + return Array.Empty<SourceCodeExpression>(); + } + } +} \ No newline at end of file diff --git a/AgileMapper/AgileMapper.csproj b/AgileMapper/AgileMapper.csproj index 3d7d55022..168a47d98 100644 --- a/AgileMapper/AgileMapper.csproj +++ b/AgileMapper/AgileMapper.csproj @@ -24,6 +24,10 @@ - Throwing ObjectDisposed on attempt to use disposed Mapper, re: #212 - Improving target member selection, re: #209 </PackageReleaseNotes> + <Version>2.0.0-preview1</Version> + <AssemblyVersion>2.0.0.0</AssemblyVersion> + <FileVersion>2.0.0.0</FileVersion> + <PackageOutputPath>../NuGet</PackageOutputPath> </PropertyGroup> <PropertyGroup> diff --git a/AgileMapper/Api/Configuration/MapperConfigurationSpecifier.cs b/AgileMapper/Api/Configuration/MapperConfigurationSpecifier.cs index aad0e03e3..55fe48443 100644 --- a/AgileMapper/Api/Configuration/MapperConfigurationSpecifier.cs +++ b/AgileMapper/Api/Configuration/MapperConfigurationSpecifier.cs @@ -330,7 +330,7 @@ private static void ThrowIfInvalidAssemblySupplied(Assembly assembly) private static MappingConfigurationException NullAssemblySupplied(string message) { // ReSharper disable once NotResolvedInText - return new MappingConfigurationException(message, new ArgumentNullException("assemblies")); + return new(message, new ArgumentNullException("assemblies")); } private static bool AllAssemblies(Assembly assembly) => true; diff --git a/AgileMapper/Configuration/MapperConfiguration.cs b/AgileMapper/Configuration/MapperConfiguration.cs index 2d46b2469..20cad6111 100644 --- a/AgileMapper/Configuration/MapperConfiguration.cs +++ b/AgileMapper/Configuration/MapperConfiguration.cs @@ -108,8 +108,11 @@ protected IPlanTargetAndRuleSetSelector<TSource> GetPlanFor<TSource>() /// An IProjectionPlanTargetSelector with which to specify the target Type to which the query projection function to /// be created should be cached. /// </returns> - protected IProjectionPlanTargetSelector<TSourceElement> GetPlanForProjecting<TSourceElement>(IQueryable<TSourceElement> exampleQueryable) - => _mapper.GetPlanForProjecting(exampleQueryable); + protected IProjectionPlanTargetSelector<TSourceElement> GetPlanForProjecting<TSourceElement>( + IQueryable<TSourceElement> exampleQueryable) + { + return _mapper.GetPlanForProjecting(exampleQueryable); + } /// <summary> /// Create and compile mapping functions for mapping from the source type specified by the given diff --git a/AgileMapper/Mapper.cs b/AgileMapper/Mapper.cs index 1cf8e96e8..13acf9ee3 100644 --- a/AgileMapper/Mapper.cs +++ b/AgileMapper/Mapper.cs @@ -17,8 +17,8 @@ #endif /// <summary> - /// Provides a configurable mapping service. Create new instances with Mapper.CreateNew or use the default - /// instance via the static Mapper access methods. + /// Provides a configurable mapping service. Create new instances with Mapper.CreateNew or use + /// the default instance via the static Mapper access methods. /// </summary> public sealed class Mapper : IMapperInternal { @@ -45,8 +45,9 @@ private Mapper(MapperContext context) #region Static Access Methods /// <summary> - /// Create and compile mapping functions for a particular type of mapping of the source type specified by - /// the given <paramref name="exampleInstance"/>. Use this overload for anonymous types. + /// Create and compile mapping functions for a particular type of mapping of the + /// <typeparamref name="TSource"/> type, as specified by the given + /// <paramref name="exampleInstance"/>. Use this overload for anonymous types. /// </summary> /// <typeparam name="TSource">The type of the given <paramref name="exampleInstance"/>.</typeparam> /// <param name="exampleInstance"> @@ -59,8 +60,8 @@ private Mapper(MapperContext context) public static IPlanTargetAndRuleSetSelector<TSource> GetPlanFor<TSource>(TSource exampleInstance) => GetPlanFor<TSource>(); /// <summary> - /// Create and compile mapping functions for a particular type of mapping of the source type - /// specified by the type argument. + /// Create and compile mapping functions for a particular type of mapping of the + /// <typeparamref name="TSource"/> type. /// </summary> /// <typeparam name="TSource">The source type for which to create the mapping functions.</typeparam> /// <returns> @@ -70,9 +71,10 @@ private Mapper(MapperContext context) public static IPlanTargetAndRuleSetSelector<TSource> GetPlanFor<TSource>() => Default.GetPlanFor<TSource>(); /// <summary> - /// Create and compile mapping functions for mapping from the source type specified by the given - /// <paramref name="exampleInstance"/>, for all mapping types (create new, merge, overwrite). Use this - /// overload for anonymous types. + /// Create and compile mapping functions for mapping from the + /// <typeparamref name="TSource"/> type, as specified by the given + /// <paramref name="exampleInstance"/>, for all mapping types (create new, merge, overwrite). + /// Use this overload for anonymous types. /// </summary> /// <typeparam name="TSource">The source type for which to create the mapping functions.</typeparam> /// <param name="exampleInstance"> @@ -85,8 +87,8 @@ private Mapper(MapperContext context) public static IPlanTargetSelector<TSource> GetPlansFor<TSource>(TSource exampleInstance) => GetPlansFor<TSource>(); /// <summary> - /// Create and compile mapping functions for the source type specified by the type argument, for all - /// mapping types (create new, merge, overwrite). + /// Create and compile mapping functions for the + /// <typeparamref name="TSource"/> type, for all mapping types (create new, merge, overwrite). /// </summary> /// <typeparam name="TSource">The source type for which to create the mapping functions.</typeparam> /// <returns> diff --git a/Directory.Build.props b/Directory.Build.props index 9fed18da9..d95ebbf86 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -11,9 +11,6 @@ <PublicSign Condition=" '$(OS)' != 'Windows_NT' ">true</PublicSign> <RepositoryType>git</RepositoryType> <RepositoryUrl>https://github.com/AgileObjects/AgileMapper</RepositoryUrl> - <Version>1.8.0</Version> - <AssemblyVersion>1.8.0.0</AssemblyVersion> - <FileVersion>1.8.0.0</FileVersion> </PropertyGroup> </Project> \ No newline at end of file From c685cd9d3360e33a4386b569f308e4a4a5c0f72b Mon Sep 17 00:00:00 2001 From: Steve Wilkes <steve@agileobjects.co.uk> Date: Tue, 15 Jun 2021 16:09:39 +0100 Subject: [PATCH 33/65] Adding GeneratedCodeAttributes to generated Mapper classes --- .../AgileMapper.Buildable.UnitTests.csproj | 2 +- .../Mappers/Mapper.cs | 3 +++ .../Mappers/StringMapper.cs | 2 ++ .../BuildableMapperExtensions.cs | 18 ++++++++++++++++++ 4 files changed, 24 insertions(+), 1 deletion(-) diff --git a/AgileMapper.Buildable.UnitTests/AgileMapper.Buildable.UnitTests.csproj b/AgileMapper.Buildable.UnitTests/AgileMapper.Buildable.UnitTests.csproj index 89825936d..ecd95d5e1 100644 --- a/AgileMapper.Buildable.UnitTests/AgileMapper.Buildable.UnitTests.csproj +++ b/AgileMapper.Buildable.UnitTests/AgileMapper.Buildable.UnitTests.csproj @@ -13,7 +13,7 @@ </PropertyGroup> <PropertyGroup> - <XprGeneratorDebug>true</XprGeneratorDebug> + <XprGeneratorDebug>false</XprGeneratorDebug> </PropertyGroup> <ItemGroup> diff --git a/AgileMapper.Buildable.UnitTests/Mappers/Mapper.cs b/AgileMapper.Buildable.UnitTests/Mappers/Mapper.cs index 86465c486..40b84c07f 100644 --- a/AgileMapper.Buildable.UnitTests/Mappers/Mapper.cs +++ b/AgileMapper.Buildable.UnitTests/Mappers/Mapper.cs @@ -1,5 +1,8 @@ +using System.CodeDom.Compiler; + namespace AgileObjects.AgileMapper.Buildable.UnitTests.Mappers { + [GeneratedCode("AgileMapper.Buildable", "0.1.0.0")] public static class Mapper { public static StringMapper Map diff --git a/AgileMapper.Buildable.UnitTests/Mappers/StringMapper.cs b/AgileMapper.Buildable.UnitTests/Mappers/StringMapper.cs index 183c82829..431670003 100644 --- a/AgileMapper.Buildable.UnitTests/Mappers/StringMapper.cs +++ b/AgileMapper.Buildable.UnitTests/Mappers/StringMapper.cs @@ -1,4 +1,5 @@ using System; +using System.CodeDom.Compiler; using AgileObjects.AgileMapper; using AgileObjects.AgileMapper.ObjectPopulation; using AgileObjects.AgileMapper.UnitTests.Common.TestClasses; @@ -7,6 +8,7 @@ namespace AgileObjects.AgileMapper.Buildable.UnitTests.Mappers { + [GeneratedCode("AgileMapper.Buildable", "0.1.0.0")] public class StringMapper : MappingExecutionContextBase<string> { public StringMapper diff --git a/AgileMapper.Buildable/BuildableMapperExtensions.cs b/AgileMapper.Buildable/BuildableMapperExtensions.cs index 63dee5e5f..098ccc8b1 100644 --- a/AgileMapper.Buildable/BuildableMapperExtensions.cs +++ b/AgileMapper.Buildable/BuildableMapperExtensions.cs @@ -1,6 +1,7 @@ namespace AgileObjects.AgileMapper.Buildable { using System; + using System.CodeDom.Compiler; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; @@ -16,6 +17,12 @@ internal static class BuildableMapperExtensions { + private static readonly string _version = typeof(BuildableMapperExtensions) + .Assembly + .GetName() + .Version + .ToString(fieldCount: 4); + public static IEnumerable<SourceCodeExpression> GetPlanSourceCodeInCache( this IMapper mapper, string rootNamespace) @@ -44,6 +51,7 @@ public static IEnumerable<SourceCodeExpression> GetPlanSourceCodeInCache( { mapperGroup.MapperInstance = mapperClass.ThisInstanceExpression; + mapperClass.AddGeneratedCodeAttribute(); mapperClass.SetBaseType(mapperGroup.MapperBaseType); mapperClass.AddConstructor(ctor => @@ -102,6 +110,7 @@ public static IEnumerable<SourceCodeExpression> GetPlanSourceCodeInCache( sourceCode.AddClass("Mapper", staticMapperClass => { + staticMapperClass.AddGeneratedCodeAttribute(); staticMapperClass.SetStatic(); foreach (var mapperClassGroup in mapperClassGroups) @@ -250,6 +259,15 @@ private static void AddUpdateInstanceMapMethod( } } + private static void AddGeneratedCodeAttribute( + this IAttributableExpressionConfigurator mapperClass) + { + mapperClass.AddAttribute(typeof(GeneratedCodeAttribute), cfg => + { + cfg.SetConstructorArguments("AgileObjects.AgileMapper.Buildable", _version); + }); + } + #region Helper Members private class MapMethodInfo From 192bafb423db009d237b9d4a27cdf831d040e237 Mon Sep 17 00:00:00 2001 From: Steve Wilkes <steve@agileobjects.co.uk> Date: Tue, 15 Jun 2021 16:39:43 +0100 Subject: [PATCH 34/65] Adding generated code header to generated mapper source code files --- .../Mappers/Mapper.cs | 12 ++++++++++- .../Mappers/StringMapper.cs | 12 ++++++++++- .../BuildableMapperExtensions.cs | 21 ++++++++++++++++++- 3 files changed, 42 insertions(+), 3 deletions(-) diff --git a/AgileMapper.Buildable.UnitTests/Mappers/Mapper.cs b/AgileMapper.Buildable.UnitTests/Mappers/Mapper.cs index 40b84c07f..11e353ada 100644 --- a/AgileMapper.Buildable.UnitTests/Mappers/Mapper.cs +++ b/AgileMapper.Buildable.UnitTests/Mappers/Mapper.cs @@ -1,8 +1,18 @@ +// ------------------------------------------------------------------------------ +// <auto-generated> +// This code was generated by AgileObjects.AgileMapper.Buildable. +// Runtime Version: 0.1.0.0 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// </auto-generated> +// ------------------------------------------------------------------------------ + using System.CodeDom.Compiler; namespace AgileObjects.AgileMapper.Buildable.UnitTests.Mappers { - [GeneratedCode("AgileMapper.Buildable", "0.1.0.0")] + [GeneratedCode("AgileObjects.AgileMapper.Buildable", "0.1.0.0")] public static class Mapper { public static StringMapper Map diff --git a/AgileMapper.Buildable.UnitTests/Mappers/StringMapper.cs b/AgileMapper.Buildable.UnitTests/Mappers/StringMapper.cs index 431670003..92e74fdf4 100644 --- a/AgileMapper.Buildable.UnitTests/Mappers/StringMapper.cs +++ b/AgileMapper.Buildable.UnitTests/Mappers/StringMapper.cs @@ -1,3 +1,13 @@ +// ------------------------------------------------------------------------------ +// <auto-generated> +// This code was generated by AgileObjects.AgileMapper.Buildable. +// Runtime Version: 0.1.0.0 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// </auto-generated> +// ------------------------------------------------------------------------------ + using System; using System.CodeDom.Compiler; using AgileObjects.AgileMapper; @@ -8,7 +18,7 @@ namespace AgileObjects.AgileMapper.Buildable.UnitTests.Mappers { - [GeneratedCode("AgileMapper.Buildable", "0.1.0.0")] + [GeneratedCode("AgileObjects.AgileMapper.Buildable", "0.1.0.0")] public class StringMapper : MappingExecutionContextBase<string> { public StringMapper diff --git a/AgileMapper.Buildable/BuildableMapperExtensions.cs b/AgileMapper.Buildable/BuildableMapperExtensions.cs index 098ccc8b1..1c98351ee 100644 --- a/AgileMapper.Buildable/BuildableMapperExtensions.cs +++ b/AgileMapper.Buildable/BuildableMapperExtensions.cs @@ -17,6 +17,8 @@ internal static class BuildableMapperExtensions { + private const string _toolName = "AgileObjects.AgileMapper.Buildable"; + private static readonly string _version = typeof(BuildableMapperExtensions) .Assembly .GetName() @@ -45,6 +47,7 @@ public static IEnumerable<SourceCodeExpression> GetPlanSourceCodeInCache( { yield return BuildableExpression.SourceCode(sourceCode => { + sourceCode.AddGeneratedCodeHeader(); sourceCode.SetNamespace(mappersNamespace); mapperGroup.MapperClass = sourceCode.AddClass(mapperGroup.MapperName, mapperClass => @@ -106,6 +109,7 @@ public static IEnumerable<SourceCodeExpression> GetPlanSourceCodeInCache( yield return BuildableExpression.SourceCode(sourceCode => { + sourceCode.AddGeneratedCodeHeader(); sourceCode.SetNamespace(mappersNamespace); sourceCode.AddClass("Mapper", staticMapperClass => @@ -259,12 +263,27 @@ private static void AddUpdateInstanceMapMethod( } } + private static void AddGeneratedCodeHeader( + this ISourceCodeExpressionConfigurator sourceCode) + { + sourceCode.SetHeader(@$" +//------------------------------------------------------------------------------ +// <auto-generated> +// This code was generated by {_toolName}. +// Runtime Version: {_version} +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// </auto-generated> +//------------------------------------------------------------------------------".TrimStart()); + } + private static void AddGeneratedCodeAttribute( this IAttributableExpressionConfigurator mapperClass) { mapperClass.AddAttribute(typeof(GeneratedCodeAttribute), cfg => { - cfg.SetConstructorArguments("AgileObjects.AgileMapper.Buildable", _version); + cfg.SetConstructorArguments(_toolName, _version); }); } From f7e80788917d3669185357809b4e0924ffdcfe94 Mon Sep 17 00:00:00 2001 From: Steve Wilkes <steve@agileobjects.co.uk> Date: Tue, 15 Jun 2021 21:15:07 +0100 Subject: [PATCH 35/65] Test coverage for generated single-source, multi-target mapper --- .../Mappers/Mapper.cs | 9 ++ .../Mappers/PublicField_StringMapper.cs | 102 ++++++++++++++ ...WhenBuildingComplexTypeCreateNewMappers.cs | 125 +++++++----------- 3 files changed, 156 insertions(+), 80 deletions(-) create mode 100644 AgileMapper.Buildable.UnitTests/Mappers/PublicField_StringMapper.cs diff --git a/AgileMapper.Buildable.UnitTests/Mappers/Mapper.cs b/AgileMapper.Buildable.UnitTests/Mappers/Mapper.cs index 11e353ada..e49e9a6d7 100644 --- a/AgileMapper.Buildable.UnitTests/Mappers/Mapper.cs +++ b/AgileMapper.Buildable.UnitTests/Mappers/Mapper.cs @@ -9,12 +9,21 @@ // ------------------------------------------------------------------------------ using System.CodeDom.Compiler; +using AgileObjects.AgileMapper.UnitTests.Common.TestClasses; namespace AgileObjects.AgileMapper.Buildable.UnitTests.Mappers { [GeneratedCode("AgileObjects.AgileMapper.Buildable", "0.1.0.0")] public static class Mapper { + public static PublicField_StringMapper Map + ( + PublicField<string> source + ) + { + return new PublicField_StringMapper(source); + } + public static StringMapper Map ( string source diff --git a/AgileMapper.Buildable.UnitTests/Mappers/PublicField_StringMapper.cs b/AgileMapper.Buildable.UnitTests/Mappers/PublicField_StringMapper.cs new file mode 100644 index 000000000..8e63b87d3 --- /dev/null +++ b/AgileMapper.Buildable.UnitTests/Mappers/PublicField_StringMapper.cs @@ -0,0 +1,102 @@ +// ------------------------------------------------------------------------------ +// <auto-generated> +// This code was generated by AgileObjects.AgileMapper.Buildable. +// Runtime Version: 0.1.0.0 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// </auto-generated> +// ------------------------------------------------------------------------------ + +using System; +using System.CodeDom.Compiler; +using AgileObjects.AgileMapper; +using AgileObjects.AgileMapper.ObjectPopulation; +using AgileObjects.AgileMapper.UnitTests.Common.TestClasses; +using AgileObjects.NetStandardPolyfills; +using AgileObjects.ReadableExpressions; +using AgileObjects.ReadableExpressions.Extensions; + +namespace AgileObjects.AgileMapper.Buildable.UnitTests.Mappers +{ + [GeneratedCode("AgileObjects.AgileMapper.Buildable", "0.1.0.0")] + public class PublicField_StringMapper : MappingExecutionContextBase<PublicField<string>> + { + public PublicField_StringMapper + ( + PublicField<string> source + ) + : base(source) + { + } + + public TTarget ToANew<TTarget>() + { + if (typeof(TTarget).IsAssignableTo(typeof(PublicField<int>))) + { + return (TTarget)((object)PublicField_StringMapper.CreateNew(this.CreateRootMappingData(default(PublicField<int>)))); + } + + if (typeof(TTarget).IsAssignableTo(typeof(PublicProperty<string>))) + { + return (TTarget)((object)PublicField_StringMapper.CreateNew(this.CreateRootMappingData(default(PublicProperty<string>)))); + } + + throw new NotSupportedException( + "Unable to perform a 'CreateNew' mapping from source type 'PublicField<string>' to target type '" + typeof(TTarget).GetFriendlyName(null) + "'"); + } + + private static PublicField<int> CreateNew + ( + IObjectMappingData<PublicField<string>, PublicField<int>> pfsToPfiData + ) + { + try + { + var publicField_Int = new PublicField<int>(); + publicField_Int.Value = PublicField_StringMapper.GetInt(pfsToPfiData); + + return publicField_Int; + } + catch (Exception ex) + { + throw MappingException.For( + "CreateNew", + "PublicField<string>", + "PublicField<int>", + ex); + } + } + + private static PublicProperty<string> CreateNew + ( + IObjectMappingData<PublicField<string>, PublicProperty<string>> pfsToPpsData + ) + { + try + { + var publicProperty_String = new PublicProperty<string>(); + publicProperty_String.Value = pfsToPpsData.Source.Value; + + return publicProperty_String; + } + catch (Exception ex) + { + throw MappingException.For( + "CreateNew", + "PublicField<string>", + "PublicProperty<string>", + ex); + } + } + + private static int GetInt + ( + IObjectMappingData<PublicField<string>, PublicField<int>> pfsToPfiData + ) + { + int intValue; + return int.TryParse(pfsToPfiData.Source.Value, out intValue) ? intValue : default(int); + } + } +} \ No newline at end of file diff --git a/AgileMapper.Buildable.UnitTests/WhenBuildingComplexTypeCreateNewMappers.cs b/AgileMapper.Buildable.UnitTests/WhenBuildingComplexTypeCreateNewMappers.cs index e2450ea53..30bfe90dd 100644 --- a/AgileMapper.Buildable.UnitTests/WhenBuildingComplexTypeCreateNewMappers.cs +++ b/AgileMapper.Buildable.UnitTests/WhenBuildingComplexTypeCreateNewMappers.cs @@ -4,89 +4,54 @@ namespace AgileObjects.AgileMapper.Buildable.UnitTests using System.Reflection; using AgileMapper.UnitTests.Common; using AgileMapper.UnitTests.Common.TestClasses; + using Configuration; using Xunit; + using GeneratedMapper = Mappers.Mapper; public class WhenBuildingComplexTypeCreateNewMappers { - //[Fact] - //public void ShouldBuildSingleSourceSingleTargetMapper() - //{ - // using (var mapper = Mapper.CreateNew()) - // { - // mapper.GetPlanFor<PublicField<string>>().ToANew<PublicField<int>>(); - - // var sourceCodeExpressions = mapper.GetPlanSourceCodeInCache(); - - // var staticMapperClass = sourceCodeExpressions - // .ShouldCompileAStaticMapperClass(); - - // var staticMapMethod = staticMapperClass - // .GetMapMethods() - // .ShouldHaveSingleItem(); - - // var source = new PublicField<string> { Value = "123" }; - - // var executor = staticMapMethod - // .ShouldCreateMappingExecutor(source); - - // var result = executor - // .ShouldHaveACreateNewMethod() - // .ShouldExecuteACreateNewMapping<PublicField<int>>(); - - // result.Value.ShouldBe(123); - // } - //} - - //[Fact] - //public void ShouldBuildSingleSourceMultipleTargetMapper() - //{ - // using (var mapper = Mapper.CreateNew()) - // { - // mapper.GetPlanFor<PublicField<string>>().ToANew<PublicField<int>>(); - // mapper.GetPlanFor<PublicField<string>>().ToANew<PublicProperty<string>>(); - - // var sourceCodeExpressions = mapper.GetPlanSourceCodeInCache(); - - // var staticMapperClass = sourceCodeExpressions - // .ShouldCompileAStaticMapperClass(); - - // var staticMapMethod = staticMapperClass - // .GetMapMethods() - // .ShouldHaveSingleItem(); - - // var source = new PublicField<string> { Value = "456" }; - - // var executor = staticMapMethod - // .ShouldCreateMappingExecutor(source); - - // var createNewMethod = executor.ShouldHaveACreateNewMethod(); - - // var publicFieldResult = createNewMethod - // .ShouldExecuteACreateNewMapping<PublicField<int>>(); - - // publicFieldResult.Value.ShouldBe(456); - - // var publicPropertyResult = createNewMethod - // .ShouldExecuteACreateNewMapping<PublicProperty<string>>(); - - // publicPropertyResult.Value.ShouldBe("456"); - - // var configEx = Should.Throw<TargetInvocationException>(() => - // { - // createNewMethod - // .ShouldExecuteACreateNewMapping<PublicField<DateTime>>(); - // }); - - // var notSupportedMessage = configEx - // .InnerException - // .ShouldBeOfType<NotSupportedException>() - // .Message; - - // notSupportedMessage.ShouldContain("Unable"); - // notSupportedMessage.ShouldContain("CreateNew"); - // notSupportedMessage.ShouldContain("source type 'PublicField<string>'"); - // notSupportedMessage.ShouldContain("target type 'PublicField<DateTime>'"); - // } - //} + [Fact] + public void ShouldBuildSingleSourceSingleTargetMapper() + { + var source = new PublicField<string> { Value = "123" }; + var result = GeneratedMapper.Map(source).ToANew<PublicField<int>>(); + result.Value.ShouldBe(123); + } + + [Fact] + public void ShouldBuildSingleSourceMultipleTargetMapper() + { + var source = new PublicField<string> { Value = "456" }; + var publicFieldResult = GeneratedMapper.Map(source).ToANew<PublicField<int>>(); + publicFieldResult.Value.ShouldBe(456); + + var publicPropertyResult = GeneratedMapper.Map(source).ToANew<PublicProperty<string>>(); + publicPropertyResult.Value.ShouldBe("456"); + + var notSupportedEx = Should.Throw<NotSupportedException>(() => + { + GeneratedMapper.Map(source).ToANew<PublicField<DateTime>>(); + }); + + var notSupportedMessage = notSupportedEx.Message.ShouldNotBeNull(); + + notSupportedMessage.ShouldContain("Unable"); + notSupportedMessage.ShouldContain("CreateNew"); + notSupportedMessage.ShouldContain("source type 'PublicField<string>'"); + notSupportedMessage.ShouldContain("target type 'PublicField<DateTime>'"); + } + + #region Configuration + + public class ComplexTypeCreateNewMapperConfiguration : BuildableMapperConfiguration + { + protected override void Configure() + { + GetPlanFor<PublicField<string>>().ToANew<PublicField<int>>(); + GetPlanFor<PublicField<string>>().ToANew<PublicProperty<string>>(); + } + } + + #endregion } } \ No newline at end of file From 11216240132eb7fe64a036a0ef20ab8ad3aaa791 Mon Sep 17 00:00:00 2001 From: Steve Wilkes <steve@agileobjects.co.uk> Date: Wed, 16 Jun 2021 09:32:36 +0100 Subject: [PATCH 36/65] Differentiating single and multi-source complex type create new mapper tests --- .../Mappers/Mapper.cs | 8 +++ .../Mappers/PublicProperty_StringMapper.cs | 67 +++++++++++++++++++ ...WhenBuildingComplexTypeCreateNewMappers.cs | 5 +- 3 files changed, 78 insertions(+), 2 deletions(-) create mode 100644 AgileMapper.Buildable.UnitTests/Mappers/PublicProperty_StringMapper.cs diff --git a/AgileMapper.Buildable.UnitTests/Mappers/Mapper.cs b/AgileMapper.Buildable.UnitTests/Mappers/Mapper.cs index e49e9a6d7..48e8471d1 100644 --- a/AgileMapper.Buildable.UnitTests/Mappers/Mapper.cs +++ b/AgileMapper.Buildable.UnitTests/Mappers/Mapper.cs @@ -24,6 +24,14 @@ PublicField<string> source return new PublicField_StringMapper(source); } + public static PublicProperty_StringMapper Map + ( + PublicProperty<string> source + ) + { + return new PublicProperty_StringMapper(source); + } + public static StringMapper Map ( string source diff --git a/AgileMapper.Buildable.UnitTests/Mappers/PublicProperty_StringMapper.cs b/AgileMapper.Buildable.UnitTests/Mappers/PublicProperty_StringMapper.cs new file mode 100644 index 000000000..657ab05c3 --- /dev/null +++ b/AgileMapper.Buildable.UnitTests/Mappers/PublicProperty_StringMapper.cs @@ -0,0 +1,67 @@ +// ------------------------------------------------------------------------------ +// <auto-generated> +// This code was generated by AgileObjects.AgileMapper.Buildable. +// Runtime Version: 0.1.0.0 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// </auto-generated> +// ------------------------------------------------------------------------------ + +using System; +using System.CodeDom.Compiler; +using AgileObjects.AgileMapper; +using AgileObjects.AgileMapper.ObjectPopulation; +using AgileObjects.AgileMapper.UnitTests.Common.TestClasses; + +namespace AgileObjects.AgileMapper.Buildable.UnitTests.Mappers +{ + [GeneratedCode("AgileObjects.AgileMapper.Buildable", "0.1.0.0")] + public class PublicProperty_StringMapper : MappingExecutionContextBase<PublicProperty<string>> + { + public PublicProperty_StringMapper + ( + PublicProperty<string> source + ) + : base(source) + { + } + + public PublicField<int> ToANew<TTarget>() + where TTarget : PublicField<int> + { + return PublicProperty_StringMapper.CreateNew(this.CreateRootMappingData(default(PublicField<int>))); + } + + private static PublicField<int> CreateNew + ( + IObjectMappingData<PublicProperty<string>, PublicField<int>> ppsToPfiData + ) + { + try + { + var publicField_Int = new PublicField<int>(); + publicField_Int.Value = PublicProperty_StringMapper.GetInt(ppsToPfiData); + + return publicField_Int; + } + catch (Exception ex) + { + throw MappingException.For( + "CreateNew", + "PublicProperty<string>", + "PublicField<int>", + ex); + } + } + + private static int GetInt + ( + IObjectMappingData<PublicProperty<string>, PublicField<int>> ppsToPfiData + ) + { + int intValue; + return int.TryParse(ppsToPfiData.Source.Value, out intValue) ? intValue : default(int); + } + } +} \ No newline at end of file diff --git a/AgileMapper.Buildable.UnitTests/WhenBuildingComplexTypeCreateNewMappers.cs b/AgileMapper.Buildable.UnitTests/WhenBuildingComplexTypeCreateNewMappers.cs index 30bfe90dd..33db0a9b4 100644 --- a/AgileMapper.Buildable.UnitTests/WhenBuildingComplexTypeCreateNewMappers.cs +++ b/AgileMapper.Buildable.UnitTests/WhenBuildingComplexTypeCreateNewMappers.cs @@ -1,7 +1,6 @@ namespace AgileObjects.AgileMapper.Buildable.UnitTests { using System; - using System.Reflection; using AgileMapper.UnitTests.Common; using AgileMapper.UnitTests.Common.TestClasses; using Configuration; @@ -13,7 +12,7 @@ public class WhenBuildingComplexTypeCreateNewMappers [Fact] public void ShouldBuildSingleSourceSingleTargetMapper() { - var source = new PublicField<string> { Value = "123" }; + var source = new PublicProperty<string> { Value = "123" }; var result = GeneratedMapper.Map(source).ToANew<PublicField<int>>(); result.Value.ShouldBe(123); } @@ -47,6 +46,8 @@ public class ComplexTypeCreateNewMapperConfiguration : BuildableMapperConfigurat { protected override void Configure() { + GetPlanFor<PublicProperty<string>>().ToANew<PublicField<int>>(); + GetPlanFor<PublicField<string>>().ToANew<PublicField<int>>(); GetPlanFor<PublicField<string>>().ToANew<PublicProperty<string>>(); } From b39e84c5e3fc781b9679d7705a4cea9022b2b58f Mon Sep 17 00:00:00 2001 From: Steve Wilkes <steve@agileobjects.co.uk> Date: Wed, 16 Jun 2021 09:57:34 +0100 Subject: [PATCH 37/65] Test coverage for a generated overwrite mapper --- .../Mappers/AddressMapper.cs | 60 +++++++++++++++++++ .../Mappers/Mapper.cs | 8 +++ ...WhenBuildingComplexTypeOverwriteMappers.cs | 45 +++++++------- 3 files changed, 88 insertions(+), 25 deletions(-) create mode 100644 AgileMapper.Buildable.UnitTests/Mappers/AddressMapper.cs diff --git a/AgileMapper.Buildable.UnitTests/Mappers/AddressMapper.cs b/AgileMapper.Buildable.UnitTests/Mappers/AddressMapper.cs new file mode 100644 index 000000000..e8054d6fe --- /dev/null +++ b/AgileMapper.Buildable.UnitTests/Mappers/AddressMapper.cs @@ -0,0 +1,60 @@ +// ------------------------------------------------------------------------------ +// <auto-generated> +// This code was generated by AgileObjects.AgileMapper.Buildable. +// Runtime Version: 0.1.0.0 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// </auto-generated> +// ------------------------------------------------------------------------------ + +using System; +using System.CodeDom.Compiler; +using AgileObjects.AgileMapper; +using AgileObjects.AgileMapper.ObjectPopulation; +using AgileObjects.AgileMapper.UnitTests.Common.TestClasses; + +namespace AgileObjects.AgileMapper.Buildable.UnitTests.Mappers +{ + [GeneratedCode("AgileObjects.AgileMapper.Buildable", "0.1.0.0")] + public class AddressMapper : MappingExecutionContextBase<Address> + { + public AddressMapper + ( + Address source + ) + : base(source) + { + } + + public Address Over + ( + Address target + ) + { + return AddressMapper.Overwrite(this.CreateRootMappingData(target)); + } + + private static Address Overwrite + ( + IObjectMappingData<Address, Address> aToAData + ) + { + try + { + aToAData.Target.Line1 = aToAData.Source.Line1; + aToAData.Target.Line2 = aToAData.Source.Line2; + + return aToAData.Target; + } + catch (Exception ex) + { + throw MappingException.For( + "Overwrite", + "Address", + "Address", + ex); + } + } + } +} \ No newline at end of file diff --git a/AgileMapper.Buildable.UnitTests/Mappers/Mapper.cs b/AgileMapper.Buildable.UnitTests/Mappers/Mapper.cs index 48e8471d1..54cd20b58 100644 --- a/AgileMapper.Buildable.UnitTests/Mappers/Mapper.cs +++ b/AgileMapper.Buildable.UnitTests/Mappers/Mapper.cs @@ -16,6 +16,14 @@ namespace AgileObjects.AgileMapper.Buildable.UnitTests.Mappers [GeneratedCode("AgileObjects.AgileMapper.Buildable", "0.1.0.0")] public static class Mapper { + public static AddressMapper Map + ( + Address source + ) + { + return new AddressMapper(source); + } + public static PublicField_StringMapper Map ( PublicField<string> source diff --git a/AgileMapper.Buildable.UnitTests/WhenBuildingComplexTypeOverwriteMappers.cs b/AgileMapper.Buildable.UnitTests/WhenBuildingComplexTypeOverwriteMappers.cs index eefb19ccc..b9e408923 100644 --- a/AgileMapper.Buildable.UnitTests/WhenBuildingComplexTypeOverwriteMappers.cs +++ b/AgileMapper.Buildable.UnitTests/WhenBuildingComplexTypeOverwriteMappers.cs @@ -2,39 +2,34 @@ namespace AgileObjects.AgileMapper.Buildable.UnitTests { using AgileMapper.UnitTests.Common; using AgileMapper.UnitTests.Common.TestClasses; + using Configuration; using Xunit; + using GeneratedMapper = Mappers.Mapper; public class WhenBuildingComplexTypeOverwriteMappers { - //[Fact] - //public void ShouldBuildASingleSourceSingleTargetMapper() - //{ - // using (var mapper = Mapper.CreateNew()) - // { - // mapper.GetPlanFor<Address>().Over<Address>(); + [Fact] + public void ShouldBuildASingleSourceSingleTargetMapper() + { + var source = new Address { Line1 = "1.1", Line2 = "1.2" }; + var target = new Address { Line1 = "2.1", Line2 = "2.2" }; - // var sourceCodeExpressions = mapper.GetPlanSourceCodeInCache(); + GeneratedMapper.Map(source).Over(target); - // var staticMapperClass = sourceCodeExpressions - // .ShouldCompileAStaticMapperClass(); + target.Line1.ShouldBe("1.1"); + target.Line2.ShouldBe("1.2"); + } - // var staticMapMethod = staticMapperClass - // .GetMapMethods() - // .ShouldHaveSingleItem(); + #region Configuration - // var source = new Address { Line1 = "1.1", Line2 = "1.2" }; - // var target = new Address { Line1 = "2.1", Line2 = "2.2" }; + public class ComplexTypeOverwriteMapperConfiguration : BuildableMapperConfiguration + { + protected override void Configure() + { + GetPlanFor<Address>().Over<Address>(); + } + } - // var executor = staticMapMethod - // .ShouldCreateMappingExecutor(source); - - // executor - // .ShouldHaveAnOverwriteMethod() - // .ShouldExecuteAnOverwriteMapping(target); - - // target.Line1.ShouldBe("1.1"); - // target.Line2.ShouldBe("1.2"); - // } - //} + #endregion } } \ No newline at end of file From abe3699a2d320028f548087bb72de978b85a8bd2 Mon Sep 17 00:00:00 2001 From: Steve Wilkes <steve@agileobjects.co.uk> Date: Wed, 16 Jun 2021 09:59:59 +0100 Subject: [PATCH 38/65] Test coverage for a generated merge mapper --- .../Mappers/AddressMapper.cs | 37 +++++++++++++++ .../WhenBuildingComplexTypeMergeMappers.cs | 45 +++++++++---------- 2 files changed, 57 insertions(+), 25 deletions(-) diff --git a/AgileMapper.Buildable.UnitTests/Mappers/AddressMapper.cs b/AgileMapper.Buildable.UnitTests/Mappers/AddressMapper.cs index e8054d6fe..e25279b40 100644 --- a/AgileMapper.Buildable.UnitTests/Mappers/AddressMapper.cs +++ b/AgileMapper.Buildable.UnitTests/Mappers/AddressMapper.cs @@ -27,6 +27,14 @@ Address source { } + public Address OnTo + ( + Address target + ) + { + return AddressMapper.Merge(this.CreateRootMappingData(target)); + } + public Address Over ( Address target @@ -35,6 +43,35 @@ Address target return AddressMapper.Overwrite(this.CreateRootMappingData(target)); } + private static Address Merge + ( + IObjectMappingData<Address, Address> aToAData + ) + { + try + { + if (aToAData.Target.Line1 == null) + { + aToAData.Target.Line1 = aToAData.Source.Line1; + } + + if (aToAData.Target.Line2 == null) + { + aToAData.Target.Line2 = aToAData.Source.Line2; + } + + return aToAData.Target; + } + catch (Exception ex) + { + throw MappingException.For( + "Merge", + "Address", + "Address", + ex); + } + } + private static Address Overwrite ( IObjectMappingData<Address, Address> aToAData diff --git a/AgileMapper.Buildable.UnitTests/WhenBuildingComplexTypeMergeMappers.cs b/AgileMapper.Buildable.UnitTests/WhenBuildingComplexTypeMergeMappers.cs index 5f747487b..7d1cdb554 100644 --- a/AgileMapper.Buildable.UnitTests/WhenBuildingComplexTypeMergeMappers.cs +++ b/AgileMapper.Buildable.UnitTests/WhenBuildingComplexTypeMergeMappers.cs @@ -2,39 +2,34 @@ namespace AgileObjects.AgileMapper.Buildable.UnitTests { using AgileMapper.UnitTests.Common; using AgileObjects.AgileMapper.UnitTests.Common.TestClasses; + using Configuration; using Xunit; + using GeneratedMapper = Mappers.Mapper; public class WhenBuildingComplexTypeMergeMappers { - //[Fact] - //public void ShouldBuildASingleSourceSingleTargetMapper() - //{ - // using (var mapper = Mapper.CreateNew()) - // { - // mapper.GetPlanFor<Address>().OnTo<Address>(); + [Fact] + public void ShouldBuildASingleSourceSingleTargetMapper() + { + var source = new Address { Line1 = "Line 1!" }; + var target = new Address { Line2 = "Line 2!" }; - // var sourceCodeExpressions = mapper.GetPlanSourceCodeInCache(); + GeneratedMapper.Map(source).OnTo(target); - // var staticMapperClass = sourceCodeExpressions - // .ShouldCompileAStaticMapperClass(); + target.Line1.ShouldBe("Line 1!"); + target.Line2.ShouldBe("Line 2!"); + } - // var staticMapMethod = staticMapperClass - // .GetMapMethods() - // .ShouldHaveSingleItem(); + #region Configuration - // var source = new Address { Line1 = "Line 1!" }; - // var target = new Address { Line2 = "Line 2!" }; + public class ComplexTypeMergeMapperConfiguration : BuildableMapperConfiguration + { + protected override void Configure() + { + GetPlanFor<Address>().OnTo<Address>(); + } + } - // var executor = staticMapMethod - // .ShouldCreateMappingExecutor(source); - - // executor - // .ShouldHaveAMergeMethod() - // .ShouldExecuteAMergeMapping(target); - - // target.Line1.ShouldBe("Line 1!"); - // target.Line2.ShouldBe("Line 2!"); - // } - //} + #endregion } } From 6ec486dd6670a03a0c12ddfb33cc64d760eb2da4 Mon Sep 17 00:00:00 2001 From: Steve Wilkes <steve@agileobjects.co.uk> Date: Wed, 16 Jun 2021 11:30:05 +0100 Subject: [PATCH 39/65] Test coverage for generated enumerable create new mappers --- .../Mappers/DateTimesMapper.cs | 85 ++++++++ .../Mappers/IntArrayMapper.cs | 70 ++++++ .../Mappers/Mapper.cs | 34 +++ .../Mappers/ProductDtosMapper.cs | 129 +++++++++++ .../Mappers/StringsMapper.cs | 80 +++++++ .../WhenBuildingEnumerableCreateNewMappers.cs | 201 +++++++----------- 6 files changed, 469 insertions(+), 130 deletions(-) create mode 100644 AgileMapper.Buildable.UnitTests/Mappers/DateTimesMapper.cs create mode 100644 AgileMapper.Buildable.UnitTests/Mappers/IntArrayMapper.cs create mode 100644 AgileMapper.Buildable.UnitTests/Mappers/ProductDtosMapper.cs create mode 100644 AgileMapper.Buildable.UnitTests/Mappers/StringsMapper.cs diff --git a/AgileMapper.Buildable.UnitTests/Mappers/DateTimesMapper.cs b/AgileMapper.Buildable.UnitTests/Mappers/DateTimesMapper.cs new file mode 100644 index 000000000..db8f1924a --- /dev/null +++ b/AgileMapper.Buildable.UnitTests/Mappers/DateTimesMapper.cs @@ -0,0 +1,85 @@ +// ------------------------------------------------------------------------------ +// <auto-generated> +// This code was generated by AgileObjects.AgileMapper.Buildable. +// Runtime Version: 0.1.0.0 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// </auto-generated> +// ------------------------------------------------------------------------------ + +using System; +using System.CodeDom.Compiler; +using System.Collections.Generic; +using AgileObjects.AgileMapper; +using AgileObjects.AgileMapper.Extensions; +using AgileObjects.AgileMapper.ObjectPopulation; +using AgileObjects.ReadableExpressions; +using AgileObjects.ReadableExpressions.Extensions; + +namespace AgileObjects.AgileMapper.Buildable.UnitTests.Mappers +{ + [GeneratedCode("AgileObjects.AgileMapper.Buildable", "0.1.0.0")] + public class DateTimesMapper : MappingExecutionContextBase<HashSet<DateTime>> + { + public DateTimesMapper + ( + HashSet<DateTime> source + ) + : base(source) + { + } + + public TTarget ToANew<TTarget>() + { + if (typeof(TTarget) == typeof(DateTime[])) + { + return (TTarget)((object)DateTimesMapper.CreateNew(this.CreateRootMappingData(default(DateTime[])))); + } + + throw new NotSupportedException( + "Unable to perform a 'CreateNew' mapping from source type 'HashSet<DateTime>' to target type '" + typeof(TTarget).GetFriendlyName(null) + "'"); + } + + private static DateTime[] CreateNew + ( + IObjectMappingData<HashSet<DateTime>, DateTime[]> dtsToDtaData + ) + { + try + { + var sourceDateTimes = dtsToDtaData.Source; + var targetDateTimes = new List<DateTime>(sourceDateTimes.Count); + var i = 0; + var enumerator = sourceDateTimes.GetEnumerator(); + try + { + while (true) + { + if (!enumerator.MoveNext()) + { + break; + } + + targetDateTimes.Add(enumerator.Current); + ++i; + } + } + finally + { + enumerator.Dispose(); + } + + return targetDateTimes.ToArray(); + } + catch (Exception ex) + { + throw MappingException.For( + "CreateNew", + "HashSet<DateTime>", + "DateTime[]", + ex); + } + } + } +} \ No newline at end of file diff --git a/AgileMapper.Buildable.UnitTests/Mappers/IntArrayMapper.cs b/AgileMapper.Buildable.UnitTests/Mappers/IntArrayMapper.cs new file mode 100644 index 000000000..f638eef22 --- /dev/null +++ b/AgileMapper.Buildable.UnitTests/Mappers/IntArrayMapper.cs @@ -0,0 +1,70 @@ +// ------------------------------------------------------------------------------ +// <auto-generated> +// This code was generated by AgileObjects.AgileMapper.Buildable. +// Runtime Version: 0.1.0.0 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// </auto-generated> +// ------------------------------------------------------------------------------ + +using System; +using System.CodeDom.Compiler; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using AgileObjects.AgileMapper; +using AgileObjects.AgileMapper.ObjectPopulation; + +namespace AgileObjects.AgileMapper.Buildable.UnitTests.Mappers +{ + [GeneratedCode("AgileObjects.AgileMapper.Buildable", "0.1.0.0")] + public class IntArrayMapper : MappingExecutionContextBase<int[]> + { + public IntArrayMapper + ( + int[] source + ) + : base(source) + { + } + + public ReadOnlyCollection<int> ToANew<TTarget>() + where TTarget : ReadOnlyCollection<int> + { + return IntArrayMapper.CreateNew(this.CreateRootMappingData(default(ReadOnlyCollection<int>))); + } + + private static ReadOnlyCollection<int> CreateNew + ( + IObjectMappingData<int[], ReadOnlyCollection<int>> iaToIsData + ) + { + try + { + var sourceIntArray = iaToIsData.Source; + var targetInts = new List<int>(sourceIntArray.Length); + var i = 0; + while (true) + { + if (i == sourceIntArray.Length) + { + break; + } + + targetInts.Add(sourceIntArray[i]); + ++i; + } + + return targetInts.AsReadOnly(); + } + catch (Exception ex) + { + throw MappingException.For( + "CreateNew", + "int[]", + "ReadOnlyCollection<int>", + ex); + } + } + } +} \ No newline at end of file diff --git a/AgileMapper.Buildable.UnitTests/Mappers/Mapper.cs b/AgileMapper.Buildable.UnitTests/Mappers/Mapper.cs index 54cd20b58..eb4d7b6e8 100644 --- a/AgileMapper.Buildable.UnitTests/Mappers/Mapper.cs +++ b/AgileMapper.Buildable.UnitTests/Mappers/Mapper.cs @@ -8,7 +8,9 @@ // </auto-generated> // ------------------------------------------------------------------------------ +using System; using System.CodeDom.Compiler; +using System.Collections.Generic; using AgileObjects.AgileMapper.UnitTests.Common.TestClasses; namespace AgileObjects.AgileMapper.Buildable.UnitTests.Mappers @@ -24,6 +26,30 @@ Address source return new AddressMapper(source); } + public static DateTimesMapper Map + ( + HashSet<DateTime> source + ) + { + return new DateTimesMapper(source); + } + + public static IntArrayMapper Map + ( + int[] source + ) + { + return new IntArrayMapper(source); + } + + public static ProductDtosMapper Map + ( + List<ProductDto> source + ) + { + return new ProductDtosMapper(source); + } + public static PublicField_StringMapper Map ( PublicField<string> source @@ -47,5 +73,13 @@ string source { return new StringMapper(source); } + + public static StringsMapper Map + ( + List<string> source + ) + { + return new StringsMapper(source); + } } } \ No newline at end of file diff --git a/AgileMapper.Buildable.UnitTests/Mappers/ProductDtosMapper.cs b/AgileMapper.Buildable.UnitTests/Mappers/ProductDtosMapper.cs new file mode 100644 index 000000000..ccf1e07f4 --- /dev/null +++ b/AgileMapper.Buildable.UnitTests/Mappers/ProductDtosMapper.cs @@ -0,0 +1,129 @@ +// ------------------------------------------------------------------------------ +// <auto-generated> +// This code was generated by AgileObjects.AgileMapper.Buildable. +// Runtime Version: 0.1.0.0 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// </auto-generated> +// ------------------------------------------------------------------------------ + +using System; +using System.CodeDom.Compiler; +using System.Collections.Generic; +using AgileObjects.AgileMapper; +using AgileObjects.AgileMapper.ObjectPopulation; +using AgileObjects.AgileMapper.UnitTests.Common.TestClasses; + +namespace AgileObjects.AgileMapper.Buildable.UnitTests.Mappers +{ + [GeneratedCode("AgileObjects.AgileMapper.Buildable", "0.1.0.0")] + public class ProductDtosMapper : MappingExecutionContextBase<List<ProductDto>> + { + public ProductDtosMapper + ( + List<ProductDto> source + ) + : base(source) + { + } + + public IList<ProductDto> ToANew<TTarget>() + where TTarget : IList<ProductDto> + { + return ProductDtosMapper.CreateNew(this.CreateRootMappingData(default(IList<ProductDto>))); + } + + private static IList<ProductDto> CreateNew + ( + IObjectMappingData<List<ProductDto>, IList<ProductDto>> pdsToPdsData + ) + { + try + { + var sourceProductDtos = pdsToPdsData.Source; + var targetProductDtos = new List<ProductDto>(sourceProductDtos.Count); + var i = 0; + while (true) + { + if (i == sourceProductDtos.Count) + { + break; + } + + var sourceProductDto = sourceProductDtos[i]; + targetProductDtos.Add(ProductDtosMapper.GetProductDto(sourceProductDto)); + ++i; + } + + return targetProductDtos; + } + catch (Exception ex) + { + throw MappingException.For( + "CreateNew", + "List<ProductDto>", + "IList<ProductDto>", + ex); + } + } + + private static ProductDto GetProductDto + ( + ProductDto sourceProductDto + ) + { + try + { + if (sourceProductDto == null) + { + return null; + } + + var sourceProductDtoMega = sourceProductDto as ProductDtoMega; + + if (sourceProductDtoMega != null) + { + return ProductDtosMapper.GetProductDtoMega(sourceProductDtoMega); + } + var productDto = new ProductDto(); + productDto.ProductId = sourceProductDto.ProductId; + productDto.Price = sourceProductDto.Price; + + return productDto; + } + catch (Exception ex) + { + throw MappingException.For( + "CreateNew", + "List<ProductDto>[i]", + "IList<ProductDto>[i]", + ex); + } + } + + private static ProductDtoMega GetProductDtoMega + ( + ProductDtoMega sourceProductDtoMega + ) + { + try + { + var productDtoMega = new ProductDtoMega(); + productDtoMega.HowMega = sourceProductDtoMega.HowMega; + productDtoMega.ProductId = sourceProductDtoMega.ProductId; + productDtoMega.Price = sourceProductDtoMega.Price; + + return productDtoMega; + } + catch (Exception ex) + { + throw MappingException.For( + "CreateNew", + "List<ProductDto>[i]", + "IList<ProductDto>[i]", + ex); + } + } + } +} \ No newline at end of file diff --git a/AgileMapper.Buildable.UnitTests/Mappers/StringsMapper.cs b/AgileMapper.Buildable.UnitTests/Mappers/StringsMapper.cs new file mode 100644 index 000000000..d18d21330 --- /dev/null +++ b/AgileMapper.Buildable.UnitTests/Mappers/StringsMapper.cs @@ -0,0 +1,80 @@ +// ------------------------------------------------------------------------------ +// <auto-generated> +// This code was generated by AgileObjects.AgileMapper.Buildable. +// Runtime Version: 0.1.0.0 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// </auto-generated> +// ------------------------------------------------------------------------------ + +using System; +using System.CodeDom.Compiler; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using AgileObjects.AgileMapper; +using AgileObjects.AgileMapper.ObjectPopulation; + +namespace AgileObjects.AgileMapper.Buildable.UnitTests.Mappers +{ + [GeneratedCode("AgileObjects.AgileMapper.Buildable", "0.1.0.0")] + public class StringsMapper : MappingExecutionContextBase<List<string>> + { + public StringsMapper + ( + List<string> source + ) + : base(source) + { + } + + public Collection<byte?> ToANew<TTarget>() + where TTarget : Collection<Nullable<byte>> + { + return StringsMapper.CreateNew(this.CreateRootMappingData(default(Collection<byte?>))); + } + + private static Collection<byte?> CreateNew + ( + IObjectMappingData<List<string>, Collection<byte?>> ssToNbsData + ) + { + try + { + var strings = ssToNbsData.Source; + var nullableBytes = new Collection<byte?>(); + var i = 0; + while (true) + { + if (i == strings.Count) + { + break; + } + + nullableBytes.Add(StringsMapper.GetNullableByte(strings, i)); + ++i; + } + + return nullableBytes; + } + catch (Exception ex) + { + throw MappingException.For( + "CreateNew", + "List<string>", + "Collection<byte?>", + ex); + } + } + + private static byte? GetNullableByte + ( + List<string> strings, + int i + ) + { + byte byteValue; + return byte.TryParse(strings[i], out byteValue) ? (byte?)byteValue : null; + } + } +} \ No newline at end of file diff --git a/AgileMapper.Buildable.UnitTests/WhenBuildingEnumerableCreateNewMappers.cs b/AgileMapper.Buildable.UnitTests/WhenBuildingEnumerableCreateNewMappers.cs index 0a7801471..a44561f09 100644 --- a/AgileMapper.Buildable.UnitTests/WhenBuildingEnumerableCreateNewMappers.cs +++ b/AgileMapper.Buildable.UnitTests/WhenBuildingEnumerableCreateNewMappers.cs @@ -6,139 +6,80 @@ using System.Linq; using AgileMapper.UnitTests.Common; using AgileMapper.UnitTests.Common.TestClasses; + using Configuration; using Xunit; + using GeneratedMapper = Mappers.Mapper; public class WhenBuildingEnumerableCreateNewMappers { - //[Fact] - //public void ShouldBuildASimpleTypeListToCollectionMapper() - //{ - // using (var mapper = Mapper.CreateNew()) - // { - // mapper.GetPlanFor<List<string>>().ToANew<Collection<byte?>>(); - - // var sourceCodeExpressions = mapper.GetPlanSourceCodeInCache(); - - // var staticMapperClass = sourceCodeExpressions - // .ShouldCompileAStaticMapperClass(); - - // var staticMapMethod = staticMapperClass - // .GetMapMethods() - // .ShouldHaveSingleItem(); - - // var source = new List<string> { "3", "2", "1", "12345" }; - - // var executor = staticMapMethod - // .ShouldCreateMappingExecutor(source); - - // var result = executor - // .ShouldHaveACreateNewMethod() - // .ShouldExecuteACreateNewMapping<Collection<byte?>>(); - - // result.ShouldNotBeNull(); - // result.ShouldBe<byte?>(3, 2, 1, null); - // } - //} - - //[Fact] - //public void ShouldBuildASimpleTypeArrayToReadOnlyCollectionMapper() - //{ - // using (var mapper = Mapper.CreateNew()) - // { - // mapper.GetPlanFor<int[]>().ToANew<ReadOnlyCollection<int>>(); - - // var sourceCodeExpressions = mapper.GetPlanSourceCodeInCache(); - - // var staticMapperClass = sourceCodeExpressions - // .ShouldCompileAStaticMapperClass(); - - // var staticMapMethod = staticMapperClass - // .GetMapMethods() - // .ShouldHaveSingleItem(); - - // var source = new[] { 1, 2, 3 }; - - // var executor = staticMapMethod - // .ShouldCreateMappingExecutor(source); - - // var result = executor - // .ShouldHaveACreateNewMethod() - // .ShouldExecuteACreateNewMapping<ReadOnlyCollection<int>>(); - - // result.ShouldNotBeNull(); - // result.ShouldBe(1, 2, 3); - // } - //} - - //[Fact] - //public void ShouldBuildASimpleTypeHashSetToArrayMapper() - //{ - // using (var mapper = Mapper.CreateNew()) - // { - // mapper.GetPlanFor<HashSet<DateTime>>().ToANew<DateTime[]>(); - - // var sourceCodeExpressions = mapper.GetPlanSourceCodeInCache(); - - // var staticMapperClass = sourceCodeExpressions - // .ShouldCompileAStaticMapperClass(); - - // var staticMapMethod = staticMapperClass - // .GetMapMethods() - // .ShouldHaveSingleItem(); - - // var today = DateTime.Today; - // var tomorrow = today.AddDays(+1); - // var yesterday = today.AddDays(-1); - - // var source = new HashSet<DateTime> { yesterday, today, tomorrow }; - - // var executor = staticMapMethod - // .ShouldCreateMappingExecutor(source); - - // var result = executor - // .ShouldHaveACreateNewMethod() - // .ShouldExecuteACreateNewMapping<DateTime[]>(); - - // result.ShouldNotBeNull().ShouldBe(yesterday, today, tomorrow); - // } - //} - - //[Fact] - //public void ShouldBuildAComplexTypeListToIListMapper() - //{ - // using (var mapper = Mapper.CreateNew()) - // { - // mapper.GetPlanFor<List<ProductDto>>().ToANew<IList<ProductDto>>(); - - // var sourceCodeExpressions = mapper.GetPlanSourceCodeInCache(); - - // var staticMapperClass = sourceCodeExpressions - // .ShouldCompileAStaticMapperClass(); - - // var staticMapMethod = staticMapperClass - // .GetMapMethods() - // .ShouldHaveSingleItem(); - - // var source = new List<ProductDto> - // { - // new ProductDto { ProductId = "Surprise" }, - // null, - // new ProductDto { ProductId = "Boomstick" } - // }; - - // var executor = staticMapMethod - // .ShouldCreateMappingExecutor(source); - - // var result = executor - // .ShouldHaveACreateNewMethod() - // .ShouldExecuteACreateNewMapping<List<ProductDto>>(); - - // result.ShouldNotBeNull(); - // result.ShouldNotBeSameAs(source); - // result.First().ShouldNotBeNull().ProductId.ShouldBe("Surprise"); - // result.Second().ShouldBeNull(); - // result.Third().ShouldNotBeNull().ProductId.ShouldBe("Boomstick"); - // } - //} + [Fact] + public void ShouldBuildASimpleTypeListToCollectionMapper() + { + var source = new List<string> { "3", "2", "1", "12345" }; + var result = GeneratedMapper.Map(source).ToANew<Collection<byte?>>(); + + result.ShouldNotBeNull(); + result.ShouldBe<byte?>(3, 2, 1, null); + } + + [Fact] + public void ShouldBuildASimpleTypeArrayToReadOnlyCollectionMapper() + { + var source = new[] { 1, 2, 3 }; + var result = GeneratedMapper.Map(source).ToANew<ReadOnlyCollection<int>>(); + + result.ShouldNotBeNull(); + result.ShouldBe(1, 2, 3); + } + + [Fact] + public void ShouldBuildASimpleTypeHashSetToArrayMapper() + { + var today = DateTime.Today; + var tomorrow = today.AddDays(+1); + var yesterday = today.AddDays(-1); + + var source = new HashSet<DateTime> { yesterday, today, tomorrow }; + var result = GeneratedMapper.Map(source).ToANew<DateTime[]>(); + + result.ShouldNotBeNull().ShouldBe(yesterday, today, tomorrow); + } + + [Fact] + public void ShouldBuildAComplexTypeListToIListMapper() + { + var source = new List<ProductDto> + { + new ProductDto { ProductId = "Surprise" }, + null, + new ProductDto { ProductId = "Boomstick" } + }; + + var result = GeneratedMapper.Map(source).ToANew<List<ProductDto>>(); + + result.ShouldNotBeNull(); + result.ShouldNotBeSameAs(source); + result.First().ShouldNotBeNull().ProductId.ShouldBe("Surprise"); + result.Second().ShouldBeNull(); + result.Third().ShouldNotBeNull().ProductId.ShouldBe("Boomstick"); + } + + #region Configuration + + public class EnumerableCreateNewMapperConfiguration : BuildableMapperConfiguration + { + protected override void Configure() + { + GetPlanFor<List<string>>().ToANew<Collection<byte?>>(); + + GetPlanFor<int[]>().ToANew<ReadOnlyCollection<int>>(); + + GetPlanFor<HashSet<DateTime>>().ToANew<DateTime[]>(); + + GetPlanFor<List<ProductDto>>().ToANew<IList<ProductDto>>(); + } + } + + #endregion } } From 7345fa8a05439582438b1d3791918b81c4d6828b Mon Sep 17 00:00:00 2001 From: Steve Wilkes <steve@agileobjects.co.uk> Date: Wed, 16 Jun 2021 21:13:59 +0100 Subject: [PATCH 40/65] Test coverage for generated-mapper enumerable overwrite mapping --- .../Mappers/CharArrayMapper.cs | 67 +++++ ...imesMapper.cs => DateTimeHashSetMapper.cs} | 6 +- .../Mappers/Mapper.cs | 51 +++- .../Mappers/ProductDtoArrayMapper.cs | 236 ++++++++++++++++++ ...tDtosMapper.cs => ProductDtoListMapper.cs} | 10 +- .../Mappers/StringCollectionMapper.cs | 63 +++++ .../{StringsMapper.cs => StringListMapper.cs} | 8 +- ...ngMapper.cs => StringPublicFieldMapper.cs} | 10 +- ...apper.cs => StringPublicPropertyMapper.cs} | 8 +- .../WhenBuildingEnumerableOverwriteMappers.cs | 151 +++++------ AgileMapper.Buildable/BuildableMapperGroup.cs | 37 ++- 11 files changed, 526 insertions(+), 121 deletions(-) create mode 100644 AgileMapper.Buildable.UnitTests/Mappers/CharArrayMapper.cs rename AgileMapper.Buildable.UnitTests/Mappers/{DateTimesMapper.cs => DateTimeHashSetMapper.cs} (90%) create mode 100644 AgileMapper.Buildable.UnitTests/Mappers/ProductDtoArrayMapper.cs rename AgileMapper.Buildable.UnitTests/Mappers/{ProductDtosMapper.cs => ProductDtoListMapper.cs} (89%) create mode 100644 AgileMapper.Buildable.UnitTests/Mappers/StringCollectionMapper.cs rename AgileMapper.Buildable.UnitTests/Mappers/{StringsMapper.cs => StringListMapper.cs} (87%) rename AgileMapper.Buildable.UnitTests/Mappers/{PublicField_StringMapper.cs => StringPublicFieldMapper.cs} (85%) rename AgileMapper.Buildable.UnitTests/Mappers/{PublicProperty_StringMapper.cs => StringPublicPropertyMapper.cs} (84%) diff --git a/AgileMapper.Buildable.UnitTests/Mappers/CharArrayMapper.cs b/AgileMapper.Buildable.UnitTests/Mappers/CharArrayMapper.cs new file mode 100644 index 000000000..f7d8cd8e4 --- /dev/null +++ b/AgileMapper.Buildable.UnitTests/Mappers/CharArrayMapper.cs @@ -0,0 +1,67 @@ +// ------------------------------------------------------------------------------ +// <auto-generated> +// This code was generated by AgileObjects.AgileMapper.Buildable. +// Runtime Version: 0.1.0.0 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// </auto-generated> +// ------------------------------------------------------------------------------ + +using System; +using System.CodeDom.Compiler; +using System.Linq; +using AgileObjects.AgileMapper; +using AgileObjects.AgileMapper.Extensions; +using AgileObjects.AgileMapper.ObjectPopulation; + +namespace AgileObjects.AgileMapper.Buildable.UnitTests.Mappers +{ + [GeneratedCode("AgileObjects.AgileMapper.Buildable", "0.1.0.0")] + public class CharArrayMapper : MappingExecutionContextBase<char[]> + { + public CharArrayMapper + ( + char[] source + ) + : base(source) + { + } + + public int[] Over + ( + int[] target + ) + { + return CharArrayMapper.Overwrite(this.CreateRootMappingData(target)); + } + + private static int[] Overwrite + ( + IObjectMappingData<char[], int[]> caToIaData + ) + { + try + { + return caToIaData.Source.Project(c => CharArrayMapper.GetInt(c)).ToArray(); + } + catch (Exception ex) + { + throw MappingException.For( + "Overwrite", + "char[]", + "int[]", + ex); + } + } + + private static int GetInt + ( + char c + ) + { + int intValue; + return int.TryParse(c.ToString(), out intValue) ? intValue : default(int); + } + } +} \ No newline at end of file diff --git a/AgileMapper.Buildable.UnitTests/Mappers/DateTimesMapper.cs b/AgileMapper.Buildable.UnitTests/Mappers/DateTimeHashSetMapper.cs similarity index 90% rename from AgileMapper.Buildable.UnitTests/Mappers/DateTimesMapper.cs rename to AgileMapper.Buildable.UnitTests/Mappers/DateTimeHashSetMapper.cs index db8f1924a..689ffe84a 100644 --- a/AgileMapper.Buildable.UnitTests/Mappers/DateTimesMapper.cs +++ b/AgileMapper.Buildable.UnitTests/Mappers/DateTimeHashSetMapper.cs @@ -20,9 +20,9 @@ namespace AgileObjects.AgileMapper.Buildable.UnitTests.Mappers { [GeneratedCode("AgileObjects.AgileMapper.Buildable", "0.1.0.0")] - public class DateTimesMapper : MappingExecutionContextBase<HashSet<DateTime>> + public class DateTimeHashSetMapper : MappingExecutionContextBase<HashSet<DateTime>> { - public DateTimesMapper + public DateTimeHashSetMapper ( HashSet<DateTime> source ) @@ -34,7 +34,7 @@ public TTarget ToANew<TTarget>() { if (typeof(TTarget) == typeof(DateTime[])) { - return (TTarget)((object)DateTimesMapper.CreateNew(this.CreateRootMappingData(default(DateTime[])))); + return (TTarget)((object)DateTimeHashSetMapper.CreateNew(this.CreateRootMappingData(default(DateTime[])))); } throw new NotSupportedException( diff --git a/AgileMapper.Buildable.UnitTests/Mappers/Mapper.cs b/AgileMapper.Buildable.UnitTests/Mappers/Mapper.cs index eb4d7b6e8..d0456f3e1 100644 --- a/AgileMapper.Buildable.UnitTests/Mappers/Mapper.cs +++ b/AgileMapper.Buildable.UnitTests/Mappers/Mapper.cs @@ -11,6 +11,7 @@ using System; using System.CodeDom.Compiler; using System.Collections.Generic; +using System.Collections.ObjectModel; using AgileObjects.AgileMapper.UnitTests.Common.TestClasses; namespace AgileObjects.AgileMapper.Buildable.UnitTests.Mappers @@ -26,12 +27,20 @@ Address source return new AddressMapper(source); } - public static DateTimesMapper Map + public static CharArrayMapper Map + ( + char[] source + ) + { + return new CharArrayMapper(source); + } + + public static DateTimeHashSetMapper Map ( HashSet<DateTime> source ) { - return new DateTimesMapper(source); + return new DateTimeHashSetMapper(source); } public static IntArrayMapper Map @@ -42,28 +51,36 @@ int[] source return new IntArrayMapper(source); } - public static ProductDtosMapper Map + public static ProductDtoArrayMapper Map + ( + ProductDto[] source + ) + { + return new ProductDtoArrayMapper(source); + } + + public static ProductDtoListMapper Map ( List<ProductDto> source ) { - return new ProductDtosMapper(source); + return new ProductDtoListMapper(source); } - public static PublicField_StringMapper Map + public static StringCollectionMapper Map ( - PublicField<string> source + Collection<string> source ) { - return new PublicField_StringMapper(source); + return new StringCollectionMapper(source); } - public static PublicProperty_StringMapper Map + public static StringListMapper Map ( - PublicProperty<string> source + List<string> source ) { - return new PublicProperty_StringMapper(source); + return new StringListMapper(source); } public static StringMapper Map @@ -74,12 +91,20 @@ string source return new StringMapper(source); } - public static StringsMapper Map + public static StringPublicFieldMapper Map ( - List<string> source + PublicField<string> source + ) + { + return new StringPublicFieldMapper(source); + } + + public static StringPublicPropertyMapper Map + ( + PublicProperty<string> source ) { - return new StringsMapper(source); + return new StringPublicPropertyMapper(source); } } } \ No newline at end of file diff --git a/AgileMapper.Buildable.UnitTests/Mappers/ProductDtoArrayMapper.cs b/AgileMapper.Buildable.UnitTests/Mappers/ProductDtoArrayMapper.cs new file mode 100644 index 000000000..34c433e4c --- /dev/null +++ b/AgileMapper.Buildable.UnitTests/Mappers/ProductDtoArrayMapper.cs @@ -0,0 +1,236 @@ +// ------------------------------------------------------------------------------ +// <auto-generated> +// This code was generated by AgileObjects.AgileMapper.Buildable. +// Runtime Version: 0.1.0.0 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// </auto-generated> +// ------------------------------------------------------------------------------ + +using System; +using System.CodeDom.Compiler; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using AgileObjects.AgileMapper; +using AgileObjects.AgileMapper.Extensions; +using AgileObjects.AgileMapper.ObjectPopulation; +using AgileObjects.AgileMapper.UnitTests.Common.TestClasses; + +namespace AgileObjects.AgileMapper.Buildable.UnitTests.Mappers +{ + [GeneratedCode("AgileObjects.AgileMapper.Buildable", "0.1.0.0")] + public class ProductDtoArrayMapper : MappingExecutionContextBase<ProductDto[]> + { + public ProductDtoArrayMapper + ( + ProductDto[] source + ) + : base(source) + { + } + + public ReadOnlyCollection<Product> Over + ( + ReadOnlyCollection<Product> target + ) + { + return ProductDtoArrayMapper.Overwrite(this.CreateRootMappingData(target)); + } + + private static ReadOnlyCollection<Product> Overwrite + ( + IObjectMappingData<ProductDto[], ReadOnlyCollection<Product>> pdaToPsData + ) + { + try + { + var collectionData = CollectionData.Create( + pdaToPsData.Source, + pdaToPsData.Target, + pd => pd.ProductId, + p => p.ProductId); + collectionData.Intersection.ForEach( + (existingProductDto, existingProduct, idx) => ProductDtoArrayMapper.GetProduct1(existingProductDto, existingProduct)); + var productDtos = collectionData.NewSourceItems; + var products = new List<Product>(pdaToPsData.Target); + collectionData.AbsentTargetItems.ForEach(p => products.Remove(p)); + var i = 0; + var enumerator = productDtos.GetEnumerator(); + try + { + while (true) + { + if (!enumerator.MoveNext()) + { + break; + } + + products.Add(ProductDtoArrayMapper.GetProduct1(enumerator)); + ++i; + } + } + finally + { + enumerator.Dispose(); + } + + return products.AsReadOnly(); + } + catch (Exception ex) + { + throw MappingException.For( + "Overwrite", + "ProductDto[]", + "ReadOnlyCollection<Product>", + ex); + } + } + + private static Product GetProduct1 + ( + ProductDto existingProductDto, + Product existingProduct + ) + { + try + { + if (existingProductDto == null) + { + return null; + } + + var sourceProductDtoMega = existingProductDto as ProductDtoMega; + + if (sourceProductDtoMega != null) + { + return ProductDtoArrayMapper.GetProduct2(existingProduct, sourceProductDtoMega); + } + + if (existingProduct is MegaProduct) + { + return ProductDtoArrayMapper.GetMegaProduct(existingProduct, existingProductDto); + } + var product = existingProduct ?? new Product(); + product.ProductId = existingProductDto.ProductId; + product.Price = (double)existingProductDto.Price; + + return product; + } + catch (Exception ex) + { + throw MappingException.For( + "Overwrite", + "ProductDto[][i]", + "ReadOnlyCollection<Product>[i]", + ex); + } + } + + private static Product GetProduct2 + ( + Product existingProduct, + ProductDtoMega sourceProductDtoMega + ) + { + try + { + var product = existingProduct ?? new Product(); + product.ProductId = sourceProductDtoMega.ProductId; + product.Price = (double)sourceProductDtoMega.Price; + + return product; + } + catch (Exception ex) + { + throw MappingException.For( + "Overwrite", + "ProductDto[][i]", + "ReadOnlyCollection<Product>[i]", + ex); + } + } + + private static MegaProduct GetMegaProduct + ( + Product existingProduct, + ProductDto existingProductDto + ) + { + try + { + var megaProduct = ((MegaProduct)existingProduct) ?? new MegaProduct(); + // No data sources for HowMega + megaProduct.ProductId = existingProductDto.ProductId; + megaProduct.Price = (double)existingProductDto.Price; + + return megaProduct; + } + catch (Exception ex) + { + throw MappingException.For( + "Overwrite", + "ProductDto[][i]", + "ReadOnlyCollection<Product>[i]", + ex); + } + } + + private static Product GetProduct1 + ( + IEnumerator<ProductDto> enumerator + ) + { + try + { + if (enumerator.Current == null) + { + return null; + } + + var sourceProductDtoMega = enumerator.Current as ProductDtoMega; + + if (sourceProductDtoMega != null) + { + return ProductDtoArrayMapper.GetProduct2(sourceProductDtoMega); + } + var product = new Product(); + product.ProductId = enumerator.Current.ProductId; + product.Price = (double)enumerator.Current.Price; + + return product; + } + catch (Exception ex) + { + throw MappingException.For( + "Overwrite", + "ProductDto[][i]", + "ReadOnlyCollection<Product>[i]", + ex); + } + } + + private static Product GetProduct2 + ( + ProductDtoMega sourceProductDtoMega + ) + { + try + { + var product = new Product(); + product.ProductId = sourceProductDtoMega.ProductId; + product.Price = (double)sourceProductDtoMega.Price; + + return product; + } + catch (Exception ex) + { + throw MappingException.For( + "Overwrite", + "ProductDto[][i]", + "ReadOnlyCollection<Product>[i]", + ex); + } + } + } +} \ No newline at end of file diff --git a/AgileMapper.Buildable.UnitTests/Mappers/ProductDtosMapper.cs b/AgileMapper.Buildable.UnitTests/Mappers/ProductDtoListMapper.cs similarity index 89% rename from AgileMapper.Buildable.UnitTests/Mappers/ProductDtosMapper.cs rename to AgileMapper.Buildable.UnitTests/Mappers/ProductDtoListMapper.cs index ccf1e07f4..4af58c3d9 100644 --- a/AgileMapper.Buildable.UnitTests/Mappers/ProductDtosMapper.cs +++ b/AgileMapper.Buildable.UnitTests/Mappers/ProductDtoListMapper.cs @@ -18,9 +18,9 @@ namespace AgileObjects.AgileMapper.Buildable.UnitTests.Mappers { [GeneratedCode("AgileObjects.AgileMapper.Buildable", "0.1.0.0")] - public class ProductDtosMapper : MappingExecutionContextBase<List<ProductDto>> + public class ProductDtoListMapper : MappingExecutionContextBase<List<ProductDto>> { - public ProductDtosMapper + public ProductDtoListMapper ( List<ProductDto> source ) @@ -31,7 +31,7 @@ List<ProductDto> source public IList<ProductDto> ToANew<TTarget>() where TTarget : IList<ProductDto> { - return ProductDtosMapper.CreateNew(this.CreateRootMappingData(default(IList<ProductDto>))); + return ProductDtoListMapper.CreateNew(this.CreateRootMappingData(default(IList<ProductDto>))); } private static IList<ProductDto> CreateNew @@ -52,7 +52,7 @@ IObjectMappingData<List<ProductDto>, IList<ProductDto>> pdsToPdsData } var sourceProductDto = sourceProductDtos[i]; - targetProductDtos.Add(ProductDtosMapper.GetProductDto(sourceProductDto)); + targetProductDtos.Add(ProductDtoListMapper.GetProductDto(sourceProductDto)); ++i; } @@ -84,7 +84,7 @@ ProductDto sourceProductDto if (sourceProductDtoMega != null) { - return ProductDtosMapper.GetProductDtoMega(sourceProductDtoMega); + return ProductDtoListMapper.GetProductDtoMega(sourceProductDtoMega); } var productDto = new ProductDto(); productDto.ProductId = sourceProductDto.ProductId; diff --git a/AgileMapper.Buildable.UnitTests/Mappers/StringCollectionMapper.cs b/AgileMapper.Buildable.UnitTests/Mappers/StringCollectionMapper.cs new file mode 100644 index 000000000..069a540c2 --- /dev/null +++ b/AgileMapper.Buildable.UnitTests/Mappers/StringCollectionMapper.cs @@ -0,0 +1,63 @@ +// ------------------------------------------------------------------------------ +// <auto-generated> +// This code was generated by AgileObjects.AgileMapper.Buildable. +// Runtime Version: 0.1.0.0 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// </auto-generated> +// ------------------------------------------------------------------------------ + +using System; +using System.CodeDom.Compiler; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using AgileObjects.AgileMapper; +using AgileObjects.AgileMapper.ObjectPopulation; + +namespace AgileObjects.AgileMapper.Buildable.UnitTests.Mappers +{ + [GeneratedCode("AgileObjects.AgileMapper.Buildable", "0.1.0.0")] + public class StringCollectionMapper : MappingExecutionContextBase<Collection<string>> + { + public StringCollectionMapper + ( + Collection<string> source + ) + : base(source) + { + } + + public List<string> Over + ( + List<string> target + ) + { + return StringCollectionMapper.Overwrite(this.CreateRootMappingData(target)); + } + + private static List<string> Overwrite + ( + IObjectMappingData<Collection<string>, List<string>> ssToSsData + ) + { + try + { + var sourceStrings = ssToSsData.Source; + var targetStrings = ssToSsData.Target; + targetStrings.Clear(); + targetStrings.AddRange(sourceStrings); + + return targetStrings; + } + catch (Exception ex) + { + throw MappingException.For( + "Overwrite", + "Collection<string>", + "List<string>", + ex); + } + } + } +} \ No newline at end of file diff --git a/AgileMapper.Buildable.UnitTests/Mappers/StringsMapper.cs b/AgileMapper.Buildable.UnitTests/Mappers/StringListMapper.cs similarity index 87% rename from AgileMapper.Buildable.UnitTests/Mappers/StringsMapper.cs rename to AgileMapper.Buildable.UnitTests/Mappers/StringListMapper.cs index d18d21330..e153c615b 100644 --- a/AgileMapper.Buildable.UnitTests/Mappers/StringsMapper.cs +++ b/AgileMapper.Buildable.UnitTests/Mappers/StringListMapper.cs @@ -18,9 +18,9 @@ namespace AgileObjects.AgileMapper.Buildable.UnitTests.Mappers { [GeneratedCode("AgileObjects.AgileMapper.Buildable", "0.1.0.0")] - public class StringsMapper : MappingExecutionContextBase<List<string>> + public class StringListMapper : MappingExecutionContextBase<List<string>> { - public StringsMapper + public StringListMapper ( List<string> source ) @@ -31,7 +31,7 @@ List<string> source public Collection<byte?> ToANew<TTarget>() where TTarget : Collection<Nullable<byte>> { - return StringsMapper.CreateNew(this.CreateRootMappingData(default(Collection<byte?>))); + return StringListMapper.CreateNew(this.CreateRootMappingData(default(Collection<byte?>))); } private static Collection<byte?> CreateNew @@ -51,7 +51,7 @@ private static Collection<byte?> CreateNew break; } - nullableBytes.Add(StringsMapper.GetNullableByte(strings, i)); + nullableBytes.Add(StringListMapper.GetNullableByte(strings, i)); ++i; } diff --git a/AgileMapper.Buildable.UnitTests/Mappers/PublicField_StringMapper.cs b/AgileMapper.Buildable.UnitTests/Mappers/StringPublicFieldMapper.cs similarity index 85% rename from AgileMapper.Buildable.UnitTests/Mappers/PublicField_StringMapper.cs rename to AgileMapper.Buildable.UnitTests/Mappers/StringPublicFieldMapper.cs index 8e63b87d3..568882308 100644 --- a/AgileMapper.Buildable.UnitTests/Mappers/PublicField_StringMapper.cs +++ b/AgileMapper.Buildable.UnitTests/Mappers/StringPublicFieldMapper.cs @@ -20,9 +20,9 @@ namespace AgileObjects.AgileMapper.Buildable.UnitTests.Mappers { [GeneratedCode("AgileObjects.AgileMapper.Buildable", "0.1.0.0")] - public class PublicField_StringMapper : MappingExecutionContextBase<PublicField<string>> + public class StringPublicFieldMapper : MappingExecutionContextBase<PublicField<string>> { - public PublicField_StringMapper + public StringPublicFieldMapper ( PublicField<string> source ) @@ -34,12 +34,12 @@ public TTarget ToANew<TTarget>() { if (typeof(TTarget).IsAssignableTo(typeof(PublicField<int>))) { - return (TTarget)((object)PublicField_StringMapper.CreateNew(this.CreateRootMappingData(default(PublicField<int>)))); + return (TTarget)((object)StringPublicFieldMapper.CreateNew(this.CreateRootMappingData(default(PublicField<int>)))); } if (typeof(TTarget).IsAssignableTo(typeof(PublicProperty<string>))) { - return (TTarget)((object)PublicField_StringMapper.CreateNew(this.CreateRootMappingData(default(PublicProperty<string>)))); + return (TTarget)((object)StringPublicFieldMapper.CreateNew(this.CreateRootMappingData(default(PublicProperty<string>)))); } throw new NotSupportedException( @@ -54,7 +54,7 @@ IObjectMappingData<PublicField<string>, PublicField<int>> pfsToPfiData try { var publicField_Int = new PublicField<int>(); - publicField_Int.Value = PublicField_StringMapper.GetInt(pfsToPfiData); + publicField_Int.Value = StringPublicFieldMapper.GetInt(pfsToPfiData); return publicField_Int; } diff --git a/AgileMapper.Buildable.UnitTests/Mappers/PublicProperty_StringMapper.cs b/AgileMapper.Buildable.UnitTests/Mappers/StringPublicPropertyMapper.cs similarity index 84% rename from AgileMapper.Buildable.UnitTests/Mappers/PublicProperty_StringMapper.cs rename to AgileMapper.Buildable.UnitTests/Mappers/StringPublicPropertyMapper.cs index 657ab05c3..9b5cfcd5b 100644 --- a/AgileMapper.Buildable.UnitTests/Mappers/PublicProperty_StringMapper.cs +++ b/AgileMapper.Buildable.UnitTests/Mappers/StringPublicPropertyMapper.cs @@ -17,9 +17,9 @@ namespace AgileObjects.AgileMapper.Buildable.UnitTests.Mappers { [GeneratedCode("AgileObjects.AgileMapper.Buildable", "0.1.0.0")] - public class PublicProperty_StringMapper : MappingExecutionContextBase<PublicProperty<string>> + public class StringPublicPropertyMapper : MappingExecutionContextBase<PublicProperty<string>> { - public PublicProperty_StringMapper + public StringPublicPropertyMapper ( PublicProperty<string> source ) @@ -30,7 +30,7 @@ PublicProperty<string> source public PublicField<int> ToANew<TTarget>() where TTarget : PublicField<int> { - return PublicProperty_StringMapper.CreateNew(this.CreateRootMappingData(default(PublicField<int>))); + return StringPublicPropertyMapper.CreateNew(this.CreateRootMappingData(default(PublicField<int>))); } private static PublicField<int> CreateNew @@ -41,7 +41,7 @@ IObjectMappingData<PublicProperty<string>, PublicField<int>> ppsToPfiData try { var publicField_Int = new PublicField<int>(); - publicField_Int.Value = PublicProperty_StringMapper.GetInt(ppsToPfiData); + publicField_Int.Value = StringPublicPropertyMapper.GetInt(ppsToPfiData); return publicField_Int; } diff --git a/AgileMapper.Buildable.UnitTests/WhenBuildingEnumerableOverwriteMappers.cs b/AgileMapper.Buildable.UnitTests/WhenBuildingEnumerableOverwriteMappers.cs index 1a40daed1..d05fdf350 100644 --- a/AgileMapper.Buildable.UnitTests/WhenBuildingEnumerableOverwriteMappers.cs +++ b/AgileMapper.Buildable.UnitTests/WhenBuildingEnumerableOverwriteMappers.cs @@ -5,95 +5,74 @@ using System.Linq; using AgileMapper.UnitTests.Common; using AgileMapper.UnitTests.Common.TestClasses; + using Configuration; using Xunit; + using GeneratedMapper = Mappers.Mapper; public class WhenBuildingEnumerableOverwriteMappers { - //[Fact] - //public void ShouldBuildASimpleTypeArrayToArrayMapper() - //{ - // using (var mapper = Mapper.CreateNew()) - // { - // mapper.GetPlanFor<char[]>().Over<int[]>(); - - // var sourceCodeExpressions = mapper.GetPlanSourceCodeInCache(); - - // var source = new[] { '5', '5', '5' }; - // var target = new[] { 3, 3, 3, 3 }; - - // var result = sourceCodeExpressions - // .ShouldCompileAStaticMapperClass() - // .GetMapMethods() - // .ShouldHaveSingleItem() - // .ShouldCreateMappingExecutor(source) - // .ShouldHaveAnOverwriteMethod() - // .ShouldExecuteAnOverwriteMapping(target); - - // result.ShouldNotBeNull().ShouldNotBeSameAs(target); - // result.ShouldBe(5, 5, 5); - // } - //} - - //[Fact] - //public void ShouldBuildASimpleTypeCollectionToListMapper() - //{ - // using (var mapper = Mapper.CreateNew()) - // { - // mapper.GetPlanFor<Collection<string>>().Over<List<string>>(); - - // var sourceCodeExpressions = mapper.GetPlanSourceCodeInCache(); - - // var source = new Collection<string> { "I", "Will" }; - // var target = new List<string> { "You", "Might" }; - - // var result = sourceCodeExpressions - // .ShouldCompileAStaticMapperClass() - // .GetMapMethods() - // .ShouldHaveSingleItem() - // .ShouldCreateMappingExecutor(source) - // .ShouldHaveAnOverwriteMethod() - // .ShouldExecuteAnOverwriteMapping(target); - - // result.ShouldNotBeNull().ShouldBeSameAs(target); - // result.SequenceEqual(source).ShouldBeTrue(); - // } - //} - - //[Fact] - //public void ShouldBuildAComplexTypeArrayToReadOnlyCollectionMapper() - //{ - // using (var mapper = Mapper.CreateNew()) - // { - // mapper.GetPlanFor<ProductDto[]>().Over<ReadOnlyCollection<Product>>(); - - // var sourceCodeExpressions = mapper.GetPlanSourceCodeInCache(); - - // var source = new[] - // { - // new ProductDto { ProductId = "1", Price = 1.99m }, - // new ProductDto { ProductId = "2", Price = 2.99m }, - // }; - - // var target = new ReadOnlyCollection<Product>(new List<Product> - // { - // new Product { ProductId = "2", Price = 4.99 } - // }); - - // var result = sourceCodeExpressions - // .ShouldCompileAStaticMapperClass() - // .GetMapMethods() - // .ShouldHaveSingleItem() - // .ShouldCreateMappingExecutor(source) - // .ShouldHaveAnOverwriteMethod() - // .ShouldExecuteAnOverwriteMapping(target); - - // result.ShouldNotBeNull(); - // result.Count.ShouldBe(2); - // result.First().ProductId.ShouldBe("2"); - // result.First().Price.ShouldBe(2.99); - // result.Second().ProductId.ShouldBe("1"); - // result.Second().Price.ShouldBe(1.99); - // } - //} + [Fact] + public void ShouldBuildASimpleTypeArrayToArrayMapper() + { + var source = new[] { '5', '5', '5' }; + var target = new[] { 3, 3, 3, 3 }; + + var result = GeneratedMapper.Map(source).Over(target); + + result.ShouldNotBeNull().ShouldNotBeSameAs(target); + result.ShouldBe(5, 5, 5); + } + + [Fact] + public void ShouldBuildASimpleTypeCollectionToListMapper() + { + var source = new Collection<string> { "I", "Will" }; + var target = new List<string> { "You", "Might" }; + + var result = GeneratedMapper.Map(source).Over(target); + + result.ShouldNotBeNull().ShouldBeSameAs(target); + result.SequenceEqual(source).ShouldBeTrue(); + } + + [Fact] + public void ShouldBuildAComplexTypeArrayToReadOnlyCollectionMapper() + { + var source = new[] + { + new ProductDto { ProductId = "1", Price = 1.99m }, + new ProductDto { ProductId = "2", Price = 2.99m }, + }; + + var target = new ReadOnlyCollection<Product>(new List<Product> + { + new Product { ProductId = "2", Price = 4.99 } + }); + + var result = GeneratedMapper.Map(source).Over(target); + + result.ShouldNotBeNull(); + result.Count.ShouldBe(2); + result.First().ProductId.ShouldBe("2"); + result.First().Price.ShouldBe(2.99); + result.Second().ProductId.ShouldBe("1"); + result.Second().Price.ShouldBe(1.99); + } + + #region Configuration + + public class EnumerableOverwriteMapperConfiguration : BuildableMapperConfiguration + { + protected override void Configure() + { + GetPlanFor<char[]>().Over<int[]>(); + + GetPlanFor<Collection<string>>().Over<List<string>>(); + + GetPlanFor<ProductDto[]>().Over<ReadOnlyCollection<Product>>(); + } + } + + #endregion } } diff --git a/AgileMapper.Buildable/BuildableMapperGroup.cs b/AgileMapper.Buildable/BuildableMapperGroup.cs index b6c4191f5..be7f7c276 100644 --- a/AgileMapper.Buildable/BuildableMapperGroup.cs +++ b/AgileMapper.Buildable/BuildableMapperGroup.cs @@ -22,10 +22,45 @@ public BuildableMapperGroup( MapperBaseType = typeof(MappingExecutionContextBase<>).MakeGenericType(sourceType); MapperBaseTypeConstructor = MapperBaseType.GetNonPublicInstanceConstructor(sourceType); CreateRootMappingDataMethod = MapperBaseType.GetNonPublicInstanceMethod("CreateRootMappingData"); - MapperName = sourceType.GetVariableNameInPascalCase() + "Mapper"; + MapperName = GetMapperNamePrefix(sourceType) + "Mapper"; MappingMethodsByPlan = plans.ToDictionary(p => p, p => new List<MethodExpression>()); } + #region Setup + + private static string GetMapperNamePrefix(Type sourceType) + { + if (sourceType.IsArray) + { + return sourceType.GetVariableNameInPascalCase(); + } + + if (sourceType == typeof(string)) + { + return nameof(String); + } + + var elementType = sourceType.GetEnumerableElementType(); + + if (elementType == null) + { + return sourceType.GetVariableNameInPascalCase(); + } + + var elementTypeName = elementType.GetVariableNameInPascalCase(); + var collectionTypeName = sourceType.Name; + + if (sourceType.IsGenericType()) + { + collectionTypeName = collectionTypeName + .Substring(0, collectionTypeName.IndexOf('`')); + } + + return elementTypeName + collectionTypeName; + } + + #endregion + public Type SourceType { get; } public Type MapperBaseType { get; } From 746b39d98ca82d3bbbd513bc574b3aa9b36d2d70 Mon Sep 17 00:00:00 2001 From: Steve Wilkes <steve@agileobjects.co.uk> Date: Wed, 16 Jun 2021 22:21:49 +0100 Subject: [PATCH 41/65] Test coverage for generated-mapper merge mappings --- .../Mappers/DecimalArrayMapper.cs | 80 +++++ .../Mappers/IntIEnumerableMapper.cs | 80 +++++ .../Mappers/Mapper.cs | 24 ++ .../Mappers/ProductArrayMapper.cs | 291 ++++++++++++++++++ .../WhenBuildingEnumerableMergeMappers.cs | 109 +++---- 5 files changed, 519 insertions(+), 65 deletions(-) create mode 100644 AgileMapper.Buildable.UnitTests/Mappers/DecimalArrayMapper.cs create mode 100644 AgileMapper.Buildable.UnitTests/Mappers/IntIEnumerableMapper.cs create mode 100644 AgileMapper.Buildable.UnitTests/Mappers/ProductArrayMapper.cs diff --git a/AgileMapper.Buildable.UnitTests/Mappers/DecimalArrayMapper.cs b/AgileMapper.Buildable.UnitTests/Mappers/DecimalArrayMapper.cs new file mode 100644 index 000000000..84a632564 --- /dev/null +++ b/AgileMapper.Buildable.UnitTests/Mappers/DecimalArrayMapper.cs @@ -0,0 +1,80 @@ +// ------------------------------------------------------------------------------ +// <auto-generated> +// This code was generated by AgileObjects.AgileMapper.Buildable. +// Runtime Version: 0.1.0.0 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// </auto-generated> +// ------------------------------------------------------------------------------ + +using System; +using System.CodeDom.Compiler; +using System.Collections.Generic; +using AgileObjects.AgileMapper; +using AgileObjects.AgileMapper.Extensions; +using AgileObjects.AgileMapper.ObjectPopulation; + +namespace AgileObjects.AgileMapper.Buildable.UnitTests.Mappers +{ + [GeneratedCode("AgileObjects.AgileMapper.Buildable", "0.1.0.0")] + public class DecimalArrayMapper : MappingExecutionContextBase<decimal[]> + { + public DecimalArrayMapper + ( + decimal[] source + ) + : base(source) + { + } + + public HashSet<double> OnTo + ( + HashSet<double> target + ) + { + return DecimalArrayMapper.Merge(this.CreateRootMappingData(target)); + } + + private static HashSet<double> Merge + ( + IObjectMappingData<decimal[], HashSet<double>> daToDsData + ) + { + try + { + var sourceDoubles = daToDsData.Source.Project(d => (double)d).Exclude(daToDsData.Target); + var targetDoubles = daToDsData.Target; + var i = 0; + var enumerator = sourceDoubles.GetEnumerator(); + try + { + while (true) + { + if (!enumerator.MoveNext()) + { + break; + } + + targetDoubles.Add(enumerator.Current); + ++i; + } + } + finally + { + enumerator.Dispose(); + } + + return targetDoubles; + } + catch (Exception ex) + { + throw MappingException.For( + "Merge", + "decimal[]", + "HashSet<double>", + ex); + } + } + } +} \ No newline at end of file diff --git a/AgileMapper.Buildable.UnitTests/Mappers/IntIEnumerableMapper.cs b/AgileMapper.Buildable.UnitTests/Mappers/IntIEnumerableMapper.cs new file mode 100644 index 000000000..cbdd6b3e2 --- /dev/null +++ b/AgileMapper.Buildable.UnitTests/Mappers/IntIEnumerableMapper.cs @@ -0,0 +1,80 @@ +// ------------------------------------------------------------------------------ +// <auto-generated> +// This code was generated by AgileObjects.AgileMapper.Buildable. +// Runtime Version: 0.1.0.0 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// </auto-generated> +// ------------------------------------------------------------------------------ + +using System; +using System.CodeDom.Compiler; +using System.Collections.Generic; +using AgileObjects.AgileMapper; +using AgileObjects.AgileMapper.Extensions; +using AgileObjects.AgileMapper.ObjectPopulation; + +namespace AgileObjects.AgileMapper.Buildable.UnitTests.Mappers +{ + [GeneratedCode("AgileObjects.AgileMapper.Buildable", "0.1.0.0")] + public class IntIEnumerableMapper : MappingExecutionContextBase<IEnumerable<int>> + { + public IntIEnumerableMapper + ( + IEnumerable<int> source + ) + : base(source) + { + } + + public ICollection<int> OnTo + ( + ICollection<int> target + ) + { + return IntIEnumerableMapper.Merge(this.CreateRootMappingData(target)); + } + + private static ICollection<int> Merge + ( + IObjectMappingData<IEnumerable<int>, ICollection<int>> isToIsData + ) + { + try + { + var sourceInts = isToIsData.Source.Exclude(isToIsData.Target); + ICollection<int> targetInts = isToIsData.Target.IsReadOnly ? new List<int>(isToIsData.Target) : isToIsData.Target; + var i = 0; + var enumerator = sourceInts.GetEnumerator(); + try + { + while (true) + { + if (!enumerator.MoveNext()) + { + break; + } + + targetInts.Add(enumerator.Current); + ++i; + } + } + finally + { + enumerator.Dispose(); + } + + return targetInts; + } + catch (Exception ex) + { + throw MappingException.For( + "Merge", + "IEnumerable<int>", + "ICollection<int>", + ex); + } + } + } +} \ No newline at end of file diff --git a/AgileMapper.Buildable.UnitTests/Mappers/Mapper.cs b/AgileMapper.Buildable.UnitTests/Mappers/Mapper.cs index d0456f3e1..37f4fedc7 100644 --- a/AgileMapper.Buildable.UnitTests/Mappers/Mapper.cs +++ b/AgileMapper.Buildable.UnitTests/Mappers/Mapper.cs @@ -43,6 +43,14 @@ HashSet<DateTime> source return new DateTimeHashSetMapper(source); } + public static DecimalArrayMapper Map + ( + decimal[] source + ) + { + return new DecimalArrayMapper(source); + } + public static IntArrayMapper Map ( int[] source @@ -51,6 +59,22 @@ int[] source return new IntArrayMapper(source); } + public static IntIEnumerableMapper Map + ( + IEnumerable<int> source + ) + { + return new IntIEnumerableMapper(source); + } + + public static ProductArrayMapper Map + ( + Product[] source + ) + { + return new ProductArrayMapper(source); + } + public static ProductDtoArrayMapper Map ( ProductDto[] source diff --git a/AgileMapper.Buildable.UnitTests/Mappers/ProductArrayMapper.cs b/AgileMapper.Buildable.UnitTests/Mappers/ProductArrayMapper.cs new file mode 100644 index 000000000..5b2d9a0cc --- /dev/null +++ b/AgileMapper.Buildable.UnitTests/Mappers/ProductArrayMapper.cs @@ -0,0 +1,291 @@ +// ------------------------------------------------------------------------------ +// <auto-generated> +// This code was generated by AgileObjects.AgileMapper.Buildable. +// Runtime Version: 0.1.0.0 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// </auto-generated> +// ------------------------------------------------------------------------------ + +using System; +using System.CodeDom.Compiler; +using System.Collections.Generic; +using AgileObjects.AgileMapper; +using AgileObjects.AgileMapper.Extensions; +using AgileObjects.AgileMapper.ObjectPopulation; +using AgileObjects.AgileMapper.UnitTests.Common.TestClasses; + +namespace AgileObjects.AgileMapper.Buildable.UnitTests.Mappers +{ + [GeneratedCode("AgileObjects.AgileMapper.Buildable", "0.1.0.0")] + public class ProductArrayMapper : MappingExecutionContextBase<Product[]> + { + public ProductArrayMapper + ( + Product[] source + ) + : base(source) + { + } + + public IEnumerable<Product> OnTo + ( + IEnumerable<Product> target + ) + { + return ProductArrayMapper.Merge(this.CreateRootMappingData(target)); + } + + private static IEnumerable<Product> Merge + ( + IObjectMappingData<Product[], IEnumerable<Product>> paToPsData + ) + { + try + { + var collectionData = CollectionData.Create(paToPsData.Source, paToPsData.Target, p => p.ProductId); + collectionData.Intersection.ForEach( + (existingSourceProduct, existingTargetProduct, idx) => ProductArrayMapper.GetProduct1(existingSourceProduct, existingTargetProduct)); + var sourceProducts = collectionData.NewSourceItems; + var targetProducts = ProductArrayMapper.GetProductICollection(paToPsData); + var i = 0; + var enumerator = sourceProducts.GetEnumerator(); + try + { + while (true) + { + if (!enumerator.MoveNext()) + { + break; + } + + targetProducts.Add(ProductArrayMapper.GetProduct2(enumerator)); + ++i; + } + } + finally + { + enumerator.Dispose(); + } + + return targetProducts; + } + catch (Exception ex) + { + throw MappingException.For( + "Merge", + "Product[]", + "IEnumerable<Product>", + ex); + } + } + + private static Product GetProduct1 + ( + Product existingSourceProduct, + Product existingTargetProduct + ) + { + try + { + if (existingSourceProduct == null) + { + return null; + } + + var sourceMegaProduct = existingSourceProduct as MegaProduct; + + if ((sourceMegaProduct != null) && ((existingTargetProduct == null) || (existingTargetProduct is MegaProduct))) + { + return ProductArrayMapper.GetMegaProduct1(existingTargetProduct, sourceMegaProduct); + } + + if (existingTargetProduct is MegaProduct) + { + return ProductArrayMapper.GetMegaProduct2(existingTargetProduct, existingSourceProduct); + } + var product = existingTargetProduct ?? new Product(); + + if (product.ProductId == null) + { + product.ProductId = existingSourceProduct.ProductId; + } + + if (product.Price == default(double)) + { + product.Price = existingSourceProduct.Price; + } + + return product; + } + catch (Exception ex) + { + throw MappingException.For( + "Merge", + "Product[][i]", + "IEnumerable<Product>[i]", + ex); + } + } + + private static MegaProduct GetMegaProduct1 + ( + Product existingTargetProduct, + MegaProduct sourceMegaProduct + ) + { + try + { + var megaProduct = ((MegaProduct)existingTargetProduct) ?? new MegaProduct(); + + if (megaProduct.HowMega == default(decimal)) + { + megaProduct.HowMega = sourceMegaProduct.HowMega; + } + + if (megaProduct.ProductId == null) + { + megaProduct.ProductId = sourceMegaProduct.ProductId; + } + + if (megaProduct.Price == default(double)) + { + megaProduct.Price = sourceMegaProduct.Price; + } + + return megaProduct; + } + catch (Exception ex) + { + throw MappingException.For( + "Merge", + "Product[][i]", + "IEnumerable<Product>[i]", + ex); + } + } + + private static MegaProduct GetMegaProduct2 + ( + Product existingTargetProduct, + Product existingSourceProduct + ) + { + try + { + var megaProduct = ((MegaProduct)existingTargetProduct) ?? new MegaProduct(); + // No data sources for HowMega + + if (megaProduct.ProductId == null) + { + megaProduct.ProductId = existingSourceProduct.ProductId; + } + + if (megaProduct.Price == default(double)) + { + megaProduct.Price = existingSourceProduct.Price; + } + + return megaProduct; + } + catch (Exception ex) + { + throw MappingException.For( + "Merge", + "Product[][i]", + "IEnumerable<Product>[i]", + ex); + } + } + + private static ICollection<Product> GetProductICollection + ( + IObjectMappingData<Product[], IEnumerable<Product>> paToPsData + ) + { + ICollection<Product> collection; + return ((collection = paToPsData.Target as ICollection<Product>) != null) + ? collection.IsReadOnly ? new List<Product>(paToPsData.Target) : collection + : new List<Product>(paToPsData.Target); + } + + private static Product GetProduct2 + ( + IEnumerator<Product> enumerator + ) + { + try + { + if (enumerator.Current == null) + { + return null; + } + + var sourceMegaProduct = enumerator.Current as MegaProduct; + + if (sourceMegaProduct != null) + { + return ProductArrayMapper.GetMegaProduct(sourceMegaProduct); + } + var product = new Product(); + + if (product.ProductId == null) + { + product.ProductId = enumerator.Current.ProductId; + } + + if (product.Price == default(double)) + { + product.Price = enumerator.Current.Price; + } + + return product; + } + catch (Exception ex) + { + throw MappingException.For( + "Merge", + "Product[][i]", + "IEnumerable<Product>[i]", + ex); + } + } + + private static MegaProduct GetMegaProduct + ( + MegaProduct sourceMegaProduct + ) + { + try + { + var megaProduct = new MegaProduct(); + + if (megaProduct.HowMega == default(decimal)) + { + megaProduct.HowMega = sourceMegaProduct.HowMega; + } + + if (megaProduct.ProductId == null) + { + megaProduct.ProductId = sourceMegaProduct.ProductId; + } + + if (megaProduct.Price == default(double)) + { + megaProduct.Price = sourceMegaProduct.Price; + } + + return megaProduct; + } + catch (Exception ex) + { + throw MappingException.For( + "Merge", + "Product[][i]", + "IEnumerable<Product>[i]", + ex); + } + } + } +} \ No newline at end of file diff --git a/AgileMapper.Buildable.UnitTests/WhenBuildingEnumerableMergeMappers.cs b/AgileMapper.Buildable.UnitTests/WhenBuildingEnumerableMergeMappers.cs index c7dfbf375..073a4dd58 100644 --- a/AgileMapper.Buildable.UnitTests/WhenBuildingEnumerableMergeMappers.cs +++ b/AgileMapper.Buildable.UnitTests/WhenBuildingEnumerableMergeMappers.cs @@ -4,88 +4,67 @@ using System.Collections.ObjectModel; using AgileMapper.UnitTests.Common; using AgileMapper.UnitTests.Common.TestClasses; + using Configuration; using Xunit; + using GeneratedMapper = Mappers.Mapper; public class WhenBuildingEnumerableMergeMappers { - //[Fact] - //public void ShouldBuildASimpleTypeIEnumerableToICollectionMapper() - //{ - // using (var mapper = Mapper.CreateNew()) - // { - // mapper.GetPlanFor<IEnumerable<int>>().OnTo<ICollection<int>>(); + [Fact] + public void ShouldBuildASimpleTypeIEnumerableToICollectionMapper() + { + IEnumerable<int> source = new[] { 4, 5, 6 }; + ICollection<int> target = new[] { 1, 2, 3 }; - // var sourceCodeExpressions = mapper.GetPlanSourceCodeInCache(); + var result = GeneratedMapper.Map(source).OnTo(target); - // IEnumerable<int> source = new[] { 4, 5, 6 }; - // ICollection<int> target = new[] { 1, 2, 3 }; + result.ShouldNotBeNull().ShouldNotBeSameAs(target); + result.ShouldBe(1, 2, 3, 4, 5, 6); + } - // var result = sourceCodeExpressions - // .ShouldCompileAStaticMapperClass() - // .GetMapMethods() - // .ShouldHaveSingleItem() - // .ShouldCreateMappingExecutor(source) - // .ShouldHaveAMergeMethod() - // .ShouldExecuteAMergeMapping(target); + [Fact] + public void ShouldBuildASimpleTypeArrayToHashSetMapper() + { + var source = new[] { 1.0m, 2.0m, 3.0m }; + var target = new HashSet<double> { 2.0, 3.0, 4.0 }; - // result.ShouldNotBeNull().ShouldNotBeSameAs(target); - // result.ShouldBe(1, 2, 3, 4, 5, 6); - // } - //} + var result = GeneratedMapper.Map(source).OnTo(target); - //[Fact] - //public void ShouldBuildASimpleTypeArrayToHashSetMapper() - //{ - // using (var mapper = Mapper.CreateNew()) - // { - // mapper.GetPlanFor<decimal[]>().OnTo<HashSet<double>>(); + result.ShouldNotBeNull().ShouldBe(2.0, 3.0, 4.0, 1.0); + } - // var sourceCodeExpressions = mapper.GetPlanSourceCodeInCache(); + [Fact] + public void ShouldBuildAComplexTypeArrayToIEnumerableMapper() + { + var source = new[] + { + new Product { ProductId = "Steve" } + }; - // var source = new[] { 1.0m, 2.0m, 3.0m }; - // var target = new HashSet<double> { 2.0, 3.0, 4.0 }; + IEnumerable<Product> target = new ReadOnlyCollection<Product>(new[] + { + new Product { ProductId = "Kate" } + }); - // var result = sourceCodeExpressions - // .ShouldCompileAStaticMapperClass() - // .GetMapMethods() - // .ShouldHaveSingleItem() - // .ShouldCreateMappingExecutor(source) - // .ShouldHaveAMergeMethod() - // .ShouldExecuteAMergeMapping(target); + var result = GeneratedMapper.Map(source).OnTo(target); - // result.ShouldNotBeNull().ShouldBe(2.0, 3.0, 4.0, 1.0); - // } - //} + result.ShouldNotBeNull().ShouldBe(p => p.ProductId, "Kate", "Steve"); + } - //[Fact] - //public void ShouldBuildAComplexTypeArrayToIEnumerableMapper() - //{ - // using (var mapper = Mapper.CreateNew()) - // { - // mapper.GetPlanFor<Product[]>().OnTo<IEnumerable<Product>>(); + #region Configuration - // var sourceCodeExpressions = mapper.GetPlanSourceCodeInCache(); + public class EnumerableMergeMapperConfiguration : BuildableMapperConfiguration + { + protected override void Configure() + { + GetPlanFor<IEnumerable<int>>().OnTo<ICollection<int>>(); - // var source = new[] - // { - // new Product { ProductId = "Steve" } - // }; + GetPlanFor<decimal[]>().OnTo<HashSet<double>>(); - // IEnumerable<Product> target = new ReadOnlyCollection<Product>(new[] - // { - // new Product { ProductId = "Kate" } - // }); + GetPlanFor<Product[]>().OnTo<IEnumerable<Product>>(); + } + } - // var result = sourceCodeExpressions - // .ShouldCompileAStaticMapperClass() - // .GetMapMethods() - // .ShouldHaveSingleItem() - // .ShouldCreateMappingExecutor(source) - // .ShouldHaveAMergeMethod() - // .ShouldExecuteAMergeMapping(target); - - // result.ShouldNotBeNull().ShouldBe(p => p.ProductId, "Kate", "Steve"); - // } - //} + #endregion } } From d19f2ba0286cd8e2e561d0f06252c241b51621db Mon Sep 17 00:00:00 2001 From: Steve Wilkes <steve@agileobjects.co.uk> Date: Wed, 16 Jun 2021 22:27:49 +0100 Subject: [PATCH 42/65] Test coverage for generated derived type mapper --- .../Mappers/Mapper.cs | 8 ++ .../Mappers/ProductMapper.cs | 98 ++++++++++++++++ .../WhenBuildingDerivedTypeMappers.cs | 106 ++++++++---------- 3 files changed, 155 insertions(+), 57 deletions(-) create mode 100644 AgileMapper.Buildable.UnitTests/Mappers/ProductMapper.cs diff --git a/AgileMapper.Buildable.UnitTests/Mappers/Mapper.cs b/AgileMapper.Buildable.UnitTests/Mappers/Mapper.cs index 37f4fedc7..b5106a8f3 100644 --- a/AgileMapper.Buildable.UnitTests/Mappers/Mapper.cs +++ b/AgileMapper.Buildable.UnitTests/Mappers/Mapper.cs @@ -91,6 +91,14 @@ List<ProductDto> source return new ProductDtoListMapper(source); } + public static ProductMapper Map + ( + Product source + ) + { + return new ProductMapper(source); + } + public static StringCollectionMapper Map ( Collection<string> source diff --git a/AgileMapper.Buildable.UnitTests/Mappers/ProductMapper.cs b/AgileMapper.Buildable.UnitTests/Mappers/ProductMapper.cs new file mode 100644 index 000000000..06ebb2d30 --- /dev/null +++ b/AgileMapper.Buildable.UnitTests/Mappers/ProductMapper.cs @@ -0,0 +1,98 @@ +// ------------------------------------------------------------------------------ +// <auto-generated> +// This code was generated by AgileObjects.AgileMapper.Buildable. +// Runtime Version: 0.1.0.0 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// </auto-generated> +// ------------------------------------------------------------------------------ + +using System; +using System.CodeDom.Compiler; +using AgileObjects.AgileMapper; +using AgileObjects.AgileMapper.ObjectPopulation; +using AgileObjects.AgileMapper.UnitTests.Common.TestClasses; + +namespace AgileObjects.AgileMapper.Buildable.UnitTests.Mappers +{ + [GeneratedCode("AgileObjects.AgileMapper.Buildable", "0.1.0.0")] + public class ProductMapper : MappingExecutionContextBase<Product> + { + public ProductMapper + ( + Product source + ) + : base(source) + { + } + + public ProductDto ToANew<TTarget>() + where TTarget : ProductDto + { + return ProductMapper.CreateNew(this.CreateRootMappingData(default(ProductDto))); + } + + private static ProductDto CreateNew + ( + IObjectMappingData<Product, ProductDto> pToPdData + ) + { + Product sourceProduct; + try + { + sourceProduct = pToPdData.Source; + + var sourceMegaProduct = sourceProduct as MegaProduct; + + if (sourceMegaProduct != null) + { + return ProductMapper.GetProductDtoMega(sourceMegaProduct); + } + var productDto = new ProductDto(); + productDto.ProductId = sourceProduct.ProductId; + productDto.Price = ((sourceProduct.Price >= ((double)-79228162514264337593543950335m)) && + (sourceProduct.Price <= ((double)79228162514264337593543950335m))) + ? (decimal)sourceProduct.Price + : default(decimal); + + return productDto; + } + catch (Exception ex) + { + throw MappingException.For( + "CreateNew", + "Product", + "ProductDto", + ex); + } + } + + private static ProductDtoMega GetProductDtoMega + ( + MegaProduct sourceMegaProduct + ) + { + try + { + var productDtoMega = new ProductDtoMega(); + productDtoMega.HowMega = sourceMegaProduct.HowMega.ToString(); + productDtoMega.ProductId = sourceMegaProduct.ProductId; + productDtoMega.Price = ((sourceMegaProduct.Price >= ((double)-79228162514264337593543950335m)) && + (sourceMegaProduct.Price <= ((double)79228162514264337593543950335m))) + ? (decimal)sourceMegaProduct.Price + : default(decimal); + + return productDtoMega; + } + catch (Exception ex) + { + throw MappingException.For( + "CreateNew", + "MegaProduct", + "ProductDtoMega", + ex); + } + } + } +} \ No newline at end of file diff --git a/AgileMapper.Buildable.UnitTests/WhenBuildingDerivedTypeMappers.cs b/AgileMapper.Buildable.UnitTests/WhenBuildingDerivedTypeMappers.cs index 4a65d620b..15f53fd29 100644 --- a/AgileMapper.Buildable.UnitTests/WhenBuildingDerivedTypeMappers.cs +++ b/AgileMapper.Buildable.UnitTests/WhenBuildingDerivedTypeMappers.cs @@ -2,66 +2,58 @@ { using AgileMapper.UnitTests.Common; using AgileMapper.UnitTests.Common.TestClasses; + using Configuration; using Xunit; + using GeneratedMapper = Mappers.Mapper; public class WhenBuildingDerivedTypeMappers { - //[Fact] - //public void ShouldBuildADerivedTypeCreateNewMapper() - //{ - // using (var mapper = Mapper.CreateNew()) - // { - // mapper.GetPlanFor<Product>().ToANew<ProductDto>(cfg => - // cfg.Map<MegaProduct>().To<ProductDtoMega>()); - - // var sourceCodeExpressions = mapper.GetPlanSourceCodeInCache(); - - // var staticMapperClass = sourceCodeExpressions - // .ShouldCompileAStaticMapperClass(); - - // var staticMapMethod = staticMapperClass - // .GetMapMethods() - // .ShouldHaveSingleItem(); - - // var baseTypeSource = new Product { ProductId = "111", Price = 19.99 }; - - // var baseTypeExecutor = staticMapMethod - // .ShouldCreateMappingExecutor(baseTypeSource); - - // var baseTypeResult = baseTypeExecutor - // .ShouldHaveACreateNewMethod() - // .ShouldExecuteACreateNewMapping<ProductDto>(); - - // baseTypeResult.ProductId.ShouldBe("111"); - // baseTypeResult.Price.ShouldBe(19.99m); - - // var derivedTypeSource = new MegaProduct - // { - // ProductId = "222", - // Price = 119.99, - // HowMega = 1.0m - // }; - - // var derivedTypeExecutor = staticMapMethod - // .ShouldCreateMappingExecutor<Product>(derivedTypeSource); - - // var derivedTypeBaseTypeResult = derivedTypeExecutor - // .ShouldHaveACreateNewMethod() - // .ShouldExecuteACreateNewMapping<ProductDto>() - // .ShouldBeOfType<ProductDtoMega>(); - - // derivedTypeBaseTypeResult.ProductId.ShouldBe("222"); - // derivedTypeBaseTypeResult.Price.ShouldBe(119.99m); - // derivedTypeBaseTypeResult.HowMega.ShouldBe("1.0"); - - // var derivedTypeDerivedTypeResult = derivedTypeExecutor - // .ShouldHaveACreateNewMethod() - // .ShouldExecuteACreateNewMapping<ProductDtoMega>(); - - // derivedTypeDerivedTypeResult.ProductId.ShouldBe("222"); - // derivedTypeDerivedTypeResult.Price.ShouldBe(119.99m); - // derivedTypeDerivedTypeResult.HowMega.ShouldBe("1.0"); - // } - //} + [Fact] + public void ShouldBuildADerivedTypeCreateNewMapper() + { + var baseTypeSource = new Product { ProductId = "111", Price = 19.99 }; + var baseTypeResult = GeneratedMapper.Map(baseTypeSource).ToANew<ProductDto>(); + + baseTypeResult.ProductId.ShouldBe("111"); + baseTypeResult.Price.ShouldBe(19.99m); + + var derivedTypeSource = new MegaProduct + { + ProductId = "222", + Price = 119.99, + HowMega = 1.0m + }; + + var derivedTypeToBaseTypeResult = GeneratedMapper + .Map(derivedTypeSource) + .ToANew<ProductDto>() + .ShouldBeOfType<ProductDtoMega>(); + + derivedTypeToBaseTypeResult.ProductId.ShouldBe("222"); + derivedTypeToBaseTypeResult.Price.ShouldBe(119.99m); + derivedTypeToBaseTypeResult.HowMega.ShouldBe("1.0"); + + var derivedTypeToDerivedTypeResult = GeneratedMapper + .Map(derivedTypeSource) + .ToANew<ProductDtoMega>() + .ShouldBeOfType<ProductDtoMega>(); + + derivedTypeToDerivedTypeResult.ProductId.ShouldBe("222"); + derivedTypeToDerivedTypeResult.Price.ShouldBe(119.99m); + derivedTypeToDerivedTypeResult.HowMega.ShouldBe("1.0"); + } + + #region Configuration + + public class EnumerableOverwriteMapperConfiguration : BuildableMapperConfiguration + { + protected override void Configure() + { + GetPlanFor<Product>().ToANew<ProductDto>(cfg => cfg + .Map<MegaProduct>().To<ProductDtoMega>()); + } + } + + #endregion } } From b0c6959e318530260c8281bc2684ebb3b5a82ef0 Mon Sep 17 00:00:00 2001 From: Steve Wilkes <steve@agileobjects.co.uk> Date: Wed, 16 Jun 2021 22:33:23 +0100 Subject: [PATCH 43/65] Test coverage for generated recursive mapper --- .../Mappers/ChildMapper.cs | 170 ++++++++++++++++++ .../Mappers/Mapper.cs | 8 + .../WhenBuildingCircularReferenceMappers.cs | 109 ++++++----- .../WhenBuildingDerivedTypeMappers.cs | 2 +- 4 files changed, 231 insertions(+), 58 deletions(-) create mode 100644 AgileMapper.Buildable.UnitTests/Mappers/ChildMapper.cs diff --git a/AgileMapper.Buildable.UnitTests/Mappers/ChildMapper.cs b/AgileMapper.Buildable.UnitTests/Mappers/ChildMapper.cs new file mode 100644 index 000000000..a004986c5 --- /dev/null +++ b/AgileMapper.Buildable.UnitTests/Mappers/ChildMapper.cs @@ -0,0 +1,170 @@ +// ------------------------------------------------------------------------------ +// <auto-generated> +// This code was generated by AgileObjects.AgileMapper.Buildable. +// Runtime Version: 0.1.0.0 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// </auto-generated> +// ------------------------------------------------------------------------------ + +using System; +using System.CodeDom.Compiler; +using AgileObjects.AgileMapper; +using AgileObjects.AgileMapper.ObjectPopulation; +using AgileObjects.AgileMapper.UnitTests.Common.TestClasses; + +namespace AgileObjects.AgileMapper.Buildable.UnitTests.Mappers +{ + [GeneratedCode("AgileObjects.AgileMapper.Buildable", "0.1.0.0")] + public class ChildMapper : MappingExecutionContextBase<Child> + { + public ChildMapper + ( + Child source + ) + : base(source) + { + } + + public Child ToANew<TTarget>() + where TTarget : Child + { + return ChildMapper.CreateNew(this.CreateRootMappingData(default(Child))); + } + + private static Child MapRepeated + ( + IObjectMappingData<Child, Child> cToCData2 + ) + { + try + { + Child child; + + if (cToCData2.TryGet(cToCData2.Source, out child)) + { + return child; + } + + child = cToCData2.Target ?? new Child(); + cToCData2.Register(cToCData2.Source, child); + child.Name = cToCData2.Source.Name; + + if (cToCData2.Source.EldestParent != null) + { + child.EldestParent = ChildMapper.MapRepeated( + MappingExecutionContextBase<Child>.CreateChildMappingData(cToCData2.Source.EldestParent, child.EldestParent, cToCData2)); + } + + return child; + } + catch (Exception ex) + { + throw MappingException.For( + "CreateNew", + "Child.EldestParent.EldestChild", + "Child.EldestParent.EldestChild", + ex); + } + } + + private static Parent MapRepeated + ( + IObjectMappingData<Parent, Parent> pToPData2 + ) + { + try + { + Parent parent; + + if (pToPData2.TryGet(pToPData2.Source, out parent)) + { + return parent; + } + + parent = pToPData2.Target ?? new Parent(); + pToPData2.Register(pToPData2.Source, parent); + parent.Name = pToPData2.Source.Name; + + if (pToPData2.Source.EldestChild != null) + { + parent.EldestChild = ChildMapper.MapRepeated( + MappingExecutionContextBase<Child>.CreateChildMappingData(pToPData2.Source.EldestChild, parent.EldestChild, pToPData2)); + } + + return parent; + } + catch (Exception ex) + { + throw MappingException.For( + "CreateNew", + "Child.EldestChild.EldestParent", + "Child.EldestParent.EldestChild.EldestParent", + ex); + } + } + + private static Child CreateNew + ( + IObjectMappingData<Child, Child> cToCData + ) + { + Child sourceChild; + try + { + sourceChild = cToCData.Source; + + var child = new Child(); + cToCData.Register(sourceChild, child); + child.Name = sourceChild.Name; + + if (sourceChild.EldestParent != null) + { + child.EldestParent = ChildMapper.GetParent(child, cToCData, sourceChild); + } + + return child; + } + catch (Exception ex) + { + throw MappingException.For( + "CreateNew", + "Child", + "Child", + ex); + } + } + + private static Parent GetParent + ( + Child child, + IObjectMappingData<Child, Child> cToCData, + Child sourceChild + ) + { + try + { + var parent = child.EldestParent ?? new Parent(); + cToCData.Register(sourceChild.EldestParent, parent); + parent.Name = sourceChild.EldestParent.Name; + + if (sourceChild.EldestParent.EldestChild != null) + { + parent.EldestChild = ChildMapper.MapRepeated( + MappingExecutionContextBase<Child>.CreateChildMappingData(sourceChild.EldestParent.EldestChild, parent.EldestChild, cToCData)); + } + + return parent; + } + catch (Exception ex) + { + throw MappingException.For( + "CreateNew", + "Child.EldestParent", + "Child.EldestParent", + ex); + } + } + } +} \ No newline at end of file diff --git a/AgileMapper.Buildable.UnitTests/Mappers/Mapper.cs b/AgileMapper.Buildable.UnitTests/Mappers/Mapper.cs index b5106a8f3..8f5ec6087 100644 --- a/AgileMapper.Buildable.UnitTests/Mappers/Mapper.cs +++ b/AgileMapper.Buildable.UnitTests/Mappers/Mapper.cs @@ -35,6 +35,14 @@ char[] source return new CharArrayMapper(source); } + public static ChildMapper Map + ( + Child source + ) + { + return new ChildMapper(source); + } + public static DateTimeHashSetMapper Map ( HashSet<DateTime> source diff --git a/AgileMapper.Buildable.UnitTests/WhenBuildingCircularReferenceMappers.cs b/AgileMapper.Buildable.UnitTests/WhenBuildingCircularReferenceMappers.cs index b5234dfa6..1f2e1dbe4 100644 --- a/AgileMapper.Buildable.UnitTests/WhenBuildingCircularReferenceMappers.cs +++ b/AgileMapper.Buildable.UnitTests/WhenBuildingCircularReferenceMappers.cs @@ -2,66 +2,61 @@ namespace AgileObjects.AgileMapper.Buildable.UnitTests { using AgileMapper.UnitTests.Common; using AgileMapper.UnitTests.Common.TestClasses; + using Configuration; using Xunit; + using GeneratedMapper = Mappers.Mapper; public class WhenBuildingCircularReferenceMappers { - //[Fact] - //public void ShouldBuildACircularReferenceMapper() - //{ - // using (var mapper = Mapper.CreateNew()) - // { - // mapper.GetPlanFor<Child>().ToANew<Child>(); - - // var sourceCodeExpressions = mapper.GetPlanSourceCodeInCache(); - - // var staticMapperClass = sourceCodeExpressions - // .ShouldCompileAStaticMapperClass(); - - // var staticMapMethod = staticMapperClass - // .GetMapMethods() - // .ShouldHaveSingleItem(); - - // var source = new Child - // { - // Name = "Fred", - // EldestParent = new Parent - // { - // Name = "Bonnie", - // EldestChild = new Child - // { - // Name = "Samson", - // EldestParent = new Parent - // { - // Name = "Franklin" - // } - // } - // } - // }; - - // source.EldestParent.EldestChild.EldestParent.EldestChild = source; - - // var executor = staticMapMethod - // .ShouldCreateMappingExecutor(source); - - // var result = executor - // .ShouldHaveACreateNewMethod() - // .ShouldExecuteACreateNewMapping<Child>(); - - // result.ShouldNotBeNull().ShouldNotBeSameAs(source); - - // result.Name.ShouldBe("Fred"); - // result.EldestParent.ShouldNotBeNull(); - - // result.EldestParent.Name.ShouldBe("Bonnie"); - // result.EldestParent.EldestChild.ShouldNotBeNull(); - - // result.EldestParent.EldestChild.Name.ShouldBe("Samson"); - // result.EldestParent.EldestChild.EldestParent.ShouldNotBeNull(); - - // result.EldestParent.EldestChild.EldestParent.Name.ShouldBe("Franklin"); - // result.EldestParent.EldestChild.EldestParent.EldestChild.ShouldBeSameAs(result); - // } - //} + [Fact] + public void ShouldBuildACircularReferenceMapper() + { + var source = new Child + { + Name = "Fred", + EldestParent = new Parent + { + Name = "Bonnie", + EldestChild = new Child + { + Name = "Samson", + EldestParent = new Parent + { + Name = "Franklin" + } + } + } + }; + + source.EldestParent.EldestChild.EldestParent.EldestChild = source; + + var result = GeneratedMapper.Map(source).ToANew<Child>(); + + result.ShouldNotBeNull().ShouldNotBeSameAs(source); + + result.Name.ShouldBe("Fred"); + result.EldestParent.ShouldNotBeNull(); + + result.EldestParent.Name.ShouldBe("Bonnie"); + result.EldestParent.EldestChild.ShouldNotBeNull(); + + result.EldestParent.EldestChild.Name.ShouldBe("Samson"); + result.EldestParent.EldestChild.EldestParent.ShouldNotBeNull(); + + result.EldestParent.EldestChild.EldestParent.Name.ShouldBe("Franklin"); + result.EldestParent.EldestChild.EldestParent.EldestChild.ShouldBeSameAs(result); + } + + #region Configuration + + public class CircularReferenceMapperConfiguration : BuildableMapperConfiguration + { + protected override void Configure() + { + GetPlanFor<Child>().ToANew<Child>(); + } + } + + #endregion } } \ No newline at end of file diff --git a/AgileMapper.Buildable.UnitTests/WhenBuildingDerivedTypeMappers.cs b/AgileMapper.Buildable.UnitTests/WhenBuildingDerivedTypeMappers.cs index 15f53fd29..fff325413 100644 --- a/AgileMapper.Buildable.UnitTests/WhenBuildingDerivedTypeMappers.cs +++ b/AgileMapper.Buildable.UnitTests/WhenBuildingDerivedTypeMappers.cs @@ -45,7 +45,7 @@ public void ShouldBuildADerivedTypeCreateNewMapper() #region Configuration - public class EnumerableOverwriteMapperConfiguration : BuildableMapperConfiguration + public class DerivedTypeMapperConfiguration : BuildableMapperConfiguration { protected override void Configure() { From 540e498a43e790f1b2203ad13d4972447497cfbb Mon Sep 17 00:00:00 2001 From: Steve Wilkes <steve@agileobjects.co.uk> Date: Wed, 16 Jun 2021 22:34:50 +0100 Subject: [PATCH 44/65] Removing unused class --- .../FluentAssertionExtensions.cs | 111 ------------------ 1 file changed, 111 deletions(-) delete mode 100644 AgileMapper.Buildable.UnitTests/FluentAssertionExtensions.cs diff --git a/AgileMapper.Buildable.UnitTests/FluentAssertionExtensions.cs b/AgileMapper.Buildable.UnitTests/FluentAssertionExtensions.cs deleted file mode 100644 index 86cd544ca..000000000 --- a/AgileMapper.Buildable.UnitTests/FluentAssertionExtensions.cs +++ /dev/null @@ -1,111 +0,0 @@ -namespace AgileObjects.AgileMapper.Buildable.UnitTests -{ - using System; - using System.Collections.Generic; - using System.Reflection; - using AgileMapper.UnitTests.Common; - using BuildableExpressions.Compilation; - using BuildableExpressions.SourceCode; - using NetStandardPolyfills; - - internal static class FluentAssertionExtensions - { - public static Type ShouldCompileAStaticMapperClass( - this IEnumerable<SourceCodeExpression> sourceCodeExpressions) - { - var mapperAssembly = sourceCodeExpressions - .ShouldHaveSingleItem() - .Compile() - .CompiledAssembly - .ShouldNotBeNull(); - - var staticMapperClass = mapperAssembly - .GetType("AgileObjects.AgileMapper.Buildable." + nameof(Mapper)) - .ShouldNotBeNull(); - - return staticMapperClass; - } - - public static IEnumerable<MethodInfo> GetMapMethods(this Type staticMapperClass) - => staticMapperClass.GetPublicStaticMethods("Map"); - - public static MappingExecutionContextBase<TSource> ShouldCreateMappingExecutor<TSource>( - this MethodInfo staticMapMethod, - TSource source) - { - return staticMapMethod - .Invoke(null, new object[] { source }) - .ShouldNotBeNull() - .ShouldBeOfType<MappingExecutionContextBase<TSource>>(); - } - - public static ExecutorTester<TSource> ShouldHaveACreateNewMethod<TSource>( - this MappingExecutionContextBase<TSource> executor) - { - return new ExecutorTester<TSource>( - executor, - executor.GetType() - .GetPublicInstanceMethods("ToANew") - .ShouldHaveSingleItem()); - } - - public static ExecutorTester<TSource> ShouldHaveAMergeMethod<TSource>( - this MappingExecutionContextBase<TSource> executor) - { - return new ExecutorTester<TSource>( - executor, - executor.GetType() - .GetPublicInstanceMethods("OnTo") - .ShouldHaveSingleItem()); - } - - public static ExecutorTester<TSource> ShouldHaveAnOverwriteMethod<TSource>( - this MappingExecutionContextBase<TSource> executor) - { - return new ExecutorTester<TSource>( - executor, - executor.GetType() - .GetPublicInstanceMethods("Over") - .ShouldHaveSingleItem()); - } - - public class ExecutorTester<TSource> - { - private readonly MappingExecutionContextBase<TSource> _executor; - private readonly MethodInfo _mappingMethod; - - public ExecutorTester( - MappingExecutionContextBase<TSource> executor, - MethodInfo mappingMethod) - { - _executor = executor; - _mappingMethod = mappingMethod; - } - - public TResult ShouldExecuteACreateNewMapping<TResult>() - { - return _mappingMethod - .MakeGenericMethod(typeof(TResult)) - .Invoke(_executor, Array.Empty<object>()) - .ShouldNotBeNull() - .ShouldBeOfType<TResult>(); - } - - public TTarget ShouldExecuteAMergeMapping<TTarget>(TTarget target) - { - return _mappingMethod - .Invoke(_executor, new object[] { target }) - .ShouldNotBeNull() - .ShouldBeOfType<TTarget>(); - } - - public TTarget ShouldExecuteAnOverwriteMapping<TTarget>(TTarget target) - { - return _mappingMethod - .Invoke(_executor, new object[] { target }) - .ShouldNotBeNull() - .ShouldBeOfType<TTarget>(); - } - } - } -} From d9e3c46174df7f4561cd7e4c6204f7c3bdc35556 Mon Sep 17 00:00:00 2001 From: Steve Wilkes <steve@agileobjects.co.uk> Date: Thu, 17 Jun 2021 09:29:46 +0100 Subject: [PATCH 45/65] Using type argument as return type in generated mappers with derived types --- .../Mappers/ProductMapper.cs | 14 +++++++++++--- .../WhenBuildingDerivedTypeMappers.cs | 3 +-- AgileMapper.Buildable/BuildableMapperExtensions.cs | 11 ++++++++++- AgileMapper.Buildable/BuildableMapperGroup.cs | 14 +++++++++++--- .../RepeatedMappings/IRepeatedMapperFunc.cs | 3 ++- .../RepeatedMappings/RepeatedMapperFunc.cs | 6 +++++- AgileMapper/Plans/IMappingPlanFunction.cs | 6 ++++++ .../Plans/RepeatedMappingMappingPlanFunction.cs | 2 ++ AgileMapper/Plans/RootMapperMappingPlanFunction.cs | 3 +++ 9 files changed, 51 insertions(+), 11 deletions(-) diff --git a/AgileMapper.Buildable.UnitTests/Mappers/ProductMapper.cs b/AgileMapper.Buildable.UnitTests/Mappers/ProductMapper.cs index 06ebb2d30..612590fb8 100644 --- a/AgileMapper.Buildable.UnitTests/Mappers/ProductMapper.cs +++ b/AgileMapper.Buildable.UnitTests/Mappers/ProductMapper.cs @@ -13,6 +13,9 @@ using AgileObjects.AgileMapper; using AgileObjects.AgileMapper.ObjectPopulation; using AgileObjects.AgileMapper.UnitTests.Common.TestClasses; +using AgileObjects.NetStandardPolyfills; +using AgileObjects.ReadableExpressions; +using AgileObjects.ReadableExpressions.Extensions; namespace AgileObjects.AgileMapper.Buildable.UnitTests.Mappers { @@ -27,10 +30,15 @@ Product source { } - public ProductDto ToANew<TTarget>() - where TTarget : ProductDto + public TTarget ToANew<TTarget>() { - return ProductMapper.CreateNew(this.CreateRootMappingData(default(ProductDto))); + if (typeof(TTarget).IsAssignableTo(typeof(ProductDto))) + { + return (TTarget)((object)ProductMapper.CreateNew(this.CreateRootMappingData(default(ProductDto)))); + } + + throw new NotSupportedException( + "Unable to perform a 'CreateNew' mapping from source type 'Product' to target type '" + typeof(TTarget).GetFriendlyName(null) + "'"); } private static ProductDto CreateNew diff --git a/AgileMapper.Buildable.UnitTests/WhenBuildingDerivedTypeMappers.cs b/AgileMapper.Buildable.UnitTests/WhenBuildingDerivedTypeMappers.cs index fff325413..69eb9b0bf 100644 --- a/AgileMapper.Buildable.UnitTests/WhenBuildingDerivedTypeMappers.cs +++ b/AgileMapper.Buildable.UnitTests/WhenBuildingDerivedTypeMappers.cs @@ -35,8 +35,7 @@ public void ShouldBuildADerivedTypeCreateNewMapper() var derivedTypeToDerivedTypeResult = GeneratedMapper .Map(derivedTypeSource) - .ToANew<ProductDtoMega>() - .ShouldBeOfType<ProductDtoMega>(); + .ToANew<ProductDtoMega>(); derivedTypeToDerivedTypeResult.ProductId.ShouldBe("222"); derivedTypeToDerivedTypeResult.Price.ShouldBe(119.99m); diff --git a/AgileMapper.Buildable/BuildableMapperExtensions.cs b/AgileMapper.Buildable/BuildableMapperExtensions.cs index 1c98351ee..2f0ea5c42 100644 --- a/AgileMapper.Buildable/BuildableMapperExtensions.cs +++ b/AgileMapper.Buildable/BuildableMapperExtensions.cs @@ -223,7 +223,14 @@ private static bool UseTypeConstraint(IList<MapMethodInfo> mapMethodInfos) return false; } - var targetType = mapMethodInfos[0].TargetType; + var firstMapMethod = mapMethodInfos[0]; + + if (firstMapMethod.HasDerivedTypes) + { + return false; + } + + var targetType = firstMapMethod.TargetType; return !(targetType.IsArray || targetType.IsEnum()); } @@ -309,6 +316,8 @@ public MapMethodInfo( public Type TargetType { get; } + public bool HasDerivedTypes => _mapperGroup.HasDerivedTypes; + public Expression CreateMapCall(Func<Type, Expression> targetFactory) { var createMappingDataCall = Call( diff --git a/AgileMapper.Buildable/BuildableMapperGroup.cs b/AgileMapper.Buildable/BuildableMapperGroup.cs index be7f7c276..85ccaef46 100644 --- a/AgileMapper.Buildable/BuildableMapperGroup.cs +++ b/AgileMapper.Buildable/BuildableMapperGroup.cs @@ -12,6 +12,7 @@ internal class BuildableMapperGroup { + private bool? _hasDerivedTypes; private MethodInfo _createChildMappingDataMethod; public BuildableMapperGroup( @@ -63,6 +64,15 @@ private static string GetMapperNamePrefix(Type sourceType) public Type SourceType { get; } + public bool HasDerivedTypes + => _hasDerivedTypes ??= DetermineIfHasDerivedTypes(); + + private bool DetermineIfHasDerivedTypes() + { + return MappingMethodsByPlan.Keys + .Any(plan => plan.Any(p => p.HasDerivedTypes)); + } + public Type MapperBaseType { get; } public ConstructorInfo MapperBaseTypeConstructor { get; } @@ -71,7 +81,7 @@ private static string GetMapperNamePrefix(Type sourceType) public MethodInfo CreateChildMappingDataMethod => _createChildMappingDataMethod ??= MapperBaseType - .GetNonPublicStaticMethod("CreateChildMappingData"); + .GetNonPublicStaticMethod("CreateChildMappingData"); public string MapperName { get; } @@ -79,8 +89,6 @@ public MethodInfo CreateChildMappingDataMethod public ClassExpression MapperClass { get; set; } - public ICollection<IMappingPlan> Plans => MappingMethodsByPlan.Keys; - public IDictionary<IMappingPlan, List<MethodExpression>> MappingMethodsByPlan { get; } } } \ No newline at end of file diff --git a/AgileMapper/ObjectPopulation/RepeatedMappings/IRepeatedMapperFunc.cs b/AgileMapper/ObjectPopulation/RepeatedMappings/IRepeatedMapperFunc.cs index abb0ce2a0..7739afd8c 100644 --- a/AgileMapper/ObjectPopulation/RepeatedMappings/IRepeatedMapperFunc.cs +++ b/AgileMapper/ObjectPopulation/RepeatedMappings/IRepeatedMapperFunc.cs @@ -13,7 +13,8 @@ internal interface IRepeatedMapperFunc : IObjectMapperFuncBase Type TargetType { get; } - LambdaExpression Mapping { get; } + bool HasDerivedTypes { get; } + LambdaExpression Mapping { get; } } } \ No newline at end of file diff --git a/AgileMapper/ObjectPopulation/RepeatedMappings/RepeatedMapperFunc.cs b/AgileMapper/ObjectPopulation/RepeatedMappings/RepeatedMapperFunc.cs index 88499c115..a1b42dedd 100644 --- a/AgileMapper/ObjectPopulation/RepeatedMappings/RepeatedMapperFunc.cs +++ b/AgileMapper/ObjectPopulation/RepeatedMappings/RepeatedMapperFunc.cs @@ -6,6 +6,7 @@ namespace AgileObjects.AgileMapper.ObjectPopulation.RepeatedMappings #else using System.Linq.Expressions; #endif + using Extensions.Internal; using Members; internal class RepeatedMapperFunc<TChildSource, TChildTarget> : IRepeatedMapperFunc @@ -16,10 +17,11 @@ internal class RepeatedMapperFunc<TChildSource, TChildTarget> : IRepeatedMapperF public RepeatedMapperFunc(IObjectMappingData mappingData, bool lazyLoadFuncs) { + _mapperData = mappingData.MapperData; + if (lazyLoadFuncs) { _mappingFuncLock = new object(); - _mapperData = mappingData.MapperData; _mapperData.SetEntryPoint(); return; } @@ -33,6 +35,8 @@ public RepeatedMapperFunc(IObjectMappingData mappingData, bool lazyLoadFuncs) public Type TargetType => typeof(TChildTarget); + public bool HasDerivedTypes => _mapperData.DerivedMapperDatas.Any(); + public object Map(IObjectMappingData mappingData) { var typedData = (ObjectMappingData<TChildSource, TChildTarget>)mappingData; diff --git a/AgileMapper/Plans/IMappingPlanFunction.cs b/AgileMapper/Plans/IMappingPlanFunction.cs index 97b3e25d9..7b9f36629 100644 --- a/AgileMapper/Plans/IMappingPlanFunction.cs +++ b/AgileMapper/Plans/IMappingPlanFunction.cs @@ -32,6 +32,12 @@ public interface IMappingPlanFunction /// </summary> Type TargetType { get; } + /// <summary> + /// Gets a value indicating whether this <see cref="IMappingPlanFunction"/> includes mapping + /// of the <see cref="SourceType"/> and <see cref="TargetType"/>. + /// </summary> + bool HasDerivedTypes { get; } + /// <summary> /// Gets an Expression summarising the <see cref="IMappingPlanFunction"/>. /// </summary> diff --git a/AgileMapper/Plans/RepeatedMappingMappingPlanFunction.cs b/AgileMapper/Plans/RepeatedMappingMappingPlanFunction.cs index 3f6a0de0d..697518030 100644 --- a/AgileMapper/Plans/RepeatedMappingMappingPlanFunction.cs +++ b/AgileMapper/Plans/RepeatedMappingMappingPlanFunction.cs @@ -26,6 +26,8 @@ public RepeatedMappingMappingPlanFunction(IRepeatedMapperFunc mapperFunc) public Type TargetType => _mapperFunc.TargetType; + public bool HasDerivedTypes => _mapperFunc.HasDerivedTypes; + public CommentExpression Summary => _summary ??= ReadableExpression.Comment(GetMappingDescription()); diff --git a/AgileMapper/Plans/RootMapperMappingPlanFunction.cs b/AgileMapper/Plans/RootMapperMappingPlanFunction.cs index d768cd865..65431f707 100644 --- a/AgileMapper/Plans/RootMapperMappingPlanFunction.cs +++ b/AgileMapper/Plans/RootMapperMappingPlanFunction.cs @@ -1,6 +1,7 @@ namespace AgileObjects.AgileMapper.Plans { using System; + using Extensions.Internal; #if NET35 using Microsoft.Scripting.Ast; #else @@ -30,6 +31,8 @@ public RootMapperMappingPlanFunction(IObjectMapper mapper) public Type TargetType => _mapperData.TargetType; + public bool HasDerivedTypes => _mapperData.DerivedMapperDatas.Any(); + public CommentExpression Summary => _summary ??= ReadableExpression.Comment(GetMappingDescription()); From 1a16e3be5cf2975d583a7f04c918f636aeec4c45 Mon Sep 17 00:00:00 2001 From: Steve Wilkes <steve@agileobjects.co.uk> Date: Thu, 17 Jun 2021 15:26:27 +0100 Subject: [PATCH 46/65] Removing unused local variables in more cases / Removing variable name generation code --- AgileMapper.Buildable/BuildableMapperGroup.cs | 37 +------- .../Configuration/WhenViewingMappingPlans.cs | 3 +- .../WhenMappingToNewDictionaries.cs | 9 ++ .../WhenViewingDictionaryMappingPlans.cs | 20 ++--- .../Internal/WhenGeneratingVariableNames.cs | 84 +------------------ .../WhenMappingOverComplexTypes.cs | 14 +++- .../WhenUsingPartialTrust.cs | 4 +- .../WhenViewingMappingPlans.cs | 42 +++++----- .../Internal/EnumerableExtensions.cs | 30 +++++-- .../Internal/ExpressionExtensions.cs | 32 ++++++- .../Extensions/Internal/TypeExtensions.cs | 70 +--------------- .../Dictionaries/DictionaryTargetMember.cs | 42 +++++++--- .../DerivedSourceTypeCheck.cs | 1 + .../DictionaryMappingExpressionFactory.cs | 4 +- .../MappingExpressionFactoryBase.cs | 45 ++++++---- .../TypeConversion/TryParseConverter.cs | 1 + 16 files changed, 180 insertions(+), 258 deletions(-) diff --git a/AgileMapper.Buildable/BuildableMapperGroup.cs b/AgileMapper.Buildable/BuildableMapperGroup.cs index 85ccaef46..9a7925e23 100644 --- a/AgileMapper.Buildable/BuildableMapperGroup.cs +++ b/AgileMapper.Buildable/BuildableMapperGroup.cs @@ -23,45 +23,10 @@ public BuildableMapperGroup( MapperBaseType = typeof(MappingExecutionContextBase<>).MakeGenericType(sourceType); MapperBaseTypeConstructor = MapperBaseType.GetNonPublicInstanceConstructor(sourceType); CreateRootMappingDataMethod = MapperBaseType.GetNonPublicInstanceMethod("CreateRootMappingData"); - MapperName = GetMapperNamePrefix(sourceType) + "Mapper"; + MapperName = sourceType.GetVariableNameInPascalCase() + "Mapper"; MappingMethodsByPlan = plans.ToDictionary(p => p, p => new List<MethodExpression>()); } - #region Setup - - private static string GetMapperNamePrefix(Type sourceType) - { - if (sourceType.IsArray) - { - return sourceType.GetVariableNameInPascalCase(); - } - - if (sourceType == typeof(string)) - { - return nameof(String); - } - - var elementType = sourceType.GetEnumerableElementType(); - - if (elementType == null) - { - return sourceType.GetVariableNameInPascalCase(); - } - - var elementTypeName = elementType.GetVariableNameInPascalCase(); - var collectionTypeName = sourceType.Name; - - if (sourceType.IsGenericType()) - { - collectionTypeName = collectionTypeName - .Substring(0, collectionTypeName.IndexOf('`')); - } - - return elementTypeName + collectionTypeName; - } - - #endregion - public Type SourceType { get; } public bool HasDerivedTypes diff --git a/AgileMapper.UnitTests/Configuration/WhenViewingMappingPlans.cs b/AgileMapper.UnitTests/Configuration/WhenViewingMappingPlans.cs index 73a6bf857..566fe219c 100644 --- a/AgileMapper.UnitTests/Configuration/WhenViewingMappingPlans.cs +++ b/AgileMapper.UnitTests/Configuration/WhenViewingMappingPlans.cs @@ -101,13 +101,12 @@ public void ShouldShowMapChildObjectCalls() .Map((pp, ptf) => pp.Value) .To(ptf => ptf.Value1); - string plan = mapper .GetPlanFor<PublicProperty<string>>() .ToANew<PublicTwoFields<string, object>>(); plan.ShouldContain("// Map PublicProperty<string> -> PublicTwoFields<string, object>"); - plan.ShouldContain(".Value1 = ppsToPtfsoData.Source.Value"); + plan.ShouldContain(".Value1 = sppToSoptfData.Source.Value"); plan.ShouldContain("// No data sources for Value2"); } } diff --git a/AgileMapper.UnitTests/Dictionaries/WhenMappingToNewDictionaries.cs b/AgileMapper.UnitTests/Dictionaries/WhenMappingToNewDictionaries.cs index 4e047ce84..3dad6eb0b 100644 --- a/AgileMapper.UnitTests/Dictionaries/WhenMappingToNewDictionaries.cs +++ b/AgileMapper.UnitTests/Dictionaries/WhenMappingToNewDictionaries.cs @@ -45,6 +45,15 @@ public void ShouldMapAComplexTypeMemberToATypedDictionary() result["Value"].ShouldBeOfType<Product>(); } + [Fact] + public void ShouldMapAComplexTypeMemberToAnUntypedDictionary() + { + var source = new PublicField<Address> { Value = new Address { Line1 = "One!" } }; + var result = Mapper.Map(source).ToANew<Dictionary<string, string>>(); + + result.ShouldContainKeyAndValue("Value.Line1", "One!"); + } + [Fact] public void ShouldMapASimpleTypeMemberToAConvertibleTypedDictionary() { diff --git a/AgileMapper.UnitTests/Dictionaries/WhenViewingDictionaryMappingPlans.cs b/AgileMapper.UnitTests/Dictionaries/WhenViewingDictionaryMappingPlans.cs index 9c2c13d49..c77ba1f5a 100644 --- a/AgileMapper.UnitTests/Dictionaries/WhenViewingDictionaryMappingPlans.cs +++ b/AgileMapper.UnitTests/Dictionaries/WhenViewingDictionaryMappingPlans.cs @@ -21,9 +21,9 @@ public void ShouldShowATargetObjectMappingPlan() .GetPlanFor<Dictionary<string, string>>() .ToANew<CustomerViewModel>(); - plan.ShouldContain("Dictionary<string, string> sourceDictionary_String_String"); - plan.ShouldContain("idKey = sourceDictionary_String_String.Keys.FirstOrDefault(key => key.MatchesKey(\"Id\""); - plan.ShouldContain("id = sourceDictionary_String_String[idKey]"); + plan.ShouldContain("Dictionary<string, string> sourceStringStringDictionary"); + plan.ShouldContain("idKey = sourceStringStringDictionary.Keys.FirstOrDefault(key => key.MatchesKey(\"Id\""); + plan.ShouldContain("id = sourceStringStringDictionary[idKey]"); plan.ShouldContain("customerViewModel.Id ="); #if NET35 plan.ShouldContain("id.ToGuid()"); @@ -40,9 +40,9 @@ public void ShouldShowATargetComplexTypeCollectionMappingPlan() .ToANew<Collection<Address>>(); plan.ShouldContain("targetElementKey = \"[\" + i + \"]\""); - plan.ShouldContain("elementKeyExists = sourceDictionary_String_Object.ContainsKey(targetElementKey)"); + plan.ShouldContain("elementKeyExists = sourceStringObjectDictionary.ContainsKey(targetElementKey)"); plan.ShouldContain("var line1Key = \"[\" + i + \"].Line1\""); - plan.ShouldContain("line1 = sourceDictionary_String_Object[line1Key]"); + plan.ShouldContain("line1 = sourceStringObjectDictionary[line1Key]"); plan.ShouldContain("address.Line1 = line1.ToString()"); } @@ -53,9 +53,9 @@ public void ShouldShowASourceObjectMappingPlan() .GetPlanFor<MysteryCustomer>() .ToANew<Dictionary<string, object>>(); - plan.ShouldContain("dictionary_String_Object = new Dictionary<string, object>()"); - plan.ShouldContain("dictionary_String_Object[\"Name\"] = sourceMysteryCustomer.Name"); - plan.ShouldContain("dictionary_String_Object[\"Address.Line1\"] = sourceMysteryCustomer.Address.Line1;"); + plan.ShouldContain("stringObjectDictionary = new Dictionary<string, object>()"); + plan.ShouldContain("stringObjectDictionary[\"Name\"] = sourceMysteryCustomer.Name"); + plan.ShouldContain("stringObjectDictionary[\"Address.Line1\"] = sourceMysteryCustomer.Address.Line1;"); } [Fact] @@ -66,8 +66,8 @@ public void ShouldShowASourceComplexTypeEnumerableMappingPlan() .ToANew<Dictionary<string, string>>(); plan.ShouldContain("sourceMysteryCustomerViewModel = enumerator.Current as MysteryCustomerViewModel"); - plan.ShouldContain("dictionary_String_String[\"[\" + i + \"].Report\"] = sourceMysteryCustomerViewModel.Report"); - plan.ShouldContain("dictionary_String_String[\"[\" + i + \"].AddressLine1\"] = enumerator.Current.AddressLine1"); + plan.ShouldContain("stringStringDictionary[\"[\" + i + \"].Report\"] = sourceMysteryCustomerViewModel.Report"); + plan.ShouldContain("stringStringDictionary[\"[\" + i + \"].AddressLine1\"] = enumerator.Current.AddressLine1"); } } diff --git a/AgileMapper.UnitTests/Extensions/Internal/WhenGeneratingVariableNames.cs b/AgileMapper.UnitTests/Extensions/Internal/WhenGeneratingVariableNames.cs index 19fa7ae0e..546ff80a2 100644 --- a/AgileMapper.UnitTests/Extensions/Internal/WhenGeneratingVariableNames.cs +++ b/AgileMapper.UnitTests/Extensions/Internal/WhenGeneratingVariableNames.cs @@ -14,91 +14,15 @@ public class WhenGeneratingVariableNames { [Fact] - public void ShouldNameAShortVariableForACollectionType() + public void ShouldNameAShortCollectionTypeVariable() { - typeof(ICollection<Person>).GetShortVariableName().ShouldBe("ps"); + typeof(ICollection<Person>).GetShortVariableName().ShouldBe("pic"); } [Fact] - public void ShouldNameAMultiLetterShortVariableForACollectionType() + public void ShouldNameAMultiLetterShortCollectionTypeVariable() { - typeof(ICollection<CustomerViewModel>).GetShortVariableName().ShouldBe("cvms"); + typeof(ICollection<CustomerViewModel>).GetShortVariableName().ShouldBe("cvmic"); } - - [Fact] - public void ShouldNameAVariableForAnArrayType() - { - typeof(Box[]).GetVariableNameInCamelCase().ShouldBe("boxArray"); - } - - [Fact] - public void ShouldNameAVariableForACollectionTypeEndingInX() - { - typeof(ICollection<Box>).GetVariableNameInCamelCase().ShouldBe("boxes"); - } - - [Fact] - public void ShouldNameAVariableForAnEnumerableTypeEndingInZ() - { - typeof(IEnumerable<Fuzz>).GetVariableNameInPascalCase().ShouldBe("Fuzzes"); - } - - [Fact] - public void ShouldNameAVariableForAnEnumerableTypeEndingInDoubleS() - { - typeof(IEnumerable<Glass>).GetVariableNameInPascalCase().ShouldBe("Glasses"); - } - - [Fact] - public void ShouldNameAVariableForAListTypeEndingInCh() - { - typeof(List<Church>).GetVariableNameInCamelCase().ShouldBe("churches"); - } - - [Fact] - public void ShouldNameAVariableForAListTypeEndingInSh() - { - typeof(List<Hush>).GetVariableNameInCamelCase().ShouldBe("hushes"); - } - - [Fact] - public void ShouldNameAVariableForAListTypeEndingInVowelY() - { - typeof(List<Journey>).GetVariableNameInCamelCase().ShouldBe("journeys"); - } - - [Fact] - public void ShouldNameAVariableForAnIListTypeEndingInConsonantY() - { - typeof(IList<Body>).GetVariableNameInPascalCase().ShouldBe("Bodies"); - } - - [Fact] - public void ShouldNameANullableLongVariable() - { - typeof(long?).GetVariableNameInCamelCase().ShouldBe("nullableLong"); - } - - [Fact] - public void ShouldNameAnArrayOfArraysVariable() - { - typeof(int?[][]).GetVariableNameInCamelCase().ShouldBe("nullableIntArrayArray"); - } - - // ReSharper disable ClassNeverInstantiated.Local - private class Box { } - - private class Fuzz { } - - private class Glass { } - - private class Church { } - - private class Hush { } - - private class Journey { } - - private class Body { } - // ReSharper restore ClassNeverInstantiated.Local } } diff --git a/AgileMapper.UnitTests/WhenMappingOverComplexTypes.cs b/AgileMapper.UnitTests/WhenMappingOverComplexTypes.cs index e6188201a..84e430701 100644 --- a/AgileMapper.UnitTests/WhenMappingOverComplexTypes.cs +++ b/AgileMapper.UnitTests/WhenMappingOverComplexTypes.cs @@ -43,7 +43,19 @@ public void ShouldOverwriteAnExistingSimpleTypePropertyValue() Mapper.Map(source).Over(target); - target.Value.ShouldBe(source.Value); + target.Value.ShouldBe(123); + } + + [Fact] + public void ShouldOverwriteExistingSimpleTypePropertyValues() + { + var source = new Address { Line1 = "Source 1", Line2 = "Source 2" }; + var target = new Address { Line1 = "Target 1", Line2 = "Target 2" }; + + Mapper.Map(source).Over(target); + + target.Line1.ShouldBe("Source 1"); + target.Line2.ShouldBe("Source 2"); } [Fact] diff --git a/AgileMapper.UnitTests/WhenUsingPartialTrust.cs b/AgileMapper.UnitTests/WhenUsingPartialTrust.cs index 9648862c3..0b08fbd1d 100644 --- a/AgileMapper.UnitTests/WhenUsingPartialTrust.cs +++ b/AgileMapper.UnitTests/WhenUsingPartialTrust.cs @@ -231,9 +231,9 @@ public void TestMappingPlan() plan); Assert.Contains("// Rule Set: Overwrite", plan); - Assert.Contains("ptfooaToPtfccData.Map(", plan); + Assert.Contains("ooaptfToCcieptfData.Map(", plan); Assert.Contains("\"Value1\"", plan); - Assert.Contains("customers.Add(oaToCsData.Map(objectArray[i]", RemoveWhiteSpace(plan)); + Assert.Contains("customerICollection.Add(oaToCieData.Map(objectArray[i]", RemoveWhiteSpace(plan)); } #region Helper Members diff --git a/AgileMapper.UnitTests/WhenViewingMappingPlans.cs b/AgileMapper.UnitTests/WhenViewingMappingPlans.cs index 9749a6d7e..f3b7991b5 100644 --- a/AgileMapper.UnitTests/WhenViewingMappingPlans.cs +++ b/AgileMapper.UnitTests/WhenViewingMappingPlans.cs @@ -28,7 +28,7 @@ public void ShouldShowASimpleTypeMemberMapping() .GetPlanFor<PublicField<string>>() .ToANew<PublicProperty<string>>(); - plan.ShouldContain("publicProperty_String.Value = pfsToPpsData.Source.Value;"); + plan.ShouldContain("stringPublicProperty.Value = spfToSppData.Source.Value;"); } [Fact] @@ -61,8 +61,8 @@ public void ShouldSupportAnonymousSourceTypesFromTheStaticApi() .ToANew<MysteryCustomer>(); plan.ShouldContain("Map AnonymousType<string, int> -> MysteryCustomer"); - plan.ShouldContain("mysteryCustomer.Name = fatsiToMcData.Source.Name;"); - plan.ShouldContain("mysteryCustomer.Discount = fatsiToMcData.Source.Discount;"); + plan.ShouldContain("mysteryCustomer.Name = siatToMcData.Source.Name;"); + plan.ShouldContain("mysteryCustomer.Discount = siatToMcData.Source.Discount;"); plan.ShouldContain("// No data sources for Report"); } @@ -76,8 +76,8 @@ public void ShouldSupportAnonymousSourceTypesFromTheInstanceApi() .OnTo<Customer>(); plan.ShouldContain("Map AnonymousType<string, string> -> Customer"); - plan.ShouldContain(".Target.Name = fatssToCData.Source.Name"); - plan.ShouldContain("address.Line1 = fatssToCData.Source.AddressLine1"); + plan.ShouldContain(".Target.Name = ssatToCData.Source.Name"); + plan.ShouldContain("address.Line1 = ssatToCData.Source.AddressLine1"); } } @@ -88,7 +88,7 @@ public void ShouldSupportStructsFromTheStaticApi() .GetPlanFor<PublicTwoFieldsStruct<int, int>>() .Over<PublicTwoFieldsStruct<string, string>>(); - plan.ShouldContain("publicTwoFieldsStruct_String_String.Value1 = ptfsiiToPtfsssData.Source.Value1.ToString();"); + plan.ShouldContain("stringStringPublicTwoFieldsStruct.Value1 = iiptfsToSsptfsData.Source.Value1.ToString();"); } [Fact] @@ -98,7 +98,7 @@ public void ShouldSupportStructMergePlansFromTheStaticApi() .GetPlanFor<PublicTwoFieldsStruct<int, int>>() .OnTo<PublicTwoFieldsStruct<string, string>>(); - plan.ShouldContain("publicTwoFieldsStruct_String_String.Value1 = ptfsiiToPtfsssData.Source.Value1.ToString();"); + plan.ShouldContain("stringStringPublicTwoFieldsStruct.Value1 = iiptfsToSsptfsData.Source.Value1.ToString();"); } [Fact] @@ -111,8 +111,8 @@ public void ShouldSupportStructsFromTheInstanceApi() .ToANew<PublicCtorStruct<string>>(); plan.ShouldContain("Map PublicPropertyStruct<string> -> PublicCtorStruct<string>"); - plan.ShouldContain("new PublicCtorStruct<string>(ppssToPcssData.Source.Value)"); - plan.ShouldContain("return publicCtorStruct_String"); + plan.ShouldContain("new PublicCtorStruct<string>(sppsToSpcsData.Source.Value)"); + plan.ShouldContain("return stringPublicCtorStruct"); } } @@ -135,10 +135,10 @@ public void ShouldShowASimpleTypeEnumerableMemberMapping() .ToANew<PublicField<IEnumerable<int>>>(); plan.ShouldContain("sourceIntArray = "); - plan.ShouldContain("ICollection<int> targetInts = "); - plan.ShouldContain(" = publicField_Ints.Value as ICollection<int>) != null"); - plan.ShouldContain("new List<int>(publicField_Ints.Value)"); - plan.ShouldContain("targetInts.Add(sourceIntArray[i])"); + plan.ShouldContain("ICollection<int> targetIntICollection = "); + plan.ShouldContain(" = intIEnumerablePublicField.Value as ICollection<int>) != null"); + plan.ShouldContain("new List<int>(intIEnumerablePublicField.Value)"); + plan.ShouldContain("targetIntICollection.Add(sourceIntArray[i])"); } [Fact] @@ -148,7 +148,7 @@ public void ShouldShowASimpleTypeMemberConversion() .GetPlanFor<PublicProperty<Guid>>() .ToANew<PublicField<string>>(); - plan.ShouldContain("ppgToPfsData.Source.Value.ToString("); + plan.ShouldContain("gppToSpfData.Source.Value.ToString("); } [Fact] @@ -159,7 +159,7 @@ public void ShouldShowARootComplexTypeEnumerableMapping() .OnTo<IEnumerable<PersonViewModel>>(); plan.ShouldContain("collectionData.Intersection.ForEach((existingPerson, existingPersonViewModel, idx) =>"); - plan.ShouldContain("persons = collectionData.NewSourceItems"); + plan.ShouldContain("personIEnumerable = collectionData.NewSourceItems"); } [Fact] @@ -169,9 +169,9 @@ public void ShouldShowAComplexTypeEnumerableMemberMapping() .GetPlanFor<IList<PersonViewModel>>() .Over<IEnumerable<Person>>(); - plan.ShouldContain("personViewModels = collectionData.NewSourceItems"); + plan.ShouldContain("personViewModelIEnumerable = collectionData.NewSourceItems"); plan.ShouldContain("collectionData.Intersection.ForEach((existingPersonViewModel, existingPerson, idx) =>"); - plan.ShouldContain("collectionData.AbsentTargetItems.ForEach(p => persons.Remove(p)"); + plan.ShouldContain("collectionData.AbsentTargetItems.ForEach(p => personICollection.Remove(p)"); plan.ShouldContain("IList<PersonViewModel> -> IEnumerable<Person>"); plan.ShouldNotContain("PersonViewModel -> Person"); @@ -208,7 +208,7 @@ public void ShouldShowMapChildCalls() .ToANew<PublicSetMethod<Customer>>(); plan.ShouldContain("// Map PublicProperty<object> -> PublicSetMethod<Customer>"); - plan.ShouldContain("ppoToPsmcData.Map("); + plan.ShouldContain("oppToCpsmData.Map("); plan.ShouldContain("\"SetValue\""); } @@ -221,7 +221,7 @@ public void ShouldShowNestedMapChildCalls() plan.ShouldContain("// Map PublicProperty<PublicField<object>> -> PublicSetMethod<PublicProperty<Order>>"); plan.ShouldNotContain("// Map PublicField<object> -> PublicProperty<Order>"); - plan.ShouldContain("pfoToPpoData.Map("); + plan.ShouldContain("opfToOppData.Map("); plan.ShouldContain("\"Value\""); } @@ -233,7 +233,7 @@ public void ShouldShowMapElementCalls() .ToANew<PublicSetMethod<ICollection<Product>>>(); plan.ShouldContain("// Map PublicProperty<object[]> -> PublicSetMethod<ICollection<Product>>"); - RemoveWhiteSpace(plan).ShouldContain("products.Add(oaToPsData.Map(objectArray[i]"); + RemoveWhiteSpace(plan).ShouldContain("productList.Add(oaToPicData.Map(objectArray[i]"); } // See https://github.com/agileobjects/AgileMapper/issues/24 @@ -411,7 +411,7 @@ public void ShouldShowUnmappableStructComplexTypeMemberDetails() .GetPlanFor<PublicTwoFields<Person, string>>() .ToANew<PublicTwoFieldsStruct<Person, int>>(); - plan.ShouldContain("int.TryParse(ptfpsToPtfspiData.Source.Value2"); + plan.ShouldContain("int.TryParse(psptfToPiptfsData.Source.Value2"); plan.ShouldContain("Person member on a struct"); } } diff --git a/AgileMapper/Extensions/Internal/EnumerableExtensions.cs b/AgileMapper/Extensions/Internal/EnumerableExtensions.cs index 5f55aa281..ba70e0f31 100644 --- a/AgileMapper/Extensions/Internal/EnumerableExtensions.cs +++ b/AgileMapper/Extensions/Internal/EnumerableExtensions.cs @@ -80,11 +80,11 @@ public static IEnumerable<T> TakeUntil<T>(this IEnumerable<T> items, Func<T, boo } [DebuggerStepThrough] - public static bool TryFindMatch<T>(this IList<T> items, Func<T, bool> predicate, out T match) - => TryFindMatch(items, predicate, (p, item) => p.Invoke(item), out match); - - [DebuggerStepThrough] - public static bool TryFindMatch<TArg, T>(this IList<T> items, TArg argument, Func<TArg, T, bool> predicate, out T match) + public static bool TryFindMatch<TArg, T>( + this IList<T> items, + TArg argument, + Func<TArg, T, bool> predicate, + out T match) { for (int i = 0, n = items.Count; i < n; ++i) { @@ -100,6 +100,26 @@ public static bool TryFindMatch<TArg, T>(this IList<T> items, TArg argument, Fun return false; } + [DebuggerStepThrough] + public static bool TryFindMatch<TArg, T>( + this IEnumerable<T> items, + TArg argument, + Func<TArg, T, bool> predicate, + out T match) + { + foreach (var item in items) + { + if (predicate.Invoke(argument, item)) + { + match = item; + return true; + } + } + + match = default; + return false; + } + [DebuggerStepThrough] public static T Last<T>(this IList<T> items) => items[items.Count - 1]; diff --git a/AgileMapper/Extensions/Internal/ExpressionExtensions.cs b/AgileMapper/Extensions/Internal/ExpressionExtensions.cs index 0a1984038..94b4533df 100644 --- a/AgileMapper/Extensions/Internal/ExpressionExtensions.cs +++ b/AgileMapper/Extensions/Internal/ExpressionExtensions.cs @@ -354,9 +354,18 @@ private static bool IsMapRepeatedCall(Expression expression) private static bool IsCallTo(string methodName, Expression call) => ((MethodCallExpression)call).Method.Name == methodName; - public static bool TryGetVariableAssignment(this IList<Expression> mappingExpressions, out BinaryExpression assignment) + public static bool TryGetAssignment( + this IList<Expression> mappingExpressions, + ParameterExpression variable, + out BinaryExpression assignment) { - if (mappingExpressions.TryFindMatch(exp => exp.NodeType == Assign, out var assignmentExpression)) + var assignmentExists = + EnumerateExpressions(mappingExpressions).TryFindMatch( + variable, + (var, exp) => exp.NodeType == Assign && ((BinaryExpression)exp).Left == var, + out var assignmentExpression); + + if (assignmentExists) { assignment = (BinaryExpression)assignmentExpression; return true; @@ -365,6 +374,25 @@ public static bool TryGetVariableAssignment(this IList<Expression> mappingExpres assignment = null; return false; } + + private static IEnumerable<Expression> EnumerateExpressions(IEnumerable<Expression> expressions) + { + foreach (var expression in expressions) + { + if (expression.NodeType != Block) + { + yield return expression; + continue; + } + + var block = (BlockExpression)expression; + + foreach (var blockExpression in EnumerateExpressions(block.Expressions)) + { + yield return blockExpression; + } + } + } #if NET35 public static LambdaExpression ToDlrExpression(this LinqExp.LambdaExpression linqLambda) => LinqExpressionToDlrExpressionConverter.Convert(linqLambda); diff --git a/AgileMapper/Extensions/Internal/TypeExtensions.cs b/AgileMapper/Extensions/Internal/TypeExtensions.cs index d82a9e16c..05c1b56fd 100644 --- a/AgileMapper/Extensions/Internal/TypeExtensions.cs +++ b/AgileMapper/Extensions/Internal/TypeExtensions.cs @@ -36,75 +36,7 @@ public static string GetShortVariableName(this Type type) variableName[0] + variableName.ToCharArray().Skip(1).Filter(char.IsUpper).Join(string.Empty); - shortVariableName = shortVariableName.ToLowerInvariant(); - - return (!type.IsArray && type.IsEnumerable()) - ? shortVariableName.Pluralise() - : shortVariableName; - } - - public static string GetVariableNameInCamelCase(this Type type) => GetVariableName(type).ToCamelCase(); - - public static string GetVariableNameInPascalCase(this Type type) => GetVariableName(type).ToPascalCase(); - - private static string GetVariableName(Type type) - { - if (type.IsArray) - { - return GetVariableName(type.GetElementType()) + "Array"; - } - - var typeIsEnumerable = type.IsEnumerable(); - var typeIsDictionary = typeIsEnumerable && type.IsDictionary(); - var namingType = (typeIsEnumerable && !typeIsDictionary) ? type.GetEnumerableElementType() : type; - var variableName = GetBaseVariableName(namingType); - - if (namingType.IsInterface()) - { - variableName = variableName.Substring(1); - } - - if (namingType.IsGenericType()) - { - variableName = GetGenericTypeVariableName(variableName, namingType); - } - - variableName = RemoveLeadingNonAlphaNumerics(variableName); - - return (typeIsDictionary || !typeIsEnumerable) ? variableName : variableName.Pluralise(); - } - - private static string GetBaseVariableName(Type namingType) - => namingType.IsPrimitive() ? namingType.GetFriendlyName() : namingType.Name; - - private static string GetGenericTypeVariableName(string variableName, Type namingType) - { - var nonNullableType = namingType.GetNonNullableType(); - var genericTypeArguments = namingType.GetGenericTypeArguments(); - - if (nonNullableType != namingType) - { - return "nullable" + genericTypeArguments[0].GetVariableNameInPascalCase(); - } - - variableName = variableName.Substring(0, variableName.IndexOf('`')); - - variableName += genericTypeArguments - .Project(arg => "_" + arg.GetVariableNameInPascalCase()) - .Join(string.Empty); - - return variableName; - } - - private static string RemoveLeadingNonAlphaNumerics(string value) - { - // Anonymous types start with non-alphanumeric characters - while (!char.IsLetterOrDigit(value, 0)) - { - value = value.Substring(1); - } - - return value; + return shortVariableName.ToLowerInvariant(); } public static bool RuntimeTypeNeeded(this Type type) diff --git a/AgileMapper/Members/Dictionaries/DictionaryTargetMember.cs b/AgileMapper/Members/Dictionaries/DictionaryTargetMember.cs index 917e8eac2..0b1132a02 100644 --- a/AgileMapper/Members/Dictionaries/DictionaryTargetMember.cs +++ b/AgileMapper/Members/Dictionaries/DictionaryTargetMember.cs @@ -304,35 +304,55 @@ private static bool TryGetMappingBody(Expression value, out Expression mapping) return false; } - var mappingExpressions = GetMappingExpressions(mappingBlock); + var mappingExpressions = GetMappingExpressions(mappingBlock, out var variables); if (mappingExpressions.HasOne() && (mappingExpressions[0].NodeType == Block)) { - IList<ParameterExpression> mappingVariables = mappingBlock.Variables; - mappingBlock = (BlockExpression)mappingExpressions[0]; - mappingVariables = mappingVariables.Append(mappingBlock.Variables); - mapping = mappingBlock.Update(mappingVariables, mappingBlock.Expressions); + mapping = mappingBlock.Update(variables, mappingBlock.Expressions); return true; } - mapping = mappingBlock.Variables.Any() - ? Expression.Block(mappingBlock.Variables, mappingExpressions) + mapping = variables.Any() + ? Expression.Block(variables, mappingExpressions) : mappingExpressions.ToExpression(); return true; } - private static IList<Expression> GetMappingExpressions(Expression mapping) + private static IList<Expression> GetMappingExpressions( + Expression mapping, + out List<ParameterExpression> variables) { - var expressions = new List<Expression>(); + var expressions = default(List<Expression>); + variables = new List<ParameterExpression>(); while (mapping.NodeType == Block) { var mappingBlock = (BlockExpression)mapping; - expressions.AddRange(mappingBlock.Expressions.Take(mappingBlock.Expressions.Count - 1)); - mapping = mappingBlock.Result; + if (mappingBlock.Variables.Any()) + { + variables.AddRange(mappingBlock.Variables); + } + + var blockExpressions = mappingBlock.Expressions; + + if (mappingBlock.Result.NodeType is Block) + { + expressions ??= new List<Expression>(); + expressions.AddRange(blockExpressions.Take(blockExpressions.Count - 1)); + mapping = mappingBlock.Result; + continue; + } + + if (expressions == null) + { + return blockExpressions; + } + + expressions.AddRange(blockExpressions); + return expressions; } return expressions; diff --git a/AgileMapper/ObjectPopulation/DerivedSourceTypeCheck.cs b/AgileMapper/ObjectPopulation/DerivedSourceTypeCheck.cs index a8d4734b4..ea9d1873c 100644 --- a/AgileMapper/ObjectPopulation/DerivedSourceTypeCheck.cs +++ b/AgileMapper/ObjectPopulation/DerivedSourceTypeCheck.cs @@ -8,6 +8,7 @@ namespace AgileObjects.AgileMapper.ObjectPopulation #endif using Extensions.Internal; using Members; + using ReadableExpressions.Extensions; internal class DerivedSourceTypeCheck { diff --git a/AgileMapper/ObjectPopulation/DictionaryMappingExpressionFactory.cs b/AgileMapper/ObjectPopulation/DictionaryMappingExpressionFactory.cs index d1e2021b1..cf15e54be 100644 --- a/AgileMapper/ObjectPopulation/DictionaryMappingExpressionFactory.cs +++ b/AgileMapper/ObjectPopulation/DictionaryMappingExpressionFactory.cs @@ -475,9 +475,7 @@ private Expression GetDictionaryPopulation(IObjectMappingData mappingData) return memberPopulations[0]; } - var memberPopulationBlock = Expression.Block(memberPopulations); - - return memberPopulationBlock; + return Expression.Block(memberPopulations); } private static Expression GetEnumerableToDictionaryMapping(IObjectMappingData mappingData) diff --git a/AgileMapper/ObjectPopulation/MappingExpressionFactoryBase.cs b/AgileMapper/ObjectPopulation/MappingExpressionFactoryBase.cs index 45f657f44..286833e58 100644 --- a/AgileMapper/ObjectPopulation/MappingExpressionFactoryBase.cs +++ b/AgileMapper/ObjectPopulation/MappingExpressionFactoryBase.cs @@ -406,25 +406,28 @@ private Expression GetMappingBlock(MappingCreationContext context) return firstExpression; } - Expression returnExpression; - - if (firstExpression.NodeType != Block) + if (TryAdjustForUnusedLocalVariableIfApplicable( + context, + out var ignoreVariable, + out var returnExpression)) { - if (TryAdjustForUnusedLocalVariableIfApplicable(context, out returnExpression)) - { - return returnExpression; - } + return returnExpression; } - else if (TryAdjustForUnusedLocalVariableIfApplicable(context, out returnExpression)) + + var localVariableUnused = ignoreVariable == true; + var mapperData = context.MapperData; + returnExpression = GetExpressionToReturn(context); + + if (localVariableUnused && (returnExpression == mapperData.LocalVariable)) { - return returnExpression; + returnExpression = mapperData.LocalVariable.Type.ToDefaultExpression(); } - mappingExpressions.Add(context.MapperData.GetReturnLabel(GetExpressionToReturn(context))); + mappingExpressions.Add(mapperData.GetReturnLabel(returnExpression)); - var mappingBlock = context.MapperData.Context.UseLocalVariable - ? Expression.Block(new[] { context.MapperData.LocalVariable }, mappingExpressions) - : mappingExpressions.ToExpression(); + var mappingBlock = localVariableUnused || !mapperData.Context.UseLocalVariable + ? mappingExpressions.ToExpression() + : Expression.Block(new[] { mapperData.LocalVariable }, mappingExpressions); return mappingBlock; } @@ -445,22 +448,32 @@ private static void AdjustForSingleExpressionBlockIfApplicable(MappingCreationCo } } - private static bool TryAdjustForUnusedLocalVariableIfApplicable(MappingCreationContext context, out Expression returnExpression) + private static bool TryAdjustForUnusedLocalVariableIfApplicable( + MappingCreationContext context, + out bool? ignoreVariable, + out Expression returnExpression) { - if (!context.MapperData.Context.UseLocalVariable) + if (!context.MapperData.Context.UseLocalVariable || + context.ToTargetDataSources.Any()) { + ignoreVariable = null; returnExpression = null; return false; } var mappingExpressions = context.MappingExpressions; - if (!mappingExpressions.TryGetVariableAssignment(out var localVariableAssignment)) + if (!mappingExpressions.TryGetAssignment( + context.MapperData.LocalVariable, + out var localVariableAssignment)) { + ignoreVariable = true; returnExpression = null; return false; } + ignoreVariable = false; + if ((localVariableAssignment.Left.NodeType != Parameter) || (localVariableAssignment != mappingExpressions.Last())) { diff --git a/AgileMapper/TypeConversion/TryParseConverter.cs b/AgileMapper/TypeConversion/TryParseConverter.cs index a19be8f8a..3facab1cf 100644 --- a/AgileMapper/TypeConversion/TryParseConverter.cs +++ b/AgileMapper/TypeConversion/TryParseConverter.cs @@ -9,6 +9,7 @@ namespace AgileObjects.AgileMapper.TypeConversion using System.Reflection; using Extensions.Internal; using NetStandardPolyfills; + using ReadableExpressions.Extensions; internal class TryParseConverter : IValueConverter { From e96c914cccc5d69e57903fd6793e40d7d181978e Mon Sep 17 00:00:00 2001 From: Steve Wilkes <steve@agileobjects.co.uk> Date: Thu, 17 Jun 2021 20:49:19 +0100 Subject: [PATCH 47/65] Removing more unused local variable conditions --- .../WhenBuildingDictionaryCreateNewMappers.cs | 65 +++++++++---------- .../Mappers/DateTimeHashSetMapper.cs | 12 ++-- .../Mappers/DecimalArrayMapper.cs | 12 ++-- .../IntAddressPublicTwoFieldsMapper.cs | 65 +++++++++++++++++++ .../Mappers/IntArrayMapper.cs | 10 +-- .../Mappers/IntIEnumerableMapper.cs | 12 ++-- .../Mappers/Mapper.cs | 8 +++ .../Mappers/ProductArrayMapper.cs | 22 +++---- .../Mappers/ProductDtoArrayMapper.cs | 18 ++--- .../Mappers/ProductDtoListMapper.cs | 14 ++-- .../Mappers/StringCollectionMapper.cs | 12 ++-- .../Mappers/StringListMapper.cs | 16 ++--- .../Mappers/StringPublicFieldMapper.cs | 20 +++--- .../Mappers/StringPublicPropertyMapper.cs | 12 ++-- .../WhenMappingToNewDictionaries.cs | 11 +++- .../Internal/ExpressionExtensions.cs | 14 +++- .../MappingCreationContext.cs | 7 +- .../MappingExpressionFactoryBase.cs | 16 +++-- .../ObjectPopulation/ObjectMapperData.cs | 14 ++-- 19 files changed, 228 insertions(+), 132 deletions(-) create mode 100644 AgileMapper.Buildable.UnitTests/Mappers/IntAddressPublicTwoFieldsMapper.cs diff --git a/AgileMapper.Buildable.UnitTests/Dictionaries/WhenBuildingDictionaryCreateNewMappers.cs b/AgileMapper.Buildable.UnitTests/Dictionaries/WhenBuildingDictionaryCreateNewMappers.cs index d97bd59b7..40fcc95b5 100644 --- a/AgileMapper.Buildable.UnitTests/Dictionaries/WhenBuildingDictionaryCreateNewMappers.cs +++ b/AgileMapper.Buildable.UnitTests/Dictionaries/WhenBuildingDictionaryCreateNewMappers.cs @@ -3,44 +3,39 @@ using System.Collections.Generic; using AgileMapper.UnitTests.Common; using AgileMapper.UnitTests.Common.TestClasses; + using Configuration; using Xunit; + using GeneratedMapper = Mappers.Mapper; public class WhenBuildingDictionaryCreateNewMappers { - //[Fact] - //public void ShouldBuildANestedComplexTypeToStringDictionaryMapper() - //{ - // using (var mapper = Mapper.CreateNew()) - // { - // mapper.GetPlanFor<PublicTwoFields<int, Address>>().ToANew<Dictionary<string, string>>(); - - // var sourceCodeExpressions = mapper.GetPlanSourceCodeInCache(); - - // var staticMapperClass = sourceCodeExpressions - // .ShouldCompileAStaticMapperClass(); - - // var staticMapMethod = staticMapperClass - // .GetMapMethods() - // .ShouldHaveSingleItem(); - - // var source = new PublicTwoFields<int, Address> - // { - // Value1 = 12345, - // Value2 = new Address { Line1 = "Line 1!", Line2 = "Line 2!" } - // }; - - // var executor = staticMapMethod - // .ShouldCreateMappingExecutor(source); - - // var result = executor - // .ShouldHaveACreateNewMethod() - // .ShouldExecuteACreateNewMapping<Dictionary<string, string>>(); - - // result.ShouldNotBeNull(); - // result.ShouldContainKeyAndValue("Value1", "12345"); - // result.ShouldContainKeyAndValue("Value2.Line1", "Line 1!"); - // result.ShouldContainKeyAndValue("Value2.Line2", "Line 2!"); - // } - //} + [Fact] + public void ShouldBuildANestedComplexTypeToStringDictionaryMapper() + { + var source = new PublicTwoFields<int, Address> + { + Value1 = 12345, + Value2 = new Address { Line1 = "Line 1!", Line2 = "Line 2!" } + }; + + var result = GeneratedMapper.Map(source).ToANew<Dictionary<string, string>>(); + + result.ShouldNotBeNull(); + result.ShouldContainKeyAndValue("Value1", "12345"); + result.ShouldContainKeyAndValue("Value2.Line1", "Line 1!"); + result.ShouldContainKeyAndValue("Value2.Line2", "Line 2!"); + } + + #region Configuration + + public class DictionaryCreateNewMapperConfiguration : BuildableMapperConfiguration + { + protected override void Configure() + { + GetPlanFor<PublicTwoFields<int, Address>>().ToANew<Dictionary<string, string>>(); + } + } + + #endregion } } diff --git a/AgileMapper.Buildable.UnitTests/Mappers/DateTimeHashSetMapper.cs b/AgileMapper.Buildable.UnitTests/Mappers/DateTimeHashSetMapper.cs index 689ffe84a..a01419066 100644 --- a/AgileMapper.Buildable.UnitTests/Mappers/DateTimeHashSetMapper.cs +++ b/AgileMapper.Buildable.UnitTests/Mappers/DateTimeHashSetMapper.cs @@ -43,15 +43,15 @@ public TTarget ToANew<TTarget>() private static DateTime[] CreateNew ( - IObjectMappingData<HashSet<DateTime>, DateTime[]> dtsToDtaData + IObjectMappingData<HashSet<DateTime>, DateTime[]> dthsToDtaData ) { try { - var sourceDateTimes = dtsToDtaData.Source; - var targetDateTimes = new List<DateTime>(sourceDateTimes.Count); + var sourceDateTimeHashSet = dthsToDtaData.Source; + var targetDateTimeList = new List<DateTime>(sourceDateTimeHashSet.Count); var i = 0; - var enumerator = sourceDateTimes.GetEnumerator(); + var enumerator = sourceDateTimeHashSet.GetEnumerator(); try { while (true) @@ -61,7 +61,7 @@ IObjectMappingData<HashSet<DateTime>, DateTime[]> dtsToDtaData break; } - targetDateTimes.Add(enumerator.Current); + targetDateTimeList.Add(enumerator.Current); ++i; } } @@ -70,7 +70,7 @@ IObjectMappingData<HashSet<DateTime>, DateTime[]> dtsToDtaData enumerator.Dispose(); } - return targetDateTimes.ToArray(); + return targetDateTimeList.ToArray(); } catch (Exception ex) { diff --git a/AgileMapper.Buildable.UnitTests/Mappers/DecimalArrayMapper.cs b/AgileMapper.Buildable.UnitTests/Mappers/DecimalArrayMapper.cs index 84a632564..b5576c953 100644 --- a/AgileMapper.Buildable.UnitTests/Mappers/DecimalArrayMapper.cs +++ b/AgileMapper.Buildable.UnitTests/Mappers/DecimalArrayMapper.cs @@ -38,15 +38,15 @@ HashSet<double> target private static HashSet<double> Merge ( - IObjectMappingData<decimal[], HashSet<double>> daToDsData + IObjectMappingData<decimal[], HashSet<double>> daToDhsData ) { try { - var sourceDoubles = daToDsData.Source.Project(d => (double)d).Exclude(daToDsData.Target); - var targetDoubles = daToDsData.Target; + var sourceDoubleIEnumerable = daToDhsData.Source.Project(d => (double)d).Exclude(daToDhsData.Target); + var targetDoubleHashSet = daToDhsData.Target; var i = 0; - var enumerator = sourceDoubles.GetEnumerator(); + var enumerator = sourceDoubleIEnumerable.GetEnumerator(); try { while (true) @@ -56,7 +56,7 @@ IObjectMappingData<decimal[], HashSet<double>> daToDsData break; } - targetDoubles.Add(enumerator.Current); + targetDoubleHashSet.Add(enumerator.Current); ++i; } } @@ -65,7 +65,7 @@ IObjectMappingData<decimal[], HashSet<double>> daToDsData enumerator.Dispose(); } - return targetDoubles; + return targetDoubleHashSet; } catch (Exception ex) { diff --git a/AgileMapper.Buildable.UnitTests/Mappers/IntAddressPublicTwoFieldsMapper.cs b/AgileMapper.Buildable.UnitTests/Mappers/IntAddressPublicTwoFieldsMapper.cs new file mode 100644 index 000000000..e028d9ace --- /dev/null +++ b/AgileMapper.Buildable.UnitTests/Mappers/IntAddressPublicTwoFieldsMapper.cs @@ -0,0 +1,65 @@ +// ------------------------------------------------------------------------------ +// <auto-generated> +// This code was generated by AgileObjects.AgileMapper.Buildable. +// Runtime Version: 0.1.0.0 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// </auto-generated> +// ------------------------------------------------------------------------------ + +using System; +using System.CodeDom.Compiler; +using System.Collections.Generic; +using AgileObjects.AgileMapper; +using AgileObjects.AgileMapper.ObjectPopulation; +using AgileObjects.AgileMapper.UnitTests.Common.TestClasses; + +namespace AgileObjects.AgileMapper.Buildable.UnitTests.Mappers +{ + [GeneratedCode("AgileObjects.AgileMapper.Buildable", "0.1.0.0")] + public class IntAddressPublicTwoFieldsMapper : MappingExecutionContextBase<PublicTwoFields<int, Address>> + { + public IntAddressPublicTwoFieldsMapper + ( + PublicTwoFields<int, Address> source + ) + : base(source) + { + } + + public Dictionary<string, string> ToANew<TTarget>() + where TTarget : Dictionary<string, string> + { + return IntAddressPublicTwoFieldsMapper.CreateNew(this.CreateRootMappingData(default(Dictionary<string, string>))); + } + + private static Dictionary<string, string> CreateNew + ( + IObjectMappingData<PublicTwoFields<int, Address>, Dictionary<string, string>> iaptfToSsdData + ) + { + try + { + var stringStringDictionary = new Dictionary<string, string>(); + stringStringDictionary["Value1"] = iaptfToSsdData.Source.Value1.ToString(); + + if (iaptfToSsdData.Source.Value2 != null) + { + stringStringDictionary["Value2.Line1"] = iaptfToSsdData.Source.Value2.Line1; + stringStringDictionary["Value2.Line2"] = iaptfToSsdData.Source.Value2.Line2; + } + + return stringStringDictionary; + } + catch (Exception ex) + { + throw MappingException.For( + "CreateNew", + "PublicTwoFields<int, Address>", + "Dictionary<string, string>", + ex); + } + } + } +} \ No newline at end of file diff --git a/AgileMapper.Buildable.UnitTests/Mappers/IntArrayMapper.cs b/AgileMapper.Buildable.UnitTests/Mappers/IntArrayMapper.cs index f638eef22..496a2e484 100644 --- a/AgileMapper.Buildable.UnitTests/Mappers/IntArrayMapper.cs +++ b/AgileMapper.Buildable.UnitTests/Mappers/IntArrayMapper.cs @@ -36,13 +36,13 @@ public ReadOnlyCollection<int> ToANew<TTarget>() private static ReadOnlyCollection<int> CreateNew ( - IObjectMappingData<int[], ReadOnlyCollection<int>> iaToIsData + IObjectMappingData<int[], ReadOnlyCollection<int>> iaToIrocData ) { try { - var sourceIntArray = iaToIsData.Source; - var targetInts = new List<int>(sourceIntArray.Length); + var sourceIntArray = iaToIrocData.Source; + var targetIntList = new List<int>(sourceIntArray.Length); var i = 0; while (true) { @@ -51,11 +51,11 @@ IObjectMappingData<int[], ReadOnlyCollection<int>> iaToIsData break; } - targetInts.Add(sourceIntArray[i]); + targetIntList.Add(sourceIntArray[i]); ++i; } - return targetInts.AsReadOnly(); + return targetIntList.AsReadOnly(); } catch (Exception ex) { diff --git a/AgileMapper.Buildable.UnitTests/Mappers/IntIEnumerableMapper.cs b/AgileMapper.Buildable.UnitTests/Mappers/IntIEnumerableMapper.cs index cbdd6b3e2..f9fe81aa9 100644 --- a/AgileMapper.Buildable.UnitTests/Mappers/IntIEnumerableMapper.cs +++ b/AgileMapper.Buildable.UnitTests/Mappers/IntIEnumerableMapper.cs @@ -38,15 +38,15 @@ ICollection<int> target private static ICollection<int> Merge ( - IObjectMappingData<IEnumerable<int>, ICollection<int>> isToIsData + IObjectMappingData<IEnumerable<int>, ICollection<int>> iieToIicData ) { try { - var sourceInts = isToIsData.Source.Exclude(isToIsData.Target); - ICollection<int> targetInts = isToIsData.Target.IsReadOnly ? new List<int>(isToIsData.Target) : isToIsData.Target; + var sourceIntIEnumerable = iieToIicData.Source.Exclude(iieToIicData.Target); + ICollection<int> targetIntICollection = iieToIicData.Target.IsReadOnly ? new List<int>(iieToIicData.Target) : iieToIicData.Target; var i = 0; - var enumerator = sourceInts.GetEnumerator(); + var enumerator = sourceIntIEnumerable.GetEnumerator(); try { while (true) @@ -56,7 +56,7 @@ IObjectMappingData<IEnumerable<int>, ICollection<int>> isToIsData break; } - targetInts.Add(enumerator.Current); + targetIntICollection.Add(enumerator.Current); ++i; } } @@ -65,7 +65,7 @@ IObjectMappingData<IEnumerable<int>, ICollection<int>> isToIsData enumerator.Dispose(); } - return targetInts; + return targetIntICollection; } catch (Exception ex) { diff --git a/AgileMapper.Buildable.UnitTests/Mappers/Mapper.cs b/AgileMapper.Buildable.UnitTests/Mappers/Mapper.cs index 8f5ec6087..91afb8173 100644 --- a/AgileMapper.Buildable.UnitTests/Mappers/Mapper.cs +++ b/AgileMapper.Buildable.UnitTests/Mappers/Mapper.cs @@ -59,6 +59,14 @@ decimal[] source return new DecimalArrayMapper(source); } + public static IntAddressPublicTwoFieldsMapper Map + ( + PublicTwoFields<int, Address> source + ) + { + return new IntAddressPublicTwoFieldsMapper(source); + } + public static IntArrayMapper Map ( int[] source diff --git a/AgileMapper.Buildable.UnitTests/Mappers/ProductArrayMapper.cs b/AgileMapper.Buildable.UnitTests/Mappers/ProductArrayMapper.cs index 5b2d9a0cc..faba24256 100644 --- a/AgileMapper.Buildable.UnitTests/Mappers/ProductArrayMapper.cs +++ b/AgileMapper.Buildable.UnitTests/Mappers/ProductArrayMapper.cs @@ -39,18 +39,18 @@ IEnumerable<Product> target private static IEnumerable<Product> Merge ( - IObjectMappingData<Product[], IEnumerable<Product>> paToPsData + IObjectMappingData<Product[], IEnumerable<Product>> paToPieData ) { try { - var collectionData = CollectionData.Create(paToPsData.Source, paToPsData.Target, p => p.ProductId); + var collectionData = CollectionData.Create(paToPieData.Source, paToPieData.Target, p => p.ProductId); collectionData.Intersection.ForEach( (existingSourceProduct, existingTargetProduct, idx) => ProductArrayMapper.GetProduct1(existingSourceProduct, existingTargetProduct)); - var sourceProducts = collectionData.NewSourceItems; - var targetProducts = ProductArrayMapper.GetProductICollection(paToPsData); + var sourceProductIEnumerable = collectionData.NewSourceItems; + var targetProductICollection = ProductArrayMapper.GetProductICollection(paToPieData); var i = 0; - var enumerator = sourceProducts.GetEnumerator(); + var enumerator = sourceProductIEnumerable.GetEnumerator(); try { while (true) @@ -60,7 +60,7 @@ IObjectMappingData<Product[], IEnumerable<Product>> paToPsData break; } - targetProducts.Add(ProductArrayMapper.GetProduct2(enumerator)); + targetProductICollection.Add(ProductArrayMapper.GetProduct2(enumerator)); ++i; } } @@ -69,7 +69,7 @@ IObjectMappingData<Product[], IEnumerable<Product>> paToPsData enumerator.Dispose(); } - return targetProducts; + return targetProductICollection; } catch (Exception ex) { @@ -201,13 +201,13 @@ Product existingSourceProduct private static ICollection<Product> GetProductICollection ( - IObjectMappingData<Product[], IEnumerable<Product>> paToPsData + IObjectMappingData<Product[], IEnumerable<Product>> paToPieData ) { ICollection<Product> collection; - return ((collection = paToPsData.Target as ICollection<Product>) != null) - ? collection.IsReadOnly ? new List<Product>(paToPsData.Target) : collection - : new List<Product>(paToPsData.Target); + return ((collection = paToPieData.Target as ICollection<Product>) != null) + ? collection.IsReadOnly ? new List<Product>(paToPieData.Target) : collection + : new List<Product>(paToPieData.Target); } private static Product GetProduct2 diff --git a/AgileMapper.Buildable.UnitTests/Mappers/ProductDtoArrayMapper.cs b/AgileMapper.Buildable.UnitTests/Mappers/ProductDtoArrayMapper.cs index 34c433e4c..b6808fb20 100644 --- a/AgileMapper.Buildable.UnitTests/Mappers/ProductDtoArrayMapper.cs +++ b/AgileMapper.Buildable.UnitTests/Mappers/ProductDtoArrayMapper.cs @@ -40,23 +40,23 @@ ReadOnlyCollection<Product> target private static ReadOnlyCollection<Product> Overwrite ( - IObjectMappingData<ProductDto[], ReadOnlyCollection<Product>> pdaToPsData + IObjectMappingData<ProductDto[], ReadOnlyCollection<Product>> pdaToProcData ) { try { var collectionData = CollectionData.Create( - pdaToPsData.Source, - pdaToPsData.Target, + pdaToProcData.Source, + pdaToProcData.Target, pd => pd.ProductId, p => p.ProductId); collectionData.Intersection.ForEach( (existingProductDto, existingProduct, idx) => ProductDtoArrayMapper.GetProduct1(existingProductDto, existingProduct)); - var productDtos = collectionData.NewSourceItems; - var products = new List<Product>(pdaToPsData.Target); - collectionData.AbsentTargetItems.ForEach(p => products.Remove(p)); + var productDtoIEnumerable = collectionData.NewSourceItems; + var productList = new List<Product>(pdaToProcData.Target); + collectionData.AbsentTargetItems.ForEach(p => productList.Remove(p)); var i = 0; - var enumerator = productDtos.GetEnumerator(); + var enumerator = productDtoIEnumerable.GetEnumerator(); try { while (true) @@ -66,7 +66,7 @@ IObjectMappingData<ProductDto[], ReadOnlyCollection<Product>> pdaToPsData break; } - products.Add(ProductDtoArrayMapper.GetProduct1(enumerator)); + productList.Add(ProductDtoArrayMapper.GetProduct1(enumerator)); ++i; } } @@ -75,7 +75,7 @@ IObjectMappingData<ProductDto[], ReadOnlyCollection<Product>> pdaToPsData enumerator.Dispose(); } - return products.AsReadOnly(); + return productList.AsReadOnly(); } catch (Exception ex) { diff --git a/AgileMapper.Buildable.UnitTests/Mappers/ProductDtoListMapper.cs b/AgileMapper.Buildable.UnitTests/Mappers/ProductDtoListMapper.cs index 4af58c3d9..e073f7878 100644 --- a/AgileMapper.Buildable.UnitTests/Mappers/ProductDtoListMapper.cs +++ b/AgileMapper.Buildable.UnitTests/Mappers/ProductDtoListMapper.cs @@ -36,27 +36,27 @@ public IList<ProductDto> ToANew<TTarget>() private static IList<ProductDto> CreateNew ( - IObjectMappingData<List<ProductDto>, IList<ProductDto>> pdsToPdsData + IObjectMappingData<List<ProductDto>, IList<ProductDto>> pdlToPdilData ) { try { - var sourceProductDtos = pdsToPdsData.Source; - var targetProductDtos = new List<ProductDto>(sourceProductDtos.Count); + var sourceProductDtoList = pdlToPdilData.Source; + var targetProductDtoList = new List<ProductDto>(sourceProductDtoList.Count); var i = 0; while (true) { - if (i == sourceProductDtos.Count) + if (i == sourceProductDtoList.Count) { break; } - var sourceProductDto = sourceProductDtos[i]; - targetProductDtos.Add(ProductDtoListMapper.GetProductDto(sourceProductDto)); + var sourceProductDto = sourceProductDtoList[i]; + targetProductDtoList.Add(ProductDtoListMapper.GetProductDto(sourceProductDto)); ++i; } - return targetProductDtos; + return targetProductDtoList; } catch (Exception ex) { diff --git a/AgileMapper.Buildable.UnitTests/Mappers/StringCollectionMapper.cs b/AgileMapper.Buildable.UnitTests/Mappers/StringCollectionMapper.cs index 069a540c2..59b82670e 100644 --- a/AgileMapper.Buildable.UnitTests/Mappers/StringCollectionMapper.cs +++ b/AgileMapper.Buildable.UnitTests/Mappers/StringCollectionMapper.cs @@ -38,17 +38,17 @@ List<string> target private static List<string> Overwrite ( - IObjectMappingData<Collection<string>, List<string>> ssToSsData + IObjectMappingData<Collection<string>, List<string>> scToSlData ) { try { - var sourceStrings = ssToSsData.Source; - var targetStrings = ssToSsData.Target; - targetStrings.Clear(); - targetStrings.AddRange(sourceStrings); + var sourceStringCollection = scToSlData.Source; + var targetStringList = scToSlData.Target; + targetStringList.Clear(); + targetStringList.AddRange(sourceStringCollection); - return targetStrings; + return targetStringList; } catch (Exception ex) { diff --git a/AgileMapper.Buildable.UnitTests/Mappers/StringListMapper.cs b/AgileMapper.Buildable.UnitTests/Mappers/StringListMapper.cs index e153c615b..f210f24f2 100644 --- a/AgileMapper.Buildable.UnitTests/Mappers/StringListMapper.cs +++ b/AgileMapper.Buildable.UnitTests/Mappers/StringListMapper.cs @@ -36,26 +36,26 @@ List<string> source private static Collection<byte?> CreateNew ( - IObjectMappingData<List<string>, Collection<byte?>> ssToNbsData + IObjectMappingData<List<string>, Collection<byte?>> slToNbcData ) { try { - var strings = ssToNbsData.Source; - var nullableBytes = new Collection<byte?>(); + var stringList = slToNbcData.Source; + var nullableByteCollection = new Collection<byte?>(); var i = 0; while (true) { - if (i == strings.Count) + if (i == stringList.Count) { break; } - nullableBytes.Add(StringListMapper.GetNullableByte(strings, i)); + nullableByteCollection.Add(StringListMapper.GetNullableByte(stringList, i)); ++i; } - return nullableBytes; + return nullableByteCollection; } catch (Exception ex) { @@ -69,12 +69,12 @@ private static Collection<byte?> CreateNew private static byte? GetNullableByte ( - List<string> strings, + List<string> stringList, int i ) { byte byteValue; - return byte.TryParse(strings[i], out byteValue) ? (byte?)byteValue : null; + return byte.TryParse(stringList[i], out byteValue) ? (byte?)byteValue : null; } } } \ No newline at end of file diff --git a/AgileMapper.Buildable.UnitTests/Mappers/StringPublicFieldMapper.cs b/AgileMapper.Buildable.UnitTests/Mappers/StringPublicFieldMapper.cs index 568882308..f3b5eb6c2 100644 --- a/AgileMapper.Buildable.UnitTests/Mappers/StringPublicFieldMapper.cs +++ b/AgileMapper.Buildable.UnitTests/Mappers/StringPublicFieldMapper.cs @@ -48,15 +48,15 @@ public TTarget ToANew<TTarget>() private static PublicField<int> CreateNew ( - IObjectMappingData<PublicField<string>, PublicField<int>> pfsToPfiData + IObjectMappingData<PublicField<string>, PublicField<int>> spfToIpfData ) { try { - var publicField_Int = new PublicField<int>(); - publicField_Int.Value = StringPublicFieldMapper.GetInt(pfsToPfiData); + var intPublicField = new PublicField<int>(); + intPublicField.Value = StringPublicFieldMapper.GetInt(spfToIpfData); - return publicField_Int; + return intPublicField; } catch (Exception ex) { @@ -70,15 +70,15 @@ IObjectMappingData<PublicField<string>, PublicField<int>> pfsToPfiData private static PublicProperty<string> CreateNew ( - IObjectMappingData<PublicField<string>, PublicProperty<string>> pfsToPpsData + IObjectMappingData<PublicField<string>, PublicProperty<string>> spfToSppData ) { try { - var publicProperty_String = new PublicProperty<string>(); - publicProperty_String.Value = pfsToPpsData.Source.Value; + var stringPublicProperty = new PublicProperty<string>(); + stringPublicProperty.Value = spfToSppData.Source.Value; - return publicProperty_String; + return stringPublicProperty; } catch (Exception ex) { @@ -92,11 +92,11 @@ IObjectMappingData<PublicField<string>, PublicProperty<string>> pfsToPpsData private static int GetInt ( - IObjectMappingData<PublicField<string>, PublicField<int>> pfsToPfiData + IObjectMappingData<PublicField<string>, PublicField<int>> spfToIpfData ) { int intValue; - return int.TryParse(pfsToPfiData.Source.Value, out intValue) ? intValue : default(int); + return int.TryParse(spfToIpfData.Source.Value, out intValue) ? intValue : default(int); } } } \ No newline at end of file diff --git a/AgileMapper.Buildable.UnitTests/Mappers/StringPublicPropertyMapper.cs b/AgileMapper.Buildable.UnitTests/Mappers/StringPublicPropertyMapper.cs index 9b5cfcd5b..db265810d 100644 --- a/AgileMapper.Buildable.UnitTests/Mappers/StringPublicPropertyMapper.cs +++ b/AgileMapper.Buildable.UnitTests/Mappers/StringPublicPropertyMapper.cs @@ -35,15 +35,15 @@ public PublicField<int> ToANew<TTarget>() private static PublicField<int> CreateNew ( - IObjectMappingData<PublicProperty<string>, PublicField<int>> ppsToPfiData + IObjectMappingData<PublicProperty<string>, PublicField<int>> sppToIpfData ) { try { - var publicField_Int = new PublicField<int>(); - publicField_Int.Value = StringPublicPropertyMapper.GetInt(ppsToPfiData); + var intPublicField = new PublicField<int>(); + intPublicField.Value = StringPublicPropertyMapper.GetInt(sppToIpfData); - return publicField_Int; + return intPublicField; } catch (Exception ex) { @@ -57,11 +57,11 @@ IObjectMappingData<PublicProperty<string>, PublicField<int>> ppsToPfiData private static int GetInt ( - IObjectMappingData<PublicProperty<string>, PublicField<int>> ppsToPfiData + IObjectMappingData<PublicProperty<string>, PublicField<int>> sppToIpfData ) { int intValue; - return int.TryParse(ppsToPfiData.Source.Value, out intValue) ? intValue : default(int); + return int.TryParse(sppToIpfData.Source.Value, out intValue) ? intValue : default(int); } } } \ No newline at end of file diff --git a/AgileMapper.UnitTests/Dictionaries/WhenMappingToNewDictionaries.cs b/AgileMapper.UnitTests/Dictionaries/WhenMappingToNewDictionaries.cs index 3dad6eb0b..efb458674 100644 --- a/AgileMapper.UnitTests/Dictionaries/WhenMappingToNewDictionaries.cs +++ b/AgileMapper.UnitTests/Dictionaries/WhenMappingToNewDictionaries.cs @@ -48,10 +48,17 @@ public void ShouldMapAComplexTypeMemberToATypedDictionary() [Fact] public void ShouldMapAComplexTypeMemberToAnUntypedDictionary() { - var source = new PublicField<Address> { Value = new Address { Line1 = "One!" } }; + var source = new PublicTwoFields<int, Address> + { + Value1 = 123, + Value2 = new Address { Line1 = "One!" } + }; + var result = Mapper.Map(source).ToANew<Dictionary<string, string>>(); - result.ShouldContainKeyAndValue("Value.Line1", "One!"); + result.ShouldContainKeyAndValue("Value1", "123"); + result.ShouldContainKeyAndValue("Value2.Line1", "One!"); + result.ShouldContainKeyAndValue("Value2.Line2", null); } [Fact] diff --git a/AgileMapper/Extensions/Internal/ExpressionExtensions.cs b/AgileMapper/Extensions/Internal/ExpressionExtensions.cs index 94b4533df..016f6eb0e 100644 --- a/AgileMapper/Extensions/Internal/ExpressionExtensions.cs +++ b/AgileMapper/Extensions/Internal/ExpressionExtensions.cs @@ -322,16 +322,24 @@ public static Expression GetRootExpression(this Expression expression) public static Expression ToExpression(this IList<Expression> expressions) => expressions.HasOne() ? expressions.First() : Expression.Block(expressions); - public static IList<Expression> GetMemberMappingExpressions(this IList<Expression> mappingExpressions) - => mappingExpressions.Filter(IsMemberMapping).ToList(); + public static IEnumerable<Expression> EnumerateMappingExpressions( + this IList<Expression> mappingExpressions, + bool includeCallbacks) + { + return mappingExpressions + .Filter(includeCallbacks, (inc, exp) => IsMappingExpression(exp, inc)); + } - private static bool IsMemberMapping(Expression expression) + private static bool IsMappingExpression(Expression expression, bool includeCallbacks) { switch (expression.NodeType) { case Constant: return false; + case Invoke: + return includeCallbacks; + case Call when ( IsCallTo(nameof(IObjectMappingDataUntyped.Register), expression) || IsCallTo(nameof(IObjectMappingDataUntyped.TryGet), expression)): diff --git a/AgileMapper/ObjectPopulation/MappingCreationContext.cs b/AgileMapper/ObjectPopulation/MappingCreationContext.cs index 1a785e84b..3265ab716 100644 --- a/AgileMapper/ObjectPopulation/MappingCreationContext.cs +++ b/AgileMapper/ObjectPopulation/MappingCreationContext.cs @@ -1,6 +1,7 @@ namespace AgileObjects.AgileMapper.ObjectPopulation { using System.Collections.Generic; + using System.Linq; #if NET35 using Microsoft.Scripting.Ast; #else @@ -79,9 +80,13 @@ public IList<Expression> GetMemberMappingExpressions() return _memberMappingExpressions ?? Enumerable<Expression>.EmptyArray; } - return _memberMappingExpressions = MappingExpressions.GetMemberMappingExpressions(); + return _memberMappingExpressions = + EnumerateMappingExpressions(includeCallbacks: true).ToList(); } + public IEnumerable<Expression> EnumerateMappingExpressions(bool includeCallbacks) + => MappingExpressions.EnumerateMappingExpressions(includeCallbacks); + public MappingCreationContext WithToTargetDataSource(IDataSource dataSource) { var newSourceMappingData = MappingData.WithToTargetSource(dataSource.SourceMember); diff --git a/AgileMapper/ObjectPopulation/MappingExpressionFactoryBase.cs b/AgileMapper/ObjectPopulation/MappingExpressionFactoryBase.cs index 286833e58..d81ce8c0e 100644 --- a/AgileMapper/ObjectPopulation/MappingExpressionFactoryBase.cs +++ b/AgileMapper/ObjectPopulation/MappingExpressionFactoryBase.cs @@ -418,12 +418,17 @@ private Expression GetMappingBlock(MappingCreationContext context) var mapperData = context.MapperData; returnExpression = GetExpressionToReturn(context); - if (localVariableUnused && (returnExpression == mapperData.LocalVariable)) + if (localVariableUnused && !mapperData.ReturnLabelUsed && + (returnExpression == mapperData.LocalVariable) && + context.EnumerateMappingExpressions(includeCallbacks: false).Any()) { - returnExpression = mapperData.LocalVariable.Type.ToDefaultExpression(); + mappingExpressions.Add(Constants.EmptyExpression); + } + else + { + mappingExpressions.Add(mapperData.GetReturnLabel(returnExpression)); + localVariableUnused = false; } - - mappingExpressions.Add(mapperData.GetReturnLabel(returnExpression)); var mappingBlock = localVariableUnused || !mapperData.Context.UseLocalVariable ? mappingExpressions.ToExpression() @@ -474,8 +479,7 @@ private static bool TryAdjustForUnusedLocalVariableIfApplicable( ignoreVariable = false; - if ((localVariableAssignment.Left.NodeType != Parameter) || - (localVariableAssignment != mappingExpressions.Last())) + if (localVariableAssignment != mappingExpressions.Last()) { returnExpression = null; return false; diff --git a/AgileMapper/ObjectPopulation/ObjectMapperData.cs b/AgileMapper/ObjectPopulation/ObjectMapperData.cs index d530bee5b..ceff43f05 100644 --- a/AgileMapper/ObjectPopulation/ObjectMapperData.cs +++ b/AgileMapper/ObjectPopulation/ObjectMapperData.cs @@ -28,7 +28,7 @@ internal class ObjectMapperData : MemberMapperDataBase, IMemberMapperData private static readonly MethodInfo _mapRepeatedElementMethod = typeof(IObjectMappingDataUntyped).GetPublicInstanceMethod("MapRepeated", parameterCount: 4); - private readonly LabelTarget _returnLabelTarget; + private LabelTarget _returnLabelTarget; private Expression _rootMappingDataObject; private ObjectMapperData _entryPointMapperData; private Expression _targetInstance; @@ -75,8 +75,7 @@ private ObjectMapperData( ElementKey = GetElementKeyAccess(); ParentObject = GetParentObjectAccess(); } - - _returnLabelTarget = Expression.Label(TargetType, "Return"); + _mappedObjectCachingMode = MapperContext.UserConfigurations.CacheMappedObjects(this); if (targetMember.IsEnumerable) @@ -668,7 +667,7 @@ public MethodCallExpression GetMapRepeatedCall( /// <see cref="ObjectMapperData" />'s LabelTarget. /// </returns> public Expression GetReturnExpression(Expression value) - => Expression.Return(_returnLabelTarget, value, TargetType); + => Expression.Return(ReturnLabelTarget, value, TargetType); /// <summary> /// Creates a LabelExpression for this <see cref="ObjectMapperData" />'s LabelTarget, with @@ -682,7 +681,12 @@ public Expression GetReturnExpression(Expression value) /// <paramref name="defaultValue"/>. /// </returns> public Expression GetReturnLabel(Expression defaultValue) - => Expression.Label(_returnLabelTarget, defaultValue); + => Expression.Label(ReturnLabelTarget, defaultValue); + + private LabelTarget ReturnLabelTarget + => _returnLabelTarget ??= Expression.Label(TargetType, "Return"); + + public bool ReturnLabelUsed => _returnLabelTarget != null; public IQualifiedMemberContext WithNoTargetMember() { From bca0acda3f2d45ad13f4ef3431d97189f604ba89 Mon Sep 17 00:00:00 2001 From: Steve Wilkes <steve@agileobjects.co.uk> Date: Fri, 18 Jun 2021 08:43:28 +0100 Subject: [PATCH 48/65] Tidying --- .../MappingExpressionFactoryBase.cs | 31 ++++++++++--------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/AgileMapper/ObjectPopulation/MappingExpressionFactoryBase.cs b/AgileMapper/ObjectPopulation/MappingExpressionFactoryBase.cs index d81ce8c0e..13e861dca 100644 --- a/AgileMapper/ObjectPopulation/MappingExpressionFactoryBase.cs +++ b/AgileMapper/ObjectPopulation/MappingExpressionFactoryBase.cs @@ -408,18 +408,16 @@ private Expression GetMappingBlock(MappingCreationContext context) if (TryAdjustForUnusedLocalVariableIfApplicable( context, - out var ignoreVariable, + out var localVariableUnused, out var returnExpression)) { return returnExpression; } - var localVariableUnused = ignoreVariable == true; var mapperData = context.MapperData; returnExpression = GetExpressionToReturn(context); - if (localVariableUnused && !mapperData.ReturnLabelUsed && - (returnExpression == mapperData.LocalVariable) && + if (localVariableUnused && (returnExpression == mapperData.LocalVariable) && context.EnumerateMappingExpressions(includeCallbacks: false).Any()) { mappingExpressions.Add(Constants.EmptyExpression); @@ -427,10 +425,10 @@ private Expression GetMappingBlock(MappingCreationContext context) else { mappingExpressions.Add(mapperData.GetReturnLabel(returnExpression)); - localVariableUnused = false; + localVariableUnused = !mapperData.Context.UseLocalVariable; } - var mappingBlock = localVariableUnused || !mapperData.Context.UseLocalVariable + var mappingBlock = localVariableUnused ? mappingExpressions.ToExpression() : Expression.Block(new[] { mapperData.LocalVariable }, mappingExpressions); @@ -455,13 +453,16 @@ private static void AdjustForSingleExpressionBlockIfApplicable(MappingCreationCo private static bool TryAdjustForUnusedLocalVariableIfApplicable( MappingCreationContext context, - out bool? ignoreVariable, + out bool localVariableUnused, out Expression returnExpression) { - if (!context.MapperData.Context.UseLocalVariable || + var mapperData = context.MapperData; + + if (!mapperData.Context.UseLocalVariable || + mapperData.ReturnLabelUsed || context.ToTargetDataSources.Any()) { - ignoreVariable = null; + localVariableUnused = false; returnExpression = null; return false; } @@ -469,15 +470,15 @@ private static bool TryAdjustForUnusedLocalVariableIfApplicable( var mappingExpressions = context.MappingExpressions; if (!mappingExpressions.TryGetAssignment( - context.MapperData.LocalVariable, - out var localVariableAssignment)) + mapperData.LocalVariable, + out var localVariableAssignment)) { - ignoreVariable = true; + localVariableUnused = true; returnExpression = null; return false; } - ignoreVariable = false; + localVariableUnused = false; if (localVariableAssignment != mappingExpressions.Last()) { @@ -489,7 +490,7 @@ private static bool TryAdjustForUnusedLocalVariableIfApplicable( returnExpression = (assignedValue.NodeType == Invoke) ? Expression.Block( - new[] { (ParameterExpression)localVariableAssignment.Left }, + new[] { mapperData.LocalVariable }, GetExpressionToReturn(localVariableAssignment, context)) : GetExpressionToReturn(assignedValue, context); @@ -498,7 +499,7 @@ private static bool TryAdjustForUnusedLocalVariableIfApplicable( return true; } - mappingExpressions[mappingExpressions.Count - 1] = context.MapperData.GetReturnLabel(returnExpression); + mappingExpressions[mappingExpressions.Count - 1] = mapperData.GetReturnLabel(returnExpression); returnExpression = Expression.Block(mappingExpressions); return true; } From 0c3458ade8d76fdc2581c1e808a18f7a45b2900f Mon Sep 17 00:00:00 2001 From: Steve Wilkes <steve@agileobjects.co.uk> Date: Sat, 19 Jun 2021 10:29:59 +0100 Subject: [PATCH 49/65] Test coverage for generated ToTarget data source mappers --- .../WhenBuildingToTargetDataSourceMappers.cs | 52 +++++ .../WhenBuildingDictionaryCreateNewMappers.cs | 2 +- .../IntStringIntToTargetValueSourceMapper.cs | 219 ++++++++++++++++++ .../Mappers/Mapper.cs | 9 + .../WhenBuildingCircularReferenceMappers.cs | 2 +- ...WhenBuildingComplexTypeCreateNewMappers.cs | 2 +- .../WhenBuildingComplexTypeMergeMappers.cs | 2 +- ...WhenBuildingComplexTypeOverwriteMappers.cs | 2 +- .../WhenBuildingDerivedTypeMappers.cs | 2 +- .../WhenBuildingEnumerableCreateNewMappers.cs | 2 +- .../WhenBuildingEnumerableMergeMappers.cs | 2 +- .../WhenBuildingEnumerableOverwriteMappers.cs | 2 +- .../WhenBuildingRootEnumMappers.cs | 2 +- 13 files changed, 290 insertions(+), 10 deletions(-) create mode 100644 AgileMapper.Buildable.UnitTests/Configuration/DataSources/WhenBuildingToTargetDataSourceMappers.cs create mode 100644 AgileMapper.Buildable.UnitTests/Mappers/IntStringIntToTargetValueSourceMapper.cs diff --git a/AgileMapper.Buildable.UnitTests/Configuration/DataSources/WhenBuildingToTargetDataSourceMappers.cs b/AgileMapper.Buildable.UnitTests/Configuration/DataSources/WhenBuildingToTargetDataSourceMappers.cs new file mode 100644 index 000000000..4204c3ebb --- /dev/null +++ b/AgileMapper.Buildable.UnitTests/Configuration/DataSources/WhenBuildingToTargetDataSourceMappers.cs @@ -0,0 +1,52 @@ +namespace AgileObjects.AgileMapper.Buildable.UnitTests.Configuration.DataSources +{ + using AgileMapper.UnitTests.Common; + using AgileMapper.UnitTests.Common.TestClasses; + using Buildable.Configuration; + using Xunit; + using GeneratedMapper = Mappers.Mapper; + + public class WhenBuildingToTargetDataSourceMappers + { + [Fact] + public void ShouldApplyAToTargetDataSource() + { + var source = new ToTargetValueSource<int, string, int> + { + Value1 = 123, + Value = new PublicTwoFields<string, int> + { + Value1 = "456", + Value2 = 789 + } + }; + + var result = GeneratedMapper.Map(source).ToANew<PublicTwoFields<int, int>>(); + + result.Value1.ShouldBe(456); + result.Value2.ShouldBe(789); + } + + #region Configuration + + public class ToTargetDataSourceMapperConfiguration : BuildableMapperConfiguration + { + protected override void Configure() + { + GetPlansFor<ToTargetValueSource<int, string, int>>() + .To<PublicTwoFields<int, int>>(cfg => cfg + .Map(ctx => ctx.Source.Value) + .ToTarget()); + } + } + + #endregion + } + + public class ToTargetValueSource<T1, T2, T3> + { + public T1 Value1 { get; set; } + + public PublicTwoFields<T2, T3> Value { get; set; } + } +} diff --git a/AgileMapper.Buildable.UnitTests/Dictionaries/WhenBuildingDictionaryCreateNewMappers.cs b/AgileMapper.Buildable.UnitTests/Dictionaries/WhenBuildingDictionaryCreateNewMappers.cs index 40fcc95b5..0106d144e 100644 --- a/AgileMapper.Buildable.UnitTests/Dictionaries/WhenBuildingDictionaryCreateNewMappers.cs +++ b/AgileMapper.Buildable.UnitTests/Dictionaries/WhenBuildingDictionaryCreateNewMappers.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; using AgileMapper.UnitTests.Common; using AgileMapper.UnitTests.Common.TestClasses; - using Configuration; + using Buildable.Configuration; using Xunit; using GeneratedMapper = Mappers.Mapper; diff --git a/AgileMapper.Buildable.UnitTests/Mappers/IntStringIntToTargetValueSourceMapper.cs b/AgileMapper.Buildable.UnitTests/Mappers/IntStringIntToTargetValueSourceMapper.cs new file mode 100644 index 000000000..9c221695a --- /dev/null +++ b/AgileMapper.Buildable.UnitTests/Mappers/IntStringIntToTargetValueSourceMapper.cs @@ -0,0 +1,219 @@ +// ------------------------------------------------------------------------------ +// <auto-generated> +// This code was generated by AgileObjects.AgileMapper.Buildable. +// Runtime Version: 0.1.0.0 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// </auto-generated> +// ------------------------------------------------------------------------------ + +using System; +using System.CodeDom.Compiler; +using AgileObjects.AgileMapper; +using AgileObjects.AgileMapper.Buildable.UnitTests.Configuration.DataSources; +using AgileObjects.AgileMapper.ObjectPopulation; +using AgileObjects.AgileMapper.UnitTests.Common.TestClasses; + +namespace AgileObjects.AgileMapper.Buildable.UnitTests.Mappers +{ + [GeneratedCode("AgileObjects.AgileMapper.Buildable", "0.1.0.0")] + public class IntStringIntToTargetValueSourceMapper : MappingExecutionContextBase<ToTargetValueSource<int, string, int>> + { + public IntStringIntToTargetValueSourceMapper + ( + ToTargetValueSource<int, string, int> source + ) + : base(source) + { + } + + public PublicTwoFields<int, int> ToANew<TTarget>() + where TTarget : PublicTwoFields<int, int> + { + return IntStringIntToTargetValueSourceMapper.CreateNew(this.CreateRootMappingData(default(PublicTwoFields<int, int>))); + } + + public PublicTwoFields<int, int> Over + ( + PublicTwoFields<int, int> target + ) + { + return IntStringIntToTargetValueSourceMapper.Overwrite(this.CreateRootMappingData(target)); + } + + public PublicTwoFields<int, int> OnTo + ( + PublicTwoFields<int, int> target + ) + { + return IntStringIntToTargetValueSourceMapper.Merge(this.CreateRootMappingData(target)); + } + + private static PublicTwoFields<int, int> CreateNew + ( + IObjectMappingData<ToTargetValueSource<int, string, int>, PublicTwoFields<int, int>> isittvsToIiptfData + ) + { + ToTargetValueSource<int, string, int> sourceIntStringIntToTargetValueSource; + try + { + sourceIntStringIntToTargetValueSource = isittvsToIiptfData.Source; + + var intIntPublicTwoFields = new PublicTwoFields<int, int>(); + intIntPublicTwoFields.Value1 = sourceIntStringIntToTargetValueSource.Value1; + // No data sources for Value2 + + if (sourceIntStringIntToTargetValueSource.Value != null) + { + if (sourceIntStringIntToTargetValueSource.Value != null) + { + intIntPublicTwoFields.Value1 = IntStringIntToTargetValueSourceMapper.GetInt1(sourceIntStringIntToTargetValueSource); + } + + if (sourceIntStringIntToTargetValueSource.Value != null) + { + intIntPublicTwoFields.Value2 = sourceIntStringIntToTargetValueSource.Value.Value2; + } + } + + return intIntPublicTwoFields; + } + catch (Exception ex) + { + throw MappingException.For( + "CreateNew", + "ToTargetValueSource<int, string, int>", + "PublicTwoFields<int, int>", + ex); + } + } + + private static PublicTwoFields<int, int> Overwrite + ( + IObjectMappingData<ToTargetValueSource<int, string, int>, PublicTwoFields<int, int>> isittvsToIiptfData + ) + { + ToTargetValueSource<int, string, int> sourceIntStringIntToTargetValueSource; + try + { + sourceIntStringIntToTargetValueSource = isittvsToIiptfData.Source; + + isittvsToIiptfData.Target.Value1 = sourceIntStringIntToTargetValueSource.Value1; + // No data sources for Value2 + + if (sourceIntStringIntToTargetValueSource.Value != null) + { + if (sourceIntStringIntToTargetValueSource.Value != null) + { + isittvsToIiptfData.Target.Value1 = IntStringIntToTargetValueSourceMapper.GetInt2(sourceIntStringIntToTargetValueSource); + } + else + { + isittvsToIiptfData.Target.Value1 = default(int); + } + + if (sourceIntStringIntToTargetValueSource.Value != null) + { + isittvsToIiptfData.Target.Value2 = sourceIntStringIntToTargetValueSource.Value.Value2; + } + else + { + isittvsToIiptfData.Target.Value2 = default(int); + } + } + + return isittvsToIiptfData.Target; + } + catch (Exception ex) + { + throw MappingException.For( + "Overwrite", + "ToTargetValueSource<int, string, int>", + "PublicTwoFields<int, int>", + ex); + } + } + + private static PublicTwoFields<int, int> Merge + ( + IObjectMappingData<ToTargetValueSource<int, string, int>, PublicTwoFields<int, int>> isittvsToIiptfData + ) + { + ToTargetValueSource<int, string, int> sourceIntStringIntToTargetValueSource; + try + { + sourceIntStringIntToTargetValueSource = isittvsToIiptfData.Source; + + if (isittvsToIiptfData.Target.Value1 == default(int)) + { + isittvsToIiptfData.Target.Value1 = sourceIntStringIntToTargetValueSource.Value1; + } + + // No data sources for Value2 + + if (sourceIntStringIntToTargetValueSource.Value != null) + { + if (isittvsToIiptfData.Target.Value1 == default(int)) + { + if (sourceIntStringIntToTargetValueSource.Value != null) + { + isittvsToIiptfData.Target.Value1 = IntStringIntToTargetValueSourceMapper.GetInt3(sourceIntStringIntToTargetValueSource); + } + } + + if (isittvsToIiptfData.Target.Value2 == default(int)) + { + if (sourceIntStringIntToTargetValueSource.Value != null) + { + isittvsToIiptfData.Target.Value2 = sourceIntStringIntToTargetValueSource.Value.Value2; + } + } + } + + return isittvsToIiptfData.Target; + } + catch (Exception ex) + { + throw MappingException.For( + "Merge", + "ToTargetValueSource<int, string, int>", + "PublicTwoFields<int, int>", + ex); + } + } + + private static int GetInt1 + ( + ToTargetValueSource<int, string, int> sourceIntStringIntToTargetValueSource + ) + { + int intValue; + return int.TryParse(sourceIntStringIntToTargetValueSource.Value.Value1, out intValue) + ? intValue + : default(int); + } + + private static int GetInt2 + ( + ToTargetValueSource<int, string, int> sourceIntStringIntToTargetValueSource + ) + { + int intValue; + return int.TryParse(sourceIntStringIntToTargetValueSource.Value.Value1, out intValue) + ? intValue + : default(int); + } + + private static int GetInt3 + ( + ToTargetValueSource<int, string, int> sourceIntStringIntToTargetValueSource + ) + { + int intValue; + return int.TryParse(sourceIntStringIntToTargetValueSource.Value.Value1, out intValue) + ? intValue + : default(int); + } + } +} \ No newline at end of file diff --git a/AgileMapper.Buildable.UnitTests/Mappers/Mapper.cs b/AgileMapper.Buildable.UnitTests/Mappers/Mapper.cs index 91afb8173..127a212e1 100644 --- a/AgileMapper.Buildable.UnitTests/Mappers/Mapper.cs +++ b/AgileMapper.Buildable.UnitTests/Mappers/Mapper.cs @@ -12,6 +12,7 @@ using System.CodeDom.Compiler; using System.Collections.Generic; using System.Collections.ObjectModel; +using AgileObjects.AgileMapper.Buildable.UnitTests.Configuration.DataSources; using AgileObjects.AgileMapper.UnitTests.Common.TestClasses; namespace AgileObjects.AgileMapper.Buildable.UnitTests.Mappers @@ -83,6 +84,14 @@ IEnumerable<int> source return new IntIEnumerableMapper(source); } + public static IntStringIntToTargetValueSourceMapper Map + ( + ToTargetValueSource<int, string, int> source + ) + { + return new IntStringIntToTargetValueSourceMapper(source); + } + public static ProductArrayMapper Map ( Product[] source diff --git a/AgileMapper.Buildable.UnitTests/WhenBuildingCircularReferenceMappers.cs b/AgileMapper.Buildable.UnitTests/WhenBuildingCircularReferenceMappers.cs index 1f2e1dbe4..5170a3d81 100644 --- a/AgileMapper.Buildable.UnitTests/WhenBuildingCircularReferenceMappers.cs +++ b/AgileMapper.Buildable.UnitTests/WhenBuildingCircularReferenceMappers.cs @@ -2,7 +2,7 @@ namespace AgileObjects.AgileMapper.Buildable.UnitTests { using AgileMapper.UnitTests.Common; using AgileMapper.UnitTests.Common.TestClasses; - using Configuration; + using Buildable.Configuration; using Xunit; using GeneratedMapper = Mappers.Mapper; diff --git a/AgileMapper.Buildable.UnitTests/WhenBuildingComplexTypeCreateNewMappers.cs b/AgileMapper.Buildable.UnitTests/WhenBuildingComplexTypeCreateNewMappers.cs index 33db0a9b4..ecfb5d568 100644 --- a/AgileMapper.Buildable.UnitTests/WhenBuildingComplexTypeCreateNewMappers.cs +++ b/AgileMapper.Buildable.UnitTests/WhenBuildingComplexTypeCreateNewMappers.cs @@ -3,7 +3,7 @@ namespace AgileObjects.AgileMapper.Buildable.UnitTests using System; using AgileMapper.UnitTests.Common; using AgileMapper.UnitTests.Common.TestClasses; - using Configuration; + using Buildable.Configuration; using Xunit; using GeneratedMapper = Mappers.Mapper; diff --git a/AgileMapper.Buildable.UnitTests/WhenBuildingComplexTypeMergeMappers.cs b/AgileMapper.Buildable.UnitTests/WhenBuildingComplexTypeMergeMappers.cs index 7d1cdb554..48629d316 100644 --- a/AgileMapper.Buildable.UnitTests/WhenBuildingComplexTypeMergeMappers.cs +++ b/AgileMapper.Buildable.UnitTests/WhenBuildingComplexTypeMergeMappers.cs @@ -2,7 +2,7 @@ namespace AgileObjects.AgileMapper.Buildable.UnitTests { using AgileMapper.UnitTests.Common; using AgileObjects.AgileMapper.UnitTests.Common.TestClasses; - using Configuration; + using Buildable.Configuration; using Xunit; using GeneratedMapper = Mappers.Mapper; diff --git a/AgileMapper.Buildable.UnitTests/WhenBuildingComplexTypeOverwriteMappers.cs b/AgileMapper.Buildable.UnitTests/WhenBuildingComplexTypeOverwriteMappers.cs index b9e408923..816afae87 100644 --- a/AgileMapper.Buildable.UnitTests/WhenBuildingComplexTypeOverwriteMappers.cs +++ b/AgileMapper.Buildable.UnitTests/WhenBuildingComplexTypeOverwriteMappers.cs @@ -2,7 +2,7 @@ namespace AgileObjects.AgileMapper.Buildable.UnitTests { using AgileMapper.UnitTests.Common; using AgileMapper.UnitTests.Common.TestClasses; - using Configuration; + using Buildable.Configuration; using Xunit; using GeneratedMapper = Mappers.Mapper; diff --git a/AgileMapper.Buildable.UnitTests/WhenBuildingDerivedTypeMappers.cs b/AgileMapper.Buildable.UnitTests/WhenBuildingDerivedTypeMappers.cs index 69eb9b0bf..797f31b1b 100644 --- a/AgileMapper.Buildable.UnitTests/WhenBuildingDerivedTypeMappers.cs +++ b/AgileMapper.Buildable.UnitTests/WhenBuildingDerivedTypeMappers.cs @@ -2,7 +2,7 @@ { using AgileMapper.UnitTests.Common; using AgileMapper.UnitTests.Common.TestClasses; - using Configuration; + using Buildable.Configuration; using Xunit; using GeneratedMapper = Mappers.Mapper; diff --git a/AgileMapper.Buildable.UnitTests/WhenBuildingEnumerableCreateNewMappers.cs b/AgileMapper.Buildable.UnitTests/WhenBuildingEnumerableCreateNewMappers.cs index a44561f09..3d88b6f0d 100644 --- a/AgileMapper.Buildable.UnitTests/WhenBuildingEnumerableCreateNewMappers.cs +++ b/AgileMapper.Buildable.UnitTests/WhenBuildingEnumerableCreateNewMappers.cs @@ -6,7 +6,7 @@ using System.Linq; using AgileMapper.UnitTests.Common; using AgileMapper.UnitTests.Common.TestClasses; - using Configuration; + using Buildable.Configuration; using Xunit; using GeneratedMapper = Mappers.Mapper; diff --git a/AgileMapper.Buildable.UnitTests/WhenBuildingEnumerableMergeMappers.cs b/AgileMapper.Buildable.UnitTests/WhenBuildingEnumerableMergeMappers.cs index 073a4dd58..d2eff741a 100644 --- a/AgileMapper.Buildable.UnitTests/WhenBuildingEnumerableMergeMappers.cs +++ b/AgileMapper.Buildable.UnitTests/WhenBuildingEnumerableMergeMappers.cs @@ -4,7 +4,7 @@ using System.Collections.ObjectModel; using AgileMapper.UnitTests.Common; using AgileMapper.UnitTests.Common.TestClasses; - using Configuration; + using Buildable.Configuration; using Xunit; using GeneratedMapper = Mappers.Mapper; diff --git a/AgileMapper.Buildable.UnitTests/WhenBuildingEnumerableOverwriteMappers.cs b/AgileMapper.Buildable.UnitTests/WhenBuildingEnumerableOverwriteMappers.cs index d05fdf350..64651f57e 100644 --- a/AgileMapper.Buildable.UnitTests/WhenBuildingEnumerableOverwriteMappers.cs +++ b/AgileMapper.Buildable.UnitTests/WhenBuildingEnumerableOverwriteMappers.cs @@ -5,7 +5,7 @@ using System.Linq; using AgileMapper.UnitTests.Common; using AgileMapper.UnitTests.Common.TestClasses; - using Configuration; + using Buildable.Configuration; using Xunit; using GeneratedMapper = Mappers.Mapper; diff --git a/AgileMapper.Buildable.UnitTests/WhenBuildingRootEnumMappers.cs b/AgileMapper.Buildable.UnitTests/WhenBuildingRootEnumMappers.cs index e62e56323..d5c7a419b 100644 --- a/AgileMapper.Buildable.UnitTests/WhenBuildingRootEnumMappers.cs +++ b/AgileMapper.Buildable.UnitTests/WhenBuildingRootEnumMappers.cs @@ -2,7 +2,7 @@ { using AgileMapper.UnitTests.Common; using AgileMapper.UnitTests.Common.TestClasses; - using Configuration; + using Buildable.Configuration; using Xunit; using GeneratedMapper = Mappers.Mapper; From 386b9761555c9ace21d59c5501f280c75fedd2ca Mon Sep 17 00:00:00 2001 From: Steve Wilkes <steve@agileobjects.co.uk> Date: Sat, 19 Jun 2021 12:36:02 +0100 Subject: [PATCH 50/65] Support for separating mapper generator and output project / Coverage w/ generator NET Core 3.1 project --- ...Mapper.Buildable.UnitTests.NetCore3.csproj | 41 +++ ...le.UnitTests.NetStandard2.Generator.csproj | 32 ++ ...able.UnitTests.NetStandard2.Mappers.csproj | 13 + .../Mappers/AddressMapper.cs | 97 ++++++ .../Mappers/CharArrayMapper.cs | 67 ++++ .../Mappers/ChildMapper.cs | 170 ++++++++++ .../Mappers/DateTimeHashSetMapper.cs | 85 +++++ .../Mappers/DecimalArrayMapper.cs | 80 +++++ .../IntAddressPublicTwoFieldsMapper.cs | 65 ++++ .../Mappers/IntArrayMapper.cs | 70 +++++ .../Mappers/IntIEnumerableMapper.cs | 80 +++++ .../IntStringIntToTargetValueSourceMapper.cs | 218 +++++++++++++ .../Mappers/Mapper.cs | 166 ++++++++++ .../Mappers/ProductArrayMapper.cs | 291 ++++++++++++++++++ .../Mappers/ProductDtoArrayMapper.cs | 236 ++++++++++++++ .../Mappers/ProductDtoListMapper.cs | 129 ++++++++ .../Mappers/ProductMapper.cs | 106 +++++++ .../Mappers/StringCollectionMapper.cs | 63 ++++ .../Mappers/StringListMapper.cs | 80 +++++ .../Mappers/StringMapper.cs | 130 ++++++++ .../Mappers/StringPublicFieldMapper.cs | 102 ++++++ .../Mappers/StringPublicPropertyMapper.cs | 67 ++++ .../AgileMapper.Buildable.UnitTests.csproj | 1 - .../WhenBuildingToTargetDataSourceMappers.cs | 23 -- .../WhenBuildingDictionaryCreateNewMappers.cs | 13 - .../CircularReferenceMapperConfiguration.cs | 13 + ...ComplexTypeCreateNewMapperConfiguration.cs | 16 + .../ComplexTypeMergeMapperConfiguration.cs | 13 + ...ComplexTypeOverwriteMapperConfiguration.cs | 13 + .../ToTargetDataSourceMapperConfiguration.cs | 16 + .../DerivedTypeMapperConfiguration.cs | 14 + .../DictionaryCreateNewMapperConfiguration.cs | 14 + .../EnumerableCreateNewMapperConfiguration.cs | 22 ++ .../EnumerableMergeMapperConfiguration.cs | 18 ++ .../EnumerableOverwriteMapperConfiguration.cs | 19 ++ .../RootEnumMapperConfiguration.cs | 13 + .../IntStringIntToTargetValueSourceMapper.cs | 1 - .../Mappers/Mapper.cs | 1 - .../WhenBuildingCircularReferenceMappers.cs | 13 - ...WhenBuildingComplexTypeCreateNewMappers.cs | 16 - .../WhenBuildingComplexTypeMergeMappers.cs | 13 - ...WhenBuildingComplexTypeOverwriteMappers.cs | 13 - .../WhenBuildingDerivedTypeMappers.cs | 14 - .../WhenBuildingEnumerableCreateNewMappers.cs | 19 -- .../WhenBuildingEnumerableMergeMappers.cs | 17 - .../WhenBuildingEnumerableOverwriteMappers.cs | 17 - .../WhenBuildingRootEnumMappers.cs | 13 - ...AgileObjects.AgileMapper.Buildable.targets | 6 + .../TestClasses/ToTargetValueSource.cs | 9 + .../AgileMapper.UnitTests.NetCore2.csproj | 4 +- AgileMapper.sln | 21 ++ Directory.Build.props | 1 + 52 files changed, 2597 insertions(+), 177 deletions(-) create mode 100644 AgileMapper.Buildable.UnitTests.NetCore3/AgileMapper.Buildable.UnitTests.NetCore3.csproj create mode 100644 AgileMapper.Buildable.UnitTests.NetStandard2.Generator/AgileMapper.Buildable.UnitTests.NetStandard2.Generator.csproj create mode 100644 AgileMapper.Buildable.UnitTests.NetStandard2.Mappers/AgileMapper.Buildable.UnitTests.NetStandard2.Mappers.csproj create mode 100644 AgileMapper.Buildable.UnitTests.NetStandard2.Mappers/Mappers/AddressMapper.cs create mode 100644 AgileMapper.Buildable.UnitTests.NetStandard2.Mappers/Mappers/CharArrayMapper.cs create mode 100644 AgileMapper.Buildable.UnitTests.NetStandard2.Mappers/Mappers/ChildMapper.cs create mode 100644 AgileMapper.Buildable.UnitTests.NetStandard2.Mappers/Mappers/DateTimeHashSetMapper.cs create mode 100644 AgileMapper.Buildable.UnitTests.NetStandard2.Mappers/Mappers/DecimalArrayMapper.cs create mode 100644 AgileMapper.Buildable.UnitTests.NetStandard2.Mappers/Mappers/IntAddressPublicTwoFieldsMapper.cs create mode 100644 AgileMapper.Buildable.UnitTests.NetStandard2.Mappers/Mappers/IntArrayMapper.cs create mode 100644 AgileMapper.Buildable.UnitTests.NetStandard2.Mappers/Mappers/IntIEnumerableMapper.cs create mode 100644 AgileMapper.Buildable.UnitTests.NetStandard2.Mappers/Mappers/IntStringIntToTargetValueSourceMapper.cs create mode 100644 AgileMapper.Buildable.UnitTests.NetStandard2.Mappers/Mappers/Mapper.cs create mode 100644 AgileMapper.Buildable.UnitTests.NetStandard2.Mappers/Mappers/ProductArrayMapper.cs create mode 100644 AgileMapper.Buildable.UnitTests.NetStandard2.Mappers/Mappers/ProductDtoArrayMapper.cs create mode 100644 AgileMapper.Buildable.UnitTests.NetStandard2.Mappers/Mappers/ProductDtoListMapper.cs create mode 100644 AgileMapper.Buildable.UnitTests.NetStandard2.Mappers/Mappers/ProductMapper.cs create mode 100644 AgileMapper.Buildable.UnitTests.NetStandard2.Mappers/Mappers/StringCollectionMapper.cs create mode 100644 AgileMapper.Buildable.UnitTests.NetStandard2.Mappers/Mappers/StringListMapper.cs create mode 100644 AgileMapper.Buildable.UnitTests.NetStandard2.Mappers/Mappers/StringMapper.cs create mode 100644 AgileMapper.Buildable.UnitTests.NetStandard2.Mappers/Mappers/StringPublicFieldMapper.cs create mode 100644 AgileMapper.Buildable.UnitTests.NetStandard2.Mappers/Mappers/StringPublicPropertyMapper.cs create mode 100644 AgileMapper.Buildable.UnitTests/MapperConfiguration/CircularReferenceMapperConfiguration.cs create mode 100644 AgileMapper.Buildable.UnitTests/MapperConfiguration/ComplexTypeCreateNewMapperConfiguration.cs create mode 100644 AgileMapper.Buildable.UnitTests/MapperConfiguration/ComplexTypeMergeMapperConfiguration.cs create mode 100644 AgileMapper.Buildable.UnitTests/MapperConfiguration/ComplexTypeOverwriteMapperConfiguration.cs create mode 100644 AgileMapper.Buildable.UnitTests/MapperConfiguration/Configuration/DataSources/ToTargetDataSourceMapperConfiguration.cs create mode 100644 AgileMapper.Buildable.UnitTests/MapperConfiguration/DerivedTypeMapperConfiguration.cs create mode 100644 AgileMapper.Buildable.UnitTests/MapperConfiguration/Dictionaries/DictionaryCreateNewMapperConfiguration.cs create mode 100644 AgileMapper.Buildable.UnitTests/MapperConfiguration/EnumerableCreateNewMapperConfiguration.cs create mode 100644 AgileMapper.Buildable.UnitTests/MapperConfiguration/EnumerableMergeMapperConfiguration.cs create mode 100644 AgileMapper.Buildable.UnitTests/MapperConfiguration/EnumerableOverwriteMapperConfiguration.cs create mode 100644 AgileMapper.Buildable.UnitTests/MapperConfiguration/RootEnumMapperConfiguration.cs create mode 100644 AgileMapper.UnitTests.Common/TestClasses/ToTargetValueSource.cs diff --git a/AgileMapper.Buildable.UnitTests.NetCore3/AgileMapper.Buildable.UnitTests.NetCore3.csproj b/AgileMapper.Buildable.UnitTests.NetCore3/AgileMapper.Buildable.UnitTests.NetCore3.csproj new file mode 100644 index 000000000..2a205221e --- /dev/null +++ b/AgileMapper.Buildable.UnitTests.NetCore3/AgileMapper.Buildable.UnitTests.NetCore3.csproj @@ -0,0 +1,41 @@ +<Project Sdk="Microsoft.NET.Sdk"> + + <PropertyGroup> + <TargetFramework>netcoreapp3.1</TargetFramework> + <AssemblyName>AgileObjects.AgileMapper.Buildable.UnitTests.NetCore3</AssemblyName> + <RootNamespace>AgileObjects.AgileMapper.Buildable.UnitTests</RootNamespace> + <TreatWarningsAsErrors>true</TreatWarningsAsErrors> + <WarningsAsErrors></WarningsAsErrors> + <NoWarn>0649;1701;1702</NoWarn> + <IsPackable>false</IsPackable> + </PropertyGroup> + + <PropertyGroup> + <XprGeneratorDebug>true</XprGeneratorDebug> + <MappersOutputProject>..\AgileMapper.Buildable.UnitTests.NetStandard2.Mappers\AgileMapper.Buildable.UnitTests.NetStandard2.Mappers.csproj</MappersOutputProject> + </PropertyGroup> + + <ItemGroup> + <Compile Include="..\AgileMapper.Buildable.UnitTests\**\*.cs" Exclude="..\AgileMapper.Buildable.UnitTests\obj\**\*.cs;..\AgileMapper.Buildable.UnitTests\Mapper*\**\*.cs"> + <Link>%(RecursiveDir)%(Filename)%(Extension)</Link> + </Compile> + </ItemGroup> + + <ItemGroup> + <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.7.1" /> + <PackageReference Include="xunit" Version="2.4.1" /> + <PackageReference Include="xunit.runner.visualstudio" Version="2.4.3"> + <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> + <PrivateAssets>all</PrivateAssets> + </PackageReference> + <PackageReference Include="coverlet.collector" Version="1.3.0"> + <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> + <PrivateAssets>all</PrivateAssets> + </PackageReference> + </ItemGroup> + + <ItemGroup> + <ProjectReference Include="..\AgileMapper.Buildable.UnitTests.NetStandard2.Mappers\AgileMapper.Buildable.UnitTests.NetStandard2.Mappers.csproj" /> + </ItemGroup> + +</Project> diff --git a/AgileMapper.Buildable.UnitTests.NetStandard2.Generator/AgileMapper.Buildable.UnitTests.NetStandard2.Generator.csproj b/AgileMapper.Buildable.UnitTests.NetStandard2.Generator/AgileMapper.Buildable.UnitTests.NetStandard2.Generator.csproj new file mode 100644 index 000000000..af02ced33 --- /dev/null +++ b/AgileMapper.Buildable.UnitTests.NetStandard2.Generator/AgileMapper.Buildable.UnitTests.NetStandard2.Generator.csproj @@ -0,0 +1,32 @@ +<Project Sdk="Microsoft.NET.Sdk"> + + <PropertyGroup> + <TargetFramework>netstandard2.0</TargetFramework> + <AssemblyName>AgileObjects.AgileMapper.Buildable.UnitTests.NetStandard2.Generator</AssemblyName> + <RootNamespace>AgileObjects.AgileMapper.Buildable.UnitTests.Generator</RootNamespace> + <CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies> + </PropertyGroup> + + <PropertyGroup> + <XprGeneratorDebug>false</XprGeneratorDebug> + <MappersOutputProject>..\AgileMapper.Buildable.UnitTests.NetStandard2.Mappers\AgileMapper.Buildable.UnitTests.NetStandard2.Mappers.csproj</MappersOutputProject> + </PropertyGroup> + + <ItemGroup> + <Compile Include="..\AgileMapper.Buildable.UnitTests\MapperConfiguration\*.cs"> + <Link>MapperConfiguration\%(RecursiveDir)%(Filename)%(Extension)</Link> + </Compile> + <Compile Include="..\AgileMapper.Buildable.UnitTests\MapperConfiguration\*\**\*.cs"> + <Link>MapperConfiguration\%(RecursiveDir)%(Filename)%(Extension)</Link> + </Compile> + </ItemGroup> + + <ItemGroup> + <PackageReference Include="AgileObjects.AgileMapper.Buildable" Version="0.1.0-preview1" /> + </ItemGroup> + + <ItemGroup> + <ProjectReference Include="..\AgileMapper.UnitTests.Common\AgileMapper.UnitTests.Common.csproj" /> + </ItemGroup> + +</Project> diff --git a/AgileMapper.Buildable.UnitTests.NetStandard2.Mappers/AgileMapper.Buildable.UnitTests.NetStandard2.Mappers.csproj b/AgileMapper.Buildable.UnitTests.NetStandard2.Mappers/AgileMapper.Buildable.UnitTests.NetStandard2.Mappers.csproj new file mode 100644 index 000000000..2864f9f03 --- /dev/null +++ b/AgileMapper.Buildable.UnitTests.NetStandard2.Mappers/AgileMapper.Buildable.UnitTests.NetStandard2.Mappers.csproj @@ -0,0 +1,13 @@ +<Project Sdk="Microsoft.NET.Sdk"> + + <PropertyGroup> + <TargetFramework>netstandard2.0</TargetFramework> + <AssemblyName>AgileObjects.AgileMapper.Buildable.UnitTests.NetStandard2.Mappers</AssemblyName> + <RootNamespace>AgileObjects.AgileMapper.Buildable.UnitTests</RootNamespace> + </PropertyGroup> + + <ItemGroup> + <ProjectReference Include="..\AgileMapper.UnitTests.Common\AgileMapper.UnitTests.Common.csproj" /> + </ItemGroup> + +</Project> diff --git a/AgileMapper.Buildable.UnitTests.NetStandard2.Mappers/Mappers/AddressMapper.cs b/AgileMapper.Buildable.UnitTests.NetStandard2.Mappers/Mappers/AddressMapper.cs new file mode 100644 index 000000000..e25279b40 --- /dev/null +++ b/AgileMapper.Buildable.UnitTests.NetStandard2.Mappers/Mappers/AddressMapper.cs @@ -0,0 +1,97 @@ +// ------------------------------------------------------------------------------ +// <auto-generated> +// This code was generated by AgileObjects.AgileMapper.Buildable. +// Runtime Version: 0.1.0.0 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// </auto-generated> +// ------------------------------------------------------------------------------ + +using System; +using System.CodeDom.Compiler; +using AgileObjects.AgileMapper; +using AgileObjects.AgileMapper.ObjectPopulation; +using AgileObjects.AgileMapper.UnitTests.Common.TestClasses; + +namespace AgileObjects.AgileMapper.Buildable.UnitTests.Mappers +{ + [GeneratedCode("AgileObjects.AgileMapper.Buildable", "0.1.0.0")] + public class AddressMapper : MappingExecutionContextBase<Address> + { + public AddressMapper + ( + Address source + ) + : base(source) + { + } + + public Address OnTo + ( + Address target + ) + { + return AddressMapper.Merge(this.CreateRootMappingData(target)); + } + + public Address Over + ( + Address target + ) + { + return AddressMapper.Overwrite(this.CreateRootMappingData(target)); + } + + private static Address Merge + ( + IObjectMappingData<Address, Address> aToAData + ) + { + try + { + if (aToAData.Target.Line1 == null) + { + aToAData.Target.Line1 = aToAData.Source.Line1; + } + + if (aToAData.Target.Line2 == null) + { + aToAData.Target.Line2 = aToAData.Source.Line2; + } + + return aToAData.Target; + } + catch (Exception ex) + { + throw MappingException.For( + "Merge", + "Address", + "Address", + ex); + } + } + + private static Address Overwrite + ( + IObjectMappingData<Address, Address> aToAData + ) + { + try + { + aToAData.Target.Line1 = aToAData.Source.Line1; + aToAData.Target.Line2 = aToAData.Source.Line2; + + return aToAData.Target; + } + catch (Exception ex) + { + throw MappingException.For( + "Overwrite", + "Address", + "Address", + ex); + } + } + } +} \ No newline at end of file diff --git a/AgileMapper.Buildable.UnitTests.NetStandard2.Mappers/Mappers/CharArrayMapper.cs b/AgileMapper.Buildable.UnitTests.NetStandard2.Mappers/Mappers/CharArrayMapper.cs new file mode 100644 index 000000000..f7d8cd8e4 --- /dev/null +++ b/AgileMapper.Buildable.UnitTests.NetStandard2.Mappers/Mappers/CharArrayMapper.cs @@ -0,0 +1,67 @@ +// ------------------------------------------------------------------------------ +// <auto-generated> +// This code was generated by AgileObjects.AgileMapper.Buildable. +// Runtime Version: 0.1.0.0 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// </auto-generated> +// ------------------------------------------------------------------------------ + +using System; +using System.CodeDom.Compiler; +using System.Linq; +using AgileObjects.AgileMapper; +using AgileObjects.AgileMapper.Extensions; +using AgileObjects.AgileMapper.ObjectPopulation; + +namespace AgileObjects.AgileMapper.Buildable.UnitTests.Mappers +{ + [GeneratedCode("AgileObjects.AgileMapper.Buildable", "0.1.0.0")] + public class CharArrayMapper : MappingExecutionContextBase<char[]> + { + public CharArrayMapper + ( + char[] source + ) + : base(source) + { + } + + public int[] Over + ( + int[] target + ) + { + return CharArrayMapper.Overwrite(this.CreateRootMappingData(target)); + } + + private static int[] Overwrite + ( + IObjectMappingData<char[], int[]> caToIaData + ) + { + try + { + return caToIaData.Source.Project(c => CharArrayMapper.GetInt(c)).ToArray(); + } + catch (Exception ex) + { + throw MappingException.For( + "Overwrite", + "char[]", + "int[]", + ex); + } + } + + private static int GetInt + ( + char c + ) + { + int intValue; + return int.TryParse(c.ToString(), out intValue) ? intValue : default(int); + } + } +} \ No newline at end of file diff --git a/AgileMapper.Buildable.UnitTests.NetStandard2.Mappers/Mappers/ChildMapper.cs b/AgileMapper.Buildable.UnitTests.NetStandard2.Mappers/Mappers/ChildMapper.cs new file mode 100644 index 000000000..a004986c5 --- /dev/null +++ b/AgileMapper.Buildable.UnitTests.NetStandard2.Mappers/Mappers/ChildMapper.cs @@ -0,0 +1,170 @@ +// ------------------------------------------------------------------------------ +// <auto-generated> +// This code was generated by AgileObjects.AgileMapper.Buildable. +// Runtime Version: 0.1.0.0 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// </auto-generated> +// ------------------------------------------------------------------------------ + +using System; +using System.CodeDom.Compiler; +using AgileObjects.AgileMapper; +using AgileObjects.AgileMapper.ObjectPopulation; +using AgileObjects.AgileMapper.UnitTests.Common.TestClasses; + +namespace AgileObjects.AgileMapper.Buildable.UnitTests.Mappers +{ + [GeneratedCode("AgileObjects.AgileMapper.Buildable", "0.1.0.0")] + public class ChildMapper : MappingExecutionContextBase<Child> + { + public ChildMapper + ( + Child source + ) + : base(source) + { + } + + public Child ToANew<TTarget>() + where TTarget : Child + { + return ChildMapper.CreateNew(this.CreateRootMappingData(default(Child))); + } + + private static Child MapRepeated + ( + IObjectMappingData<Child, Child> cToCData2 + ) + { + try + { + Child child; + + if (cToCData2.TryGet(cToCData2.Source, out child)) + { + return child; + } + + child = cToCData2.Target ?? new Child(); + cToCData2.Register(cToCData2.Source, child); + child.Name = cToCData2.Source.Name; + + if (cToCData2.Source.EldestParent != null) + { + child.EldestParent = ChildMapper.MapRepeated( + MappingExecutionContextBase<Child>.CreateChildMappingData(cToCData2.Source.EldestParent, child.EldestParent, cToCData2)); + } + + return child; + } + catch (Exception ex) + { + throw MappingException.For( + "CreateNew", + "Child.EldestParent.EldestChild", + "Child.EldestParent.EldestChild", + ex); + } + } + + private static Parent MapRepeated + ( + IObjectMappingData<Parent, Parent> pToPData2 + ) + { + try + { + Parent parent; + + if (pToPData2.TryGet(pToPData2.Source, out parent)) + { + return parent; + } + + parent = pToPData2.Target ?? new Parent(); + pToPData2.Register(pToPData2.Source, parent); + parent.Name = pToPData2.Source.Name; + + if (pToPData2.Source.EldestChild != null) + { + parent.EldestChild = ChildMapper.MapRepeated( + MappingExecutionContextBase<Child>.CreateChildMappingData(pToPData2.Source.EldestChild, parent.EldestChild, pToPData2)); + } + + return parent; + } + catch (Exception ex) + { + throw MappingException.For( + "CreateNew", + "Child.EldestChild.EldestParent", + "Child.EldestParent.EldestChild.EldestParent", + ex); + } + } + + private static Child CreateNew + ( + IObjectMappingData<Child, Child> cToCData + ) + { + Child sourceChild; + try + { + sourceChild = cToCData.Source; + + var child = new Child(); + cToCData.Register(sourceChild, child); + child.Name = sourceChild.Name; + + if (sourceChild.EldestParent != null) + { + child.EldestParent = ChildMapper.GetParent(child, cToCData, sourceChild); + } + + return child; + } + catch (Exception ex) + { + throw MappingException.For( + "CreateNew", + "Child", + "Child", + ex); + } + } + + private static Parent GetParent + ( + Child child, + IObjectMappingData<Child, Child> cToCData, + Child sourceChild + ) + { + try + { + var parent = child.EldestParent ?? new Parent(); + cToCData.Register(sourceChild.EldestParent, parent); + parent.Name = sourceChild.EldestParent.Name; + + if (sourceChild.EldestParent.EldestChild != null) + { + parent.EldestChild = ChildMapper.MapRepeated( + MappingExecutionContextBase<Child>.CreateChildMappingData(sourceChild.EldestParent.EldestChild, parent.EldestChild, cToCData)); + } + + return parent; + } + catch (Exception ex) + { + throw MappingException.For( + "CreateNew", + "Child.EldestParent", + "Child.EldestParent", + ex); + } + } + } +} \ No newline at end of file diff --git a/AgileMapper.Buildable.UnitTests.NetStandard2.Mappers/Mappers/DateTimeHashSetMapper.cs b/AgileMapper.Buildable.UnitTests.NetStandard2.Mappers/Mappers/DateTimeHashSetMapper.cs new file mode 100644 index 000000000..a01419066 --- /dev/null +++ b/AgileMapper.Buildable.UnitTests.NetStandard2.Mappers/Mappers/DateTimeHashSetMapper.cs @@ -0,0 +1,85 @@ +// ------------------------------------------------------------------------------ +// <auto-generated> +// This code was generated by AgileObjects.AgileMapper.Buildable. +// Runtime Version: 0.1.0.0 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// </auto-generated> +// ------------------------------------------------------------------------------ + +using System; +using System.CodeDom.Compiler; +using System.Collections.Generic; +using AgileObjects.AgileMapper; +using AgileObjects.AgileMapper.Extensions; +using AgileObjects.AgileMapper.ObjectPopulation; +using AgileObjects.ReadableExpressions; +using AgileObjects.ReadableExpressions.Extensions; + +namespace AgileObjects.AgileMapper.Buildable.UnitTests.Mappers +{ + [GeneratedCode("AgileObjects.AgileMapper.Buildable", "0.1.0.0")] + public class DateTimeHashSetMapper : MappingExecutionContextBase<HashSet<DateTime>> + { + public DateTimeHashSetMapper + ( + HashSet<DateTime> source + ) + : base(source) + { + } + + public TTarget ToANew<TTarget>() + { + if (typeof(TTarget) == typeof(DateTime[])) + { + return (TTarget)((object)DateTimeHashSetMapper.CreateNew(this.CreateRootMappingData(default(DateTime[])))); + } + + throw new NotSupportedException( + "Unable to perform a 'CreateNew' mapping from source type 'HashSet<DateTime>' to target type '" + typeof(TTarget).GetFriendlyName(null) + "'"); + } + + private static DateTime[] CreateNew + ( + IObjectMappingData<HashSet<DateTime>, DateTime[]> dthsToDtaData + ) + { + try + { + var sourceDateTimeHashSet = dthsToDtaData.Source; + var targetDateTimeList = new List<DateTime>(sourceDateTimeHashSet.Count); + var i = 0; + var enumerator = sourceDateTimeHashSet.GetEnumerator(); + try + { + while (true) + { + if (!enumerator.MoveNext()) + { + break; + } + + targetDateTimeList.Add(enumerator.Current); + ++i; + } + } + finally + { + enumerator.Dispose(); + } + + return targetDateTimeList.ToArray(); + } + catch (Exception ex) + { + throw MappingException.For( + "CreateNew", + "HashSet<DateTime>", + "DateTime[]", + ex); + } + } + } +} \ No newline at end of file diff --git a/AgileMapper.Buildable.UnitTests.NetStandard2.Mappers/Mappers/DecimalArrayMapper.cs b/AgileMapper.Buildable.UnitTests.NetStandard2.Mappers/Mappers/DecimalArrayMapper.cs new file mode 100644 index 000000000..b5576c953 --- /dev/null +++ b/AgileMapper.Buildable.UnitTests.NetStandard2.Mappers/Mappers/DecimalArrayMapper.cs @@ -0,0 +1,80 @@ +// ------------------------------------------------------------------------------ +// <auto-generated> +// This code was generated by AgileObjects.AgileMapper.Buildable. +// Runtime Version: 0.1.0.0 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// </auto-generated> +// ------------------------------------------------------------------------------ + +using System; +using System.CodeDom.Compiler; +using System.Collections.Generic; +using AgileObjects.AgileMapper; +using AgileObjects.AgileMapper.Extensions; +using AgileObjects.AgileMapper.ObjectPopulation; + +namespace AgileObjects.AgileMapper.Buildable.UnitTests.Mappers +{ + [GeneratedCode("AgileObjects.AgileMapper.Buildable", "0.1.0.0")] + public class DecimalArrayMapper : MappingExecutionContextBase<decimal[]> + { + public DecimalArrayMapper + ( + decimal[] source + ) + : base(source) + { + } + + public HashSet<double> OnTo + ( + HashSet<double> target + ) + { + return DecimalArrayMapper.Merge(this.CreateRootMappingData(target)); + } + + private static HashSet<double> Merge + ( + IObjectMappingData<decimal[], HashSet<double>> daToDhsData + ) + { + try + { + var sourceDoubleIEnumerable = daToDhsData.Source.Project(d => (double)d).Exclude(daToDhsData.Target); + var targetDoubleHashSet = daToDhsData.Target; + var i = 0; + var enumerator = sourceDoubleIEnumerable.GetEnumerator(); + try + { + while (true) + { + if (!enumerator.MoveNext()) + { + break; + } + + targetDoubleHashSet.Add(enumerator.Current); + ++i; + } + } + finally + { + enumerator.Dispose(); + } + + return targetDoubleHashSet; + } + catch (Exception ex) + { + throw MappingException.For( + "Merge", + "decimal[]", + "HashSet<double>", + ex); + } + } + } +} \ No newline at end of file diff --git a/AgileMapper.Buildable.UnitTests.NetStandard2.Mappers/Mappers/IntAddressPublicTwoFieldsMapper.cs b/AgileMapper.Buildable.UnitTests.NetStandard2.Mappers/Mappers/IntAddressPublicTwoFieldsMapper.cs new file mode 100644 index 000000000..e028d9ace --- /dev/null +++ b/AgileMapper.Buildable.UnitTests.NetStandard2.Mappers/Mappers/IntAddressPublicTwoFieldsMapper.cs @@ -0,0 +1,65 @@ +// ------------------------------------------------------------------------------ +// <auto-generated> +// This code was generated by AgileObjects.AgileMapper.Buildable. +// Runtime Version: 0.1.0.0 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// </auto-generated> +// ------------------------------------------------------------------------------ + +using System; +using System.CodeDom.Compiler; +using System.Collections.Generic; +using AgileObjects.AgileMapper; +using AgileObjects.AgileMapper.ObjectPopulation; +using AgileObjects.AgileMapper.UnitTests.Common.TestClasses; + +namespace AgileObjects.AgileMapper.Buildable.UnitTests.Mappers +{ + [GeneratedCode("AgileObjects.AgileMapper.Buildable", "0.1.0.0")] + public class IntAddressPublicTwoFieldsMapper : MappingExecutionContextBase<PublicTwoFields<int, Address>> + { + public IntAddressPublicTwoFieldsMapper + ( + PublicTwoFields<int, Address> source + ) + : base(source) + { + } + + public Dictionary<string, string> ToANew<TTarget>() + where TTarget : Dictionary<string, string> + { + return IntAddressPublicTwoFieldsMapper.CreateNew(this.CreateRootMappingData(default(Dictionary<string, string>))); + } + + private static Dictionary<string, string> CreateNew + ( + IObjectMappingData<PublicTwoFields<int, Address>, Dictionary<string, string>> iaptfToSsdData + ) + { + try + { + var stringStringDictionary = new Dictionary<string, string>(); + stringStringDictionary["Value1"] = iaptfToSsdData.Source.Value1.ToString(); + + if (iaptfToSsdData.Source.Value2 != null) + { + stringStringDictionary["Value2.Line1"] = iaptfToSsdData.Source.Value2.Line1; + stringStringDictionary["Value2.Line2"] = iaptfToSsdData.Source.Value2.Line2; + } + + return stringStringDictionary; + } + catch (Exception ex) + { + throw MappingException.For( + "CreateNew", + "PublicTwoFields<int, Address>", + "Dictionary<string, string>", + ex); + } + } + } +} \ No newline at end of file diff --git a/AgileMapper.Buildable.UnitTests.NetStandard2.Mappers/Mappers/IntArrayMapper.cs b/AgileMapper.Buildable.UnitTests.NetStandard2.Mappers/Mappers/IntArrayMapper.cs new file mode 100644 index 000000000..496a2e484 --- /dev/null +++ b/AgileMapper.Buildable.UnitTests.NetStandard2.Mappers/Mappers/IntArrayMapper.cs @@ -0,0 +1,70 @@ +// ------------------------------------------------------------------------------ +// <auto-generated> +// This code was generated by AgileObjects.AgileMapper.Buildable. +// Runtime Version: 0.1.0.0 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// </auto-generated> +// ------------------------------------------------------------------------------ + +using System; +using System.CodeDom.Compiler; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using AgileObjects.AgileMapper; +using AgileObjects.AgileMapper.ObjectPopulation; + +namespace AgileObjects.AgileMapper.Buildable.UnitTests.Mappers +{ + [GeneratedCode("AgileObjects.AgileMapper.Buildable", "0.1.0.0")] + public class IntArrayMapper : MappingExecutionContextBase<int[]> + { + public IntArrayMapper + ( + int[] source + ) + : base(source) + { + } + + public ReadOnlyCollection<int> ToANew<TTarget>() + where TTarget : ReadOnlyCollection<int> + { + return IntArrayMapper.CreateNew(this.CreateRootMappingData(default(ReadOnlyCollection<int>))); + } + + private static ReadOnlyCollection<int> CreateNew + ( + IObjectMappingData<int[], ReadOnlyCollection<int>> iaToIrocData + ) + { + try + { + var sourceIntArray = iaToIrocData.Source; + var targetIntList = new List<int>(sourceIntArray.Length); + var i = 0; + while (true) + { + if (i == sourceIntArray.Length) + { + break; + } + + targetIntList.Add(sourceIntArray[i]); + ++i; + } + + return targetIntList.AsReadOnly(); + } + catch (Exception ex) + { + throw MappingException.For( + "CreateNew", + "int[]", + "ReadOnlyCollection<int>", + ex); + } + } + } +} \ No newline at end of file diff --git a/AgileMapper.Buildable.UnitTests.NetStandard2.Mappers/Mappers/IntIEnumerableMapper.cs b/AgileMapper.Buildable.UnitTests.NetStandard2.Mappers/Mappers/IntIEnumerableMapper.cs new file mode 100644 index 000000000..f9fe81aa9 --- /dev/null +++ b/AgileMapper.Buildable.UnitTests.NetStandard2.Mappers/Mappers/IntIEnumerableMapper.cs @@ -0,0 +1,80 @@ +// ------------------------------------------------------------------------------ +// <auto-generated> +// This code was generated by AgileObjects.AgileMapper.Buildable. +// Runtime Version: 0.1.0.0 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// </auto-generated> +// ------------------------------------------------------------------------------ + +using System; +using System.CodeDom.Compiler; +using System.Collections.Generic; +using AgileObjects.AgileMapper; +using AgileObjects.AgileMapper.Extensions; +using AgileObjects.AgileMapper.ObjectPopulation; + +namespace AgileObjects.AgileMapper.Buildable.UnitTests.Mappers +{ + [GeneratedCode("AgileObjects.AgileMapper.Buildable", "0.1.0.0")] + public class IntIEnumerableMapper : MappingExecutionContextBase<IEnumerable<int>> + { + public IntIEnumerableMapper + ( + IEnumerable<int> source + ) + : base(source) + { + } + + public ICollection<int> OnTo + ( + ICollection<int> target + ) + { + return IntIEnumerableMapper.Merge(this.CreateRootMappingData(target)); + } + + private static ICollection<int> Merge + ( + IObjectMappingData<IEnumerable<int>, ICollection<int>> iieToIicData + ) + { + try + { + var sourceIntIEnumerable = iieToIicData.Source.Exclude(iieToIicData.Target); + ICollection<int> targetIntICollection = iieToIicData.Target.IsReadOnly ? new List<int>(iieToIicData.Target) : iieToIicData.Target; + var i = 0; + var enumerator = sourceIntIEnumerable.GetEnumerator(); + try + { + while (true) + { + if (!enumerator.MoveNext()) + { + break; + } + + targetIntICollection.Add(enumerator.Current); + ++i; + } + } + finally + { + enumerator.Dispose(); + } + + return targetIntICollection; + } + catch (Exception ex) + { + throw MappingException.For( + "Merge", + "IEnumerable<int>", + "ICollection<int>", + ex); + } + } + } +} \ No newline at end of file diff --git a/AgileMapper.Buildable.UnitTests.NetStandard2.Mappers/Mappers/IntStringIntToTargetValueSourceMapper.cs b/AgileMapper.Buildable.UnitTests.NetStandard2.Mappers/Mappers/IntStringIntToTargetValueSourceMapper.cs new file mode 100644 index 000000000..ab63e2a18 --- /dev/null +++ b/AgileMapper.Buildable.UnitTests.NetStandard2.Mappers/Mappers/IntStringIntToTargetValueSourceMapper.cs @@ -0,0 +1,218 @@ +// ------------------------------------------------------------------------------ +// <auto-generated> +// This code was generated by AgileObjects.AgileMapper.Buildable. +// Runtime Version: 0.1.0.0 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// </auto-generated> +// ------------------------------------------------------------------------------ + +using System; +using System.CodeDom.Compiler; +using AgileObjects.AgileMapper; +using AgileObjects.AgileMapper.ObjectPopulation; +using AgileObjects.AgileMapper.UnitTests.Common.TestClasses; + +namespace AgileObjects.AgileMapper.Buildable.UnitTests.Mappers +{ + [GeneratedCode("AgileObjects.AgileMapper.Buildable", "0.1.0.0")] + public class IntStringIntToTargetValueSourceMapper : MappingExecutionContextBase<ToTargetValueSource<int, string, int>> + { + public IntStringIntToTargetValueSourceMapper + ( + ToTargetValueSource<int, string, int> source + ) + : base(source) + { + } + + public PublicTwoFields<int, int> ToANew<TTarget>() + where TTarget : PublicTwoFields<int, int> + { + return IntStringIntToTargetValueSourceMapper.CreateNew(this.CreateRootMappingData(default(PublicTwoFields<int, int>))); + } + + public PublicTwoFields<int, int> Over + ( + PublicTwoFields<int, int> target + ) + { + return IntStringIntToTargetValueSourceMapper.Overwrite(this.CreateRootMappingData(target)); + } + + public PublicTwoFields<int, int> OnTo + ( + PublicTwoFields<int, int> target + ) + { + return IntStringIntToTargetValueSourceMapper.Merge(this.CreateRootMappingData(target)); + } + + private static PublicTwoFields<int, int> CreateNew + ( + IObjectMappingData<ToTargetValueSource<int, string, int>, PublicTwoFields<int, int>> isittvsToIiptfData + ) + { + ToTargetValueSource<int, string, int> sourceIntStringIntToTargetValueSource; + try + { + sourceIntStringIntToTargetValueSource = isittvsToIiptfData.Source; + + var intIntPublicTwoFields = new PublicTwoFields<int, int>(); + intIntPublicTwoFields.Value1 = sourceIntStringIntToTargetValueSource.Value1; + // No data sources for Value2 + + if (sourceIntStringIntToTargetValueSource.Value != null) + { + if (sourceIntStringIntToTargetValueSource.Value != null) + { + intIntPublicTwoFields.Value1 = IntStringIntToTargetValueSourceMapper.GetInt1(sourceIntStringIntToTargetValueSource); + } + + if (sourceIntStringIntToTargetValueSource.Value != null) + { + intIntPublicTwoFields.Value2 = sourceIntStringIntToTargetValueSource.Value.Value2; + } + } + + return intIntPublicTwoFields; + } + catch (Exception ex) + { + throw MappingException.For( + "CreateNew", + "ToTargetValueSource<int, string, int>", + "PublicTwoFields<int, int>", + ex); + } + } + + private static PublicTwoFields<int, int> Overwrite + ( + IObjectMappingData<ToTargetValueSource<int, string, int>, PublicTwoFields<int, int>> isittvsToIiptfData + ) + { + ToTargetValueSource<int, string, int> sourceIntStringIntToTargetValueSource; + try + { + sourceIntStringIntToTargetValueSource = isittvsToIiptfData.Source; + + isittvsToIiptfData.Target.Value1 = sourceIntStringIntToTargetValueSource.Value1; + // No data sources for Value2 + + if (sourceIntStringIntToTargetValueSource.Value != null) + { + if (sourceIntStringIntToTargetValueSource.Value != null) + { + isittvsToIiptfData.Target.Value1 = IntStringIntToTargetValueSourceMapper.GetInt2(sourceIntStringIntToTargetValueSource); + } + else + { + isittvsToIiptfData.Target.Value1 = default(int); + } + + if (sourceIntStringIntToTargetValueSource.Value != null) + { + isittvsToIiptfData.Target.Value2 = sourceIntStringIntToTargetValueSource.Value.Value2; + } + else + { + isittvsToIiptfData.Target.Value2 = default(int); + } + } + + return isittvsToIiptfData.Target; + } + catch (Exception ex) + { + throw MappingException.For( + "Overwrite", + "ToTargetValueSource<int, string, int>", + "PublicTwoFields<int, int>", + ex); + } + } + + private static PublicTwoFields<int, int> Merge + ( + IObjectMappingData<ToTargetValueSource<int, string, int>, PublicTwoFields<int, int>> isittvsToIiptfData + ) + { + ToTargetValueSource<int, string, int> sourceIntStringIntToTargetValueSource; + try + { + sourceIntStringIntToTargetValueSource = isittvsToIiptfData.Source; + + if (isittvsToIiptfData.Target.Value1 == default(int)) + { + isittvsToIiptfData.Target.Value1 = sourceIntStringIntToTargetValueSource.Value1; + } + + // No data sources for Value2 + + if (sourceIntStringIntToTargetValueSource.Value != null) + { + if (isittvsToIiptfData.Target.Value1 == default(int)) + { + if (sourceIntStringIntToTargetValueSource.Value != null) + { + isittvsToIiptfData.Target.Value1 = IntStringIntToTargetValueSourceMapper.GetInt3(sourceIntStringIntToTargetValueSource); + } + } + + if (isittvsToIiptfData.Target.Value2 == default(int)) + { + if (sourceIntStringIntToTargetValueSource.Value != null) + { + isittvsToIiptfData.Target.Value2 = sourceIntStringIntToTargetValueSource.Value.Value2; + } + } + } + + return isittvsToIiptfData.Target; + } + catch (Exception ex) + { + throw MappingException.For( + "Merge", + "ToTargetValueSource<int, string, int>", + "PublicTwoFields<int, int>", + ex); + } + } + + private static int GetInt1 + ( + ToTargetValueSource<int, string, int> sourceIntStringIntToTargetValueSource + ) + { + int intValue; + return int.TryParse(sourceIntStringIntToTargetValueSource.Value.Value1, out intValue) + ? intValue + : default(int); + } + + private static int GetInt2 + ( + ToTargetValueSource<int, string, int> sourceIntStringIntToTargetValueSource + ) + { + int intValue; + return int.TryParse(sourceIntStringIntToTargetValueSource.Value.Value1, out intValue) + ? intValue + : default(int); + } + + private static int GetInt3 + ( + ToTargetValueSource<int, string, int> sourceIntStringIntToTargetValueSource + ) + { + int intValue; + return int.TryParse(sourceIntStringIntToTargetValueSource.Value.Value1, out intValue) + ? intValue + : default(int); + } + } +} \ No newline at end of file diff --git a/AgileMapper.Buildable.UnitTests.NetStandard2.Mappers/Mappers/Mapper.cs b/AgileMapper.Buildable.UnitTests.NetStandard2.Mappers/Mappers/Mapper.cs new file mode 100644 index 000000000..674b9f324 --- /dev/null +++ b/AgileMapper.Buildable.UnitTests.NetStandard2.Mappers/Mappers/Mapper.cs @@ -0,0 +1,166 @@ +// ------------------------------------------------------------------------------ +// <auto-generated> +// This code was generated by AgileObjects.AgileMapper.Buildable. +// Runtime Version: 0.1.0.0 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// </auto-generated> +// ------------------------------------------------------------------------------ + +using System; +using System.CodeDom.Compiler; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using AgileObjects.AgileMapper.UnitTests.Common.TestClasses; + +namespace AgileObjects.AgileMapper.Buildable.UnitTests.Mappers +{ + [GeneratedCode("AgileObjects.AgileMapper.Buildable", "0.1.0.0")] + public static class Mapper + { + public static AddressMapper Map + ( + Address source + ) + { + return new AddressMapper(source); + } + + public static CharArrayMapper Map + ( + char[] source + ) + { + return new CharArrayMapper(source); + } + + public static ChildMapper Map + ( + Child source + ) + { + return new ChildMapper(source); + } + + public static DateTimeHashSetMapper Map + ( + HashSet<DateTime> source + ) + { + return new DateTimeHashSetMapper(source); + } + + public static DecimalArrayMapper Map + ( + decimal[] source + ) + { + return new DecimalArrayMapper(source); + } + + public static IntAddressPublicTwoFieldsMapper Map + ( + PublicTwoFields<int, Address> source + ) + { + return new IntAddressPublicTwoFieldsMapper(source); + } + + public static IntArrayMapper Map + ( + int[] source + ) + { + return new IntArrayMapper(source); + } + + public static IntIEnumerableMapper Map + ( + IEnumerable<int> source + ) + { + return new IntIEnumerableMapper(source); + } + + public static IntStringIntToTargetValueSourceMapper Map + ( + ToTargetValueSource<int, string, int> source + ) + { + return new IntStringIntToTargetValueSourceMapper(source); + } + + public static ProductArrayMapper Map + ( + Product[] source + ) + { + return new ProductArrayMapper(source); + } + + public static ProductDtoArrayMapper Map + ( + ProductDto[] source + ) + { + return new ProductDtoArrayMapper(source); + } + + public static ProductDtoListMapper Map + ( + List<ProductDto> source + ) + { + return new ProductDtoListMapper(source); + } + + public static ProductMapper Map + ( + Product source + ) + { + return new ProductMapper(source); + } + + public static StringCollectionMapper Map + ( + Collection<string> source + ) + { + return new StringCollectionMapper(source); + } + + public static StringListMapper Map + ( + List<string> source + ) + { + return new StringListMapper(source); + } + + public static StringMapper Map + ( + string source + ) + { + return new StringMapper(source); + } + + public static StringPublicFieldMapper Map + ( + PublicField<string> source + ) + { + return new StringPublicFieldMapper(source); + } + + public static StringPublicPropertyMapper Map + ( + PublicProperty<string> source + ) + { + return new StringPublicPropertyMapper(source); + } + } +} \ No newline at end of file diff --git a/AgileMapper.Buildable.UnitTests.NetStandard2.Mappers/Mappers/ProductArrayMapper.cs b/AgileMapper.Buildable.UnitTests.NetStandard2.Mappers/Mappers/ProductArrayMapper.cs new file mode 100644 index 000000000..faba24256 --- /dev/null +++ b/AgileMapper.Buildable.UnitTests.NetStandard2.Mappers/Mappers/ProductArrayMapper.cs @@ -0,0 +1,291 @@ +// ------------------------------------------------------------------------------ +// <auto-generated> +// This code was generated by AgileObjects.AgileMapper.Buildable. +// Runtime Version: 0.1.0.0 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// </auto-generated> +// ------------------------------------------------------------------------------ + +using System; +using System.CodeDom.Compiler; +using System.Collections.Generic; +using AgileObjects.AgileMapper; +using AgileObjects.AgileMapper.Extensions; +using AgileObjects.AgileMapper.ObjectPopulation; +using AgileObjects.AgileMapper.UnitTests.Common.TestClasses; + +namespace AgileObjects.AgileMapper.Buildable.UnitTests.Mappers +{ + [GeneratedCode("AgileObjects.AgileMapper.Buildable", "0.1.0.0")] + public class ProductArrayMapper : MappingExecutionContextBase<Product[]> + { + public ProductArrayMapper + ( + Product[] source + ) + : base(source) + { + } + + public IEnumerable<Product> OnTo + ( + IEnumerable<Product> target + ) + { + return ProductArrayMapper.Merge(this.CreateRootMappingData(target)); + } + + private static IEnumerable<Product> Merge + ( + IObjectMappingData<Product[], IEnumerable<Product>> paToPieData + ) + { + try + { + var collectionData = CollectionData.Create(paToPieData.Source, paToPieData.Target, p => p.ProductId); + collectionData.Intersection.ForEach( + (existingSourceProduct, existingTargetProduct, idx) => ProductArrayMapper.GetProduct1(existingSourceProduct, existingTargetProduct)); + var sourceProductIEnumerable = collectionData.NewSourceItems; + var targetProductICollection = ProductArrayMapper.GetProductICollection(paToPieData); + var i = 0; + var enumerator = sourceProductIEnumerable.GetEnumerator(); + try + { + while (true) + { + if (!enumerator.MoveNext()) + { + break; + } + + targetProductICollection.Add(ProductArrayMapper.GetProduct2(enumerator)); + ++i; + } + } + finally + { + enumerator.Dispose(); + } + + return targetProductICollection; + } + catch (Exception ex) + { + throw MappingException.For( + "Merge", + "Product[]", + "IEnumerable<Product>", + ex); + } + } + + private static Product GetProduct1 + ( + Product existingSourceProduct, + Product existingTargetProduct + ) + { + try + { + if (existingSourceProduct == null) + { + return null; + } + + var sourceMegaProduct = existingSourceProduct as MegaProduct; + + if ((sourceMegaProduct != null) && ((existingTargetProduct == null) || (existingTargetProduct is MegaProduct))) + { + return ProductArrayMapper.GetMegaProduct1(existingTargetProduct, sourceMegaProduct); + } + + if (existingTargetProduct is MegaProduct) + { + return ProductArrayMapper.GetMegaProduct2(existingTargetProduct, existingSourceProduct); + } + var product = existingTargetProduct ?? new Product(); + + if (product.ProductId == null) + { + product.ProductId = existingSourceProduct.ProductId; + } + + if (product.Price == default(double)) + { + product.Price = existingSourceProduct.Price; + } + + return product; + } + catch (Exception ex) + { + throw MappingException.For( + "Merge", + "Product[][i]", + "IEnumerable<Product>[i]", + ex); + } + } + + private static MegaProduct GetMegaProduct1 + ( + Product existingTargetProduct, + MegaProduct sourceMegaProduct + ) + { + try + { + var megaProduct = ((MegaProduct)existingTargetProduct) ?? new MegaProduct(); + + if (megaProduct.HowMega == default(decimal)) + { + megaProduct.HowMega = sourceMegaProduct.HowMega; + } + + if (megaProduct.ProductId == null) + { + megaProduct.ProductId = sourceMegaProduct.ProductId; + } + + if (megaProduct.Price == default(double)) + { + megaProduct.Price = sourceMegaProduct.Price; + } + + return megaProduct; + } + catch (Exception ex) + { + throw MappingException.For( + "Merge", + "Product[][i]", + "IEnumerable<Product>[i]", + ex); + } + } + + private static MegaProduct GetMegaProduct2 + ( + Product existingTargetProduct, + Product existingSourceProduct + ) + { + try + { + var megaProduct = ((MegaProduct)existingTargetProduct) ?? new MegaProduct(); + // No data sources for HowMega + + if (megaProduct.ProductId == null) + { + megaProduct.ProductId = existingSourceProduct.ProductId; + } + + if (megaProduct.Price == default(double)) + { + megaProduct.Price = existingSourceProduct.Price; + } + + return megaProduct; + } + catch (Exception ex) + { + throw MappingException.For( + "Merge", + "Product[][i]", + "IEnumerable<Product>[i]", + ex); + } + } + + private static ICollection<Product> GetProductICollection + ( + IObjectMappingData<Product[], IEnumerable<Product>> paToPieData + ) + { + ICollection<Product> collection; + return ((collection = paToPieData.Target as ICollection<Product>) != null) + ? collection.IsReadOnly ? new List<Product>(paToPieData.Target) : collection + : new List<Product>(paToPieData.Target); + } + + private static Product GetProduct2 + ( + IEnumerator<Product> enumerator + ) + { + try + { + if (enumerator.Current == null) + { + return null; + } + + var sourceMegaProduct = enumerator.Current as MegaProduct; + + if (sourceMegaProduct != null) + { + return ProductArrayMapper.GetMegaProduct(sourceMegaProduct); + } + var product = new Product(); + + if (product.ProductId == null) + { + product.ProductId = enumerator.Current.ProductId; + } + + if (product.Price == default(double)) + { + product.Price = enumerator.Current.Price; + } + + return product; + } + catch (Exception ex) + { + throw MappingException.For( + "Merge", + "Product[][i]", + "IEnumerable<Product>[i]", + ex); + } + } + + private static MegaProduct GetMegaProduct + ( + MegaProduct sourceMegaProduct + ) + { + try + { + var megaProduct = new MegaProduct(); + + if (megaProduct.HowMega == default(decimal)) + { + megaProduct.HowMega = sourceMegaProduct.HowMega; + } + + if (megaProduct.ProductId == null) + { + megaProduct.ProductId = sourceMegaProduct.ProductId; + } + + if (megaProduct.Price == default(double)) + { + megaProduct.Price = sourceMegaProduct.Price; + } + + return megaProduct; + } + catch (Exception ex) + { + throw MappingException.For( + "Merge", + "Product[][i]", + "IEnumerable<Product>[i]", + ex); + } + } + } +} \ No newline at end of file diff --git a/AgileMapper.Buildable.UnitTests.NetStandard2.Mappers/Mappers/ProductDtoArrayMapper.cs b/AgileMapper.Buildable.UnitTests.NetStandard2.Mappers/Mappers/ProductDtoArrayMapper.cs new file mode 100644 index 000000000..b6808fb20 --- /dev/null +++ b/AgileMapper.Buildable.UnitTests.NetStandard2.Mappers/Mappers/ProductDtoArrayMapper.cs @@ -0,0 +1,236 @@ +// ------------------------------------------------------------------------------ +// <auto-generated> +// This code was generated by AgileObjects.AgileMapper.Buildable. +// Runtime Version: 0.1.0.0 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// </auto-generated> +// ------------------------------------------------------------------------------ + +using System; +using System.CodeDom.Compiler; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using AgileObjects.AgileMapper; +using AgileObjects.AgileMapper.Extensions; +using AgileObjects.AgileMapper.ObjectPopulation; +using AgileObjects.AgileMapper.UnitTests.Common.TestClasses; + +namespace AgileObjects.AgileMapper.Buildable.UnitTests.Mappers +{ + [GeneratedCode("AgileObjects.AgileMapper.Buildable", "0.1.0.0")] + public class ProductDtoArrayMapper : MappingExecutionContextBase<ProductDto[]> + { + public ProductDtoArrayMapper + ( + ProductDto[] source + ) + : base(source) + { + } + + public ReadOnlyCollection<Product> Over + ( + ReadOnlyCollection<Product> target + ) + { + return ProductDtoArrayMapper.Overwrite(this.CreateRootMappingData(target)); + } + + private static ReadOnlyCollection<Product> Overwrite + ( + IObjectMappingData<ProductDto[], ReadOnlyCollection<Product>> pdaToProcData + ) + { + try + { + var collectionData = CollectionData.Create( + pdaToProcData.Source, + pdaToProcData.Target, + pd => pd.ProductId, + p => p.ProductId); + collectionData.Intersection.ForEach( + (existingProductDto, existingProduct, idx) => ProductDtoArrayMapper.GetProduct1(existingProductDto, existingProduct)); + var productDtoIEnumerable = collectionData.NewSourceItems; + var productList = new List<Product>(pdaToProcData.Target); + collectionData.AbsentTargetItems.ForEach(p => productList.Remove(p)); + var i = 0; + var enumerator = productDtoIEnumerable.GetEnumerator(); + try + { + while (true) + { + if (!enumerator.MoveNext()) + { + break; + } + + productList.Add(ProductDtoArrayMapper.GetProduct1(enumerator)); + ++i; + } + } + finally + { + enumerator.Dispose(); + } + + return productList.AsReadOnly(); + } + catch (Exception ex) + { + throw MappingException.For( + "Overwrite", + "ProductDto[]", + "ReadOnlyCollection<Product>", + ex); + } + } + + private static Product GetProduct1 + ( + ProductDto existingProductDto, + Product existingProduct + ) + { + try + { + if (existingProductDto == null) + { + return null; + } + + var sourceProductDtoMega = existingProductDto as ProductDtoMega; + + if (sourceProductDtoMega != null) + { + return ProductDtoArrayMapper.GetProduct2(existingProduct, sourceProductDtoMega); + } + + if (existingProduct is MegaProduct) + { + return ProductDtoArrayMapper.GetMegaProduct(existingProduct, existingProductDto); + } + var product = existingProduct ?? new Product(); + product.ProductId = existingProductDto.ProductId; + product.Price = (double)existingProductDto.Price; + + return product; + } + catch (Exception ex) + { + throw MappingException.For( + "Overwrite", + "ProductDto[][i]", + "ReadOnlyCollection<Product>[i]", + ex); + } + } + + private static Product GetProduct2 + ( + Product existingProduct, + ProductDtoMega sourceProductDtoMega + ) + { + try + { + var product = existingProduct ?? new Product(); + product.ProductId = sourceProductDtoMega.ProductId; + product.Price = (double)sourceProductDtoMega.Price; + + return product; + } + catch (Exception ex) + { + throw MappingException.For( + "Overwrite", + "ProductDto[][i]", + "ReadOnlyCollection<Product>[i]", + ex); + } + } + + private static MegaProduct GetMegaProduct + ( + Product existingProduct, + ProductDto existingProductDto + ) + { + try + { + var megaProduct = ((MegaProduct)existingProduct) ?? new MegaProduct(); + // No data sources for HowMega + megaProduct.ProductId = existingProductDto.ProductId; + megaProduct.Price = (double)existingProductDto.Price; + + return megaProduct; + } + catch (Exception ex) + { + throw MappingException.For( + "Overwrite", + "ProductDto[][i]", + "ReadOnlyCollection<Product>[i]", + ex); + } + } + + private static Product GetProduct1 + ( + IEnumerator<ProductDto> enumerator + ) + { + try + { + if (enumerator.Current == null) + { + return null; + } + + var sourceProductDtoMega = enumerator.Current as ProductDtoMega; + + if (sourceProductDtoMega != null) + { + return ProductDtoArrayMapper.GetProduct2(sourceProductDtoMega); + } + var product = new Product(); + product.ProductId = enumerator.Current.ProductId; + product.Price = (double)enumerator.Current.Price; + + return product; + } + catch (Exception ex) + { + throw MappingException.For( + "Overwrite", + "ProductDto[][i]", + "ReadOnlyCollection<Product>[i]", + ex); + } + } + + private static Product GetProduct2 + ( + ProductDtoMega sourceProductDtoMega + ) + { + try + { + var product = new Product(); + product.ProductId = sourceProductDtoMega.ProductId; + product.Price = (double)sourceProductDtoMega.Price; + + return product; + } + catch (Exception ex) + { + throw MappingException.For( + "Overwrite", + "ProductDto[][i]", + "ReadOnlyCollection<Product>[i]", + ex); + } + } + } +} \ No newline at end of file diff --git a/AgileMapper.Buildable.UnitTests.NetStandard2.Mappers/Mappers/ProductDtoListMapper.cs b/AgileMapper.Buildable.UnitTests.NetStandard2.Mappers/Mappers/ProductDtoListMapper.cs new file mode 100644 index 000000000..e073f7878 --- /dev/null +++ b/AgileMapper.Buildable.UnitTests.NetStandard2.Mappers/Mappers/ProductDtoListMapper.cs @@ -0,0 +1,129 @@ +// ------------------------------------------------------------------------------ +// <auto-generated> +// This code was generated by AgileObjects.AgileMapper.Buildable. +// Runtime Version: 0.1.0.0 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// </auto-generated> +// ------------------------------------------------------------------------------ + +using System; +using System.CodeDom.Compiler; +using System.Collections.Generic; +using AgileObjects.AgileMapper; +using AgileObjects.AgileMapper.ObjectPopulation; +using AgileObjects.AgileMapper.UnitTests.Common.TestClasses; + +namespace AgileObjects.AgileMapper.Buildable.UnitTests.Mappers +{ + [GeneratedCode("AgileObjects.AgileMapper.Buildable", "0.1.0.0")] + public class ProductDtoListMapper : MappingExecutionContextBase<List<ProductDto>> + { + public ProductDtoListMapper + ( + List<ProductDto> source + ) + : base(source) + { + } + + public IList<ProductDto> ToANew<TTarget>() + where TTarget : IList<ProductDto> + { + return ProductDtoListMapper.CreateNew(this.CreateRootMappingData(default(IList<ProductDto>))); + } + + private static IList<ProductDto> CreateNew + ( + IObjectMappingData<List<ProductDto>, IList<ProductDto>> pdlToPdilData + ) + { + try + { + var sourceProductDtoList = pdlToPdilData.Source; + var targetProductDtoList = new List<ProductDto>(sourceProductDtoList.Count); + var i = 0; + while (true) + { + if (i == sourceProductDtoList.Count) + { + break; + } + + var sourceProductDto = sourceProductDtoList[i]; + targetProductDtoList.Add(ProductDtoListMapper.GetProductDto(sourceProductDto)); + ++i; + } + + return targetProductDtoList; + } + catch (Exception ex) + { + throw MappingException.For( + "CreateNew", + "List<ProductDto>", + "IList<ProductDto>", + ex); + } + } + + private static ProductDto GetProductDto + ( + ProductDto sourceProductDto + ) + { + try + { + if (sourceProductDto == null) + { + return null; + } + + var sourceProductDtoMega = sourceProductDto as ProductDtoMega; + + if (sourceProductDtoMega != null) + { + return ProductDtoListMapper.GetProductDtoMega(sourceProductDtoMega); + } + var productDto = new ProductDto(); + productDto.ProductId = sourceProductDto.ProductId; + productDto.Price = sourceProductDto.Price; + + return productDto; + } + catch (Exception ex) + { + throw MappingException.For( + "CreateNew", + "List<ProductDto>[i]", + "IList<ProductDto>[i]", + ex); + } + } + + private static ProductDtoMega GetProductDtoMega + ( + ProductDtoMega sourceProductDtoMega + ) + { + try + { + var productDtoMega = new ProductDtoMega(); + productDtoMega.HowMega = sourceProductDtoMega.HowMega; + productDtoMega.ProductId = sourceProductDtoMega.ProductId; + productDtoMega.Price = sourceProductDtoMega.Price; + + return productDtoMega; + } + catch (Exception ex) + { + throw MappingException.For( + "CreateNew", + "List<ProductDto>[i]", + "IList<ProductDto>[i]", + ex); + } + } + } +} \ No newline at end of file diff --git a/AgileMapper.Buildable.UnitTests.NetStandard2.Mappers/Mappers/ProductMapper.cs b/AgileMapper.Buildable.UnitTests.NetStandard2.Mappers/Mappers/ProductMapper.cs new file mode 100644 index 000000000..612590fb8 --- /dev/null +++ b/AgileMapper.Buildable.UnitTests.NetStandard2.Mappers/Mappers/ProductMapper.cs @@ -0,0 +1,106 @@ +// ------------------------------------------------------------------------------ +// <auto-generated> +// This code was generated by AgileObjects.AgileMapper.Buildable. +// Runtime Version: 0.1.0.0 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// </auto-generated> +// ------------------------------------------------------------------------------ + +using System; +using System.CodeDom.Compiler; +using AgileObjects.AgileMapper; +using AgileObjects.AgileMapper.ObjectPopulation; +using AgileObjects.AgileMapper.UnitTests.Common.TestClasses; +using AgileObjects.NetStandardPolyfills; +using AgileObjects.ReadableExpressions; +using AgileObjects.ReadableExpressions.Extensions; + +namespace AgileObjects.AgileMapper.Buildable.UnitTests.Mappers +{ + [GeneratedCode("AgileObjects.AgileMapper.Buildable", "0.1.0.0")] + public class ProductMapper : MappingExecutionContextBase<Product> + { + public ProductMapper + ( + Product source + ) + : base(source) + { + } + + public TTarget ToANew<TTarget>() + { + if (typeof(TTarget).IsAssignableTo(typeof(ProductDto))) + { + return (TTarget)((object)ProductMapper.CreateNew(this.CreateRootMappingData(default(ProductDto)))); + } + + throw new NotSupportedException( + "Unable to perform a 'CreateNew' mapping from source type 'Product' to target type '" + typeof(TTarget).GetFriendlyName(null) + "'"); + } + + private static ProductDto CreateNew + ( + IObjectMappingData<Product, ProductDto> pToPdData + ) + { + Product sourceProduct; + try + { + sourceProduct = pToPdData.Source; + + var sourceMegaProduct = sourceProduct as MegaProduct; + + if (sourceMegaProduct != null) + { + return ProductMapper.GetProductDtoMega(sourceMegaProduct); + } + var productDto = new ProductDto(); + productDto.ProductId = sourceProduct.ProductId; + productDto.Price = ((sourceProduct.Price >= ((double)-79228162514264337593543950335m)) && + (sourceProduct.Price <= ((double)79228162514264337593543950335m))) + ? (decimal)sourceProduct.Price + : default(decimal); + + return productDto; + } + catch (Exception ex) + { + throw MappingException.For( + "CreateNew", + "Product", + "ProductDto", + ex); + } + } + + private static ProductDtoMega GetProductDtoMega + ( + MegaProduct sourceMegaProduct + ) + { + try + { + var productDtoMega = new ProductDtoMega(); + productDtoMega.HowMega = sourceMegaProduct.HowMega.ToString(); + productDtoMega.ProductId = sourceMegaProduct.ProductId; + productDtoMega.Price = ((sourceMegaProduct.Price >= ((double)-79228162514264337593543950335m)) && + (sourceMegaProduct.Price <= ((double)79228162514264337593543950335m))) + ? (decimal)sourceMegaProduct.Price + : default(decimal); + + return productDtoMega; + } + catch (Exception ex) + { + throw MappingException.For( + "CreateNew", + "MegaProduct", + "ProductDtoMega", + ex); + } + } + } +} \ No newline at end of file diff --git a/AgileMapper.Buildable.UnitTests.NetStandard2.Mappers/Mappers/StringCollectionMapper.cs b/AgileMapper.Buildable.UnitTests.NetStandard2.Mappers/Mappers/StringCollectionMapper.cs new file mode 100644 index 000000000..59b82670e --- /dev/null +++ b/AgileMapper.Buildable.UnitTests.NetStandard2.Mappers/Mappers/StringCollectionMapper.cs @@ -0,0 +1,63 @@ +// ------------------------------------------------------------------------------ +// <auto-generated> +// This code was generated by AgileObjects.AgileMapper.Buildable. +// Runtime Version: 0.1.0.0 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// </auto-generated> +// ------------------------------------------------------------------------------ + +using System; +using System.CodeDom.Compiler; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using AgileObjects.AgileMapper; +using AgileObjects.AgileMapper.ObjectPopulation; + +namespace AgileObjects.AgileMapper.Buildable.UnitTests.Mappers +{ + [GeneratedCode("AgileObjects.AgileMapper.Buildable", "0.1.0.0")] + public class StringCollectionMapper : MappingExecutionContextBase<Collection<string>> + { + public StringCollectionMapper + ( + Collection<string> source + ) + : base(source) + { + } + + public List<string> Over + ( + List<string> target + ) + { + return StringCollectionMapper.Overwrite(this.CreateRootMappingData(target)); + } + + private static List<string> Overwrite + ( + IObjectMappingData<Collection<string>, List<string>> scToSlData + ) + { + try + { + var sourceStringCollection = scToSlData.Source; + var targetStringList = scToSlData.Target; + targetStringList.Clear(); + targetStringList.AddRange(sourceStringCollection); + + return targetStringList; + } + catch (Exception ex) + { + throw MappingException.For( + "Overwrite", + "Collection<string>", + "List<string>", + ex); + } + } + } +} \ No newline at end of file diff --git a/AgileMapper.Buildable.UnitTests.NetStandard2.Mappers/Mappers/StringListMapper.cs b/AgileMapper.Buildable.UnitTests.NetStandard2.Mappers/Mappers/StringListMapper.cs new file mode 100644 index 000000000..f210f24f2 --- /dev/null +++ b/AgileMapper.Buildable.UnitTests.NetStandard2.Mappers/Mappers/StringListMapper.cs @@ -0,0 +1,80 @@ +// ------------------------------------------------------------------------------ +// <auto-generated> +// This code was generated by AgileObjects.AgileMapper.Buildable. +// Runtime Version: 0.1.0.0 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// </auto-generated> +// ------------------------------------------------------------------------------ + +using System; +using System.CodeDom.Compiler; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using AgileObjects.AgileMapper; +using AgileObjects.AgileMapper.ObjectPopulation; + +namespace AgileObjects.AgileMapper.Buildable.UnitTests.Mappers +{ + [GeneratedCode("AgileObjects.AgileMapper.Buildable", "0.1.0.0")] + public class StringListMapper : MappingExecutionContextBase<List<string>> + { + public StringListMapper + ( + List<string> source + ) + : base(source) + { + } + + public Collection<byte?> ToANew<TTarget>() + where TTarget : Collection<Nullable<byte>> + { + return StringListMapper.CreateNew(this.CreateRootMappingData(default(Collection<byte?>))); + } + + private static Collection<byte?> CreateNew + ( + IObjectMappingData<List<string>, Collection<byte?>> slToNbcData + ) + { + try + { + var stringList = slToNbcData.Source; + var nullableByteCollection = new Collection<byte?>(); + var i = 0; + while (true) + { + if (i == stringList.Count) + { + break; + } + + nullableByteCollection.Add(StringListMapper.GetNullableByte(stringList, i)); + ++i; + } + + return nullableByteCollection; + } + catch (Exception ex) + { + throw MappingException.For( + "CreateNew", + "List<string>", + "Collection<byte?>", + ex); + } + } + + private static byte? GetNullableByte + ( + List<string> stringList, + int i + ) + { + byte byteValue; + return byte.TryParse(stringList[i], out byteValue) ? (byte?)byteValue : null; + } + } +} \ No newline at end of file diff --git a/AgileMapper.Buildable.UnitTests.NetStandard2.Mappers/Mappers/StringMapper.cs b/AgileMapper.Buildable.UnitTests.NetStandard2.Mappers/Mappers/StringMapper.cs new file mode 100644 index 000000000..92e74fdf4 --- /dev/null +++ b/AgileMapper.Buildable.UnitTests.NetStandard2.Mappers/Mappers/StringMapper.cs @@ -0,0 +1,130 @@ +// ------------------------------------------------------------------------------ +// <auto-generated> +// This code was generated by AgileObjects.AgileMapper.Buildable. +// Runtime Version: 0.1.0.0 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// </auto-generated> +// ------------------------------------------------------------------------------ + +using System; +using System.CodeDom.Compiler; +using AgileObjects.AgileMapper; +using AgileObjects.AgileMapper.ObjectPopulation; +using AgileObjects.AgileMapper.UnitTests.Common.TestClasses; +using AgileObjects.ReadableExpressions; +using AgileObjects.ReadableExpressions.Extensions; + +namespace AgileObjects.AgileMapper.Buildable.UnitTests.Mappers +{ + [GeneratedCode("AgileObjects.AgileMapper.Buildable", "0.1.0.0")] + public class StringMapper : MappingExecutionContextBase<string> + { + public StringMapper + ( + string source + ) + : base(source) + { + } + + public TTarget ToANew<TTarget>() + { + if (typeof(TTarget) == typeof(Title)) + { + return (TTarget)((object)StringMapper.CreateNew(this.CreateRootMappingData(default(Title)))); + } + + throw new NotSupportedException( + "Unable to perform a 'CreateNew' mapping from source type 'string' to target type '" + typeof(TTarget).GetFriendlyName(null) + "'"); + } + + private static Title CreateNew + ( + IObjectMappingData<string, Title> sToTData + ) + { + try + { + if (sToTData.Source == null) + { + return default(Title); + } + + switch (sToTData.Source.ToUpperInvariant()) + { + case "0": + case "UNKNOWN": + return Title.Unknown; + + case "1": + case "MR": + return Title.Mr; + + case "2": + case "MASTER": + return Title.Master; + + case "3": + case "MS": + return Title.Ms; + + case "4": + case "MISS": + return Title.Miss; + + case "5": + case "MRS": + return Title.Mrs; + + case "6": + case "DR": + return Title.Dr; + + case "7": + case "HON": + return Title.Hon; + + case "8": + case "DUKE": + return Title.Duke; + + case "9": + case "COUNT": + return Title.Count; + + case "10": + case "EARL": + return Title.Earl; + + case "11": + case "VISCOUNT": + return Title.Viscount; + + case "12": + case "LORD": + return Title.Lord; + + case "13": + case "LADY": + return Title.Lady; + + case "14": + case "OTHER": + return Title.Other; + } + + return default(Title); + } + catch (Exception ex) + { + throw MappingException.For( + "CreateNew", + "string", + "Title", + ex); + } + } + } +} \ No newline at end of file diff --git a/AgileMapper.Buildable.UnitTests.NetStandard2.Mappers/Mappers/StringPublicFieldMapper.cs b/AgileMapper.Buildable.UnitTests.NetStandard2.Mappers/Mappers/StringPublicFieldMapper.cs new file mode 100644 index 000000000..f3b5eb6c2 --- /dev/null +++ b/AgileMapper.Buildable.UnitTests.NetStandard2.Mappers/Mappers/StringPublicFieldMapper.cs @@ -0,0 +1,102 @@ +// ------------------------------------------------------------------------------ +// <auto-generated> +// This code was generated by AgileObjects.AgileMapper.Buildable. +// Runtime Version: 0.1.0.0 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// </auto-generated> +// ------------------------------------------------------------------------------ + +using System; +using System.CodeDom.Compiler; +using AgileObjects.AgileMapper; +using AgileObjects.AgileMapper.ObjectPopulation; +using AgileObjects.AgileMapper.UnitTests.Common.TestClasses; +using AgileObjects.NetStandardPolyfills; +using AgileObjects.ReadableExpressions; +using AgileObjects.ReadableExpressions.Extensions; + +namespace AgileObjects.AgileMapper.Buildable.UnitTests.Mappers +{ + [GeneratedCode("AgileObjects.AgileMapper.Buildable", "0.1.0.0")] + public class StringPublicFieldMapper : MappingExecutionContextBase<PublicField<string>> + { + public StringPublicFieldMapper + ( + PublicField<string> source + ) + : base(source) + { + } + + public TTarget ToANew<TTarget>() + { + if (typeof(TTarget).IsAssignableTo(typeof(PublicField<int>))) + { + return (TTarget)((object)StringPublicFieldMapper.CreateNew(this.CreateRootMappingData(default(PublicField<int>)))); + } + + if (typeof(TTarget).IsAssignableTo(typeof(PublicProperty<string>))) + { + return (TTarget)((object)StringPublicFieldMapper.CreateNew(this.CreateRootMappingData(default(PublicProperty<string>)))); + } + + throw new NotSupportedException( + "Unable to perform a 'CreateNew' mapping from source type 'PublicField<string>' to target type '" + typeof(TTarget).GetFriendlyName(null) + "'"); + } + + private static PublicField<int> CreateNew + ( + IObjectMappingData<PublicField<string>, PublicField<int>> spfToIpfData + ) + { + try + { + var intPublicField = new PublicField<int>(); + intPublicField.Value = StringPublicFieldMapper.GetInt(spfToIpfData); + + return intPublicField; + } + catch (Exception ex) + { + throw MappingException.For( + "CreateNew", + "PublicField<string>", + "PublicField<int>", + ex); + } + } + + private static PublicProperty<string> CreateNew + ( + IObjectMappingData<PublicField<string>, PublicProperty<string>> spfToSppData + ) + { + try + { + var stringPublicProperty = new PublicProperty<string>(); + stringPublicProperty.Value = spfToSppData.Source.Value; + + return stringPublicProperty; + } + catch (Exception ex) + { + throw MappingException.For( + "CreateNew", + "PublicField<string>", + "PublicProperty<string>", + ex); + } + } + + private static int GetInt + ( + IObjectMappingData<PublicField<string>, PublicField<int>> spfToIpfData + ) + { + int intValue; + return int.TryParse(spfToIpfData.Source.Value, out intValue) ? intValue : default(int); + } + } +} \ No newline at end of file diff --git a/AgileMapper.Buildable.UnitTests.NetStandard2.Mappers/Mappers/StringPublicPropertyMapper.cs b/AgileMapper.Buildable.UnitTests.NetStandard2.Mappers/Mappers/StringPublicPropertyMapper.cs new file mode 100644 index 000000000..db265810d --- /dev/null +++ b/AgileMapper.Buildable.UnitTests.NetStandard2.Mappers/Mappers/StringPublicPropertyMapper.cs @@ -0,0 +1,67 @@ +// ------------------------------------------------------------------------------ +// <auto-generated> +// This code was generated by AgileObjects.AgileMapper.Buildable. +// Runtime Version: 0.1.0.0 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// </auto-generated> +// ------------------------------------------------------------------------------ + +using System; +using System.CodeDom.Compiler; +using AgileObjects.AgileMapper; +using AgileObjects.AgileMapper.ObjectPopulation; +using AgileObjects.AgileMapper.UnitTests.Common.TestClasses; + +namespace AgileObjects.AgileMapper.Buildable.UnitTests.Mappers +{ + [GeneratedCode("AgileObjects.AgileMapper.Buildable", "0.1.0.0")] + public class StringPublicPropertyMapper : MappingExecutionContextBase<PublicProperty<string>> + { + public StringPublicPropertyMapper + ( + PublicProperty<string> source + ) + : base(source) + { + } + + public PublicField<int> ToANew<TTarget>() + where TTarget : PublicField<int> + { + return StringPublicPropertyMapper.CreateNew(this.CreateRootMappingData(default(PublicField<int>))); + } + + private static PublicField<int> CreateNew + ( + IObjectMappingData<PublicProperty<string>, PublicField<int>> sppToIpfData + ) + { + try + { + var intPublicField = new PublicField<int>(); + intPublicField.Value = StringPublicPropertyMapper.GetInt(sppToIpfData); + + return intPublicField; + } + catch (Exception ex) + { + throw MappingException.For( + "CreateNew", + "PublicProperty<string>", + "PublicField<int>", + ex); + } + } + + private static int GetInt + ( + IObjectMappingData<PublicProperty<string>, PublicField<int>> sppToIpfData + ) + { + int intValue; + return int.TryParse(sppToIpfData.Source.Value, out intValue) ? intValue : default(int); + } + } +} \ No newline at end of file diff --git a/AgileMapper.Buildable.UnitTests/AgileMapper.Buildable.UnitTests.csproj b/AgileMapper.Buildable.UnitTests/AgileMapper.Buildable.UnitTests.csproj index ecd95d5e1..c650a8dd9 100644 --- a/AgileMapper.Buildable.UnitTests/AgileMapper.Buildable.UnitTests.csproj +++ b/AgileMapper.Buildable.UnitTests/AgileMapper.Buildable.UnitTests.csproj @@ -2,7 +2,6 @@ <PropertyGroup> <TargetFramework>net461</TargetFramework> - <LangVersion>8.0</LangVersion> <AssemblyName>AgileObjects.AgileMapper.Buildable.UnitTests</AssemblyName> <RootNamespace>AgileObjects.AgileMapper.Buildable.UnitTests</RootNamespace> <TreatWarningsAsErrors>true</TreatWarningsAsErrors> diff --git a/AgileMapper.Buildable.UnitTests/Configuration/DataSources/WhenBuildingToTargetDataSourceMappers.cs b/AgileMapper.Buildable.UnitTests/Configuration/DataSources/WhenBuildingToTargetDataSourceMappers.cs index 4204c3ebb..da284f0ec 100644 --- a/AgileMapper.Buildable.UnitTests/Configuration/DataSources/WhenBuildingToTargetDataSourceMappers.cs +++ b/AgileMapper.Buildable.UnitTests/Configuration/DataSources/WhenBuildingToTargetDataSourceMappers.cs @@ -2,7 +2,6 @@ { using AgileMapper.UnitTests.Common; using AgileMapper.UnitTests.Common.TestClasses; - using Buildable.Configuration; using Xunit; using GeneratedMapper = Mappers.Mapper; @@ -26,27 +25,5 @@ public void ShouldApplyAToTargetDataSource() result.Value1.ShouldBe(456); result.Value2.ShouldBe(789); } - - #region Configuration - - public class ToTargetDataSourceMapperConfiguration : BuildableMapperConfiguration - { - protected override void Configure() - { - GetPlansFor<ToTargetValueSource<int, string, int>>() - .To<PublicTwoFields<int, int>>(cfg => cfg - .Map(ctx => ctx.Source.Value) - .ToTarget()); - } - } - - #endregion - } - - public class ToTargetValueSource<T1, T2, T3> - { - public T1 Value1 { get; set; } - - public PublicTwoFields<T2, T3> Value { get; set; } } } diff --git a/AgileMapper.Buildable.UnitTests/Dictionaries/WhenBuildingDictionaryCreateNewMappers.cs b/AgileMapper.Buildable.UnitTests/Dictionaries/WhenBuildingDictionaryCreateNewMappers.cs index 0106d144e..c0dd5588f 100644 --- a/AgileMapper.Buildable.UnitTests/Dictionaries/WhenBuildingDictionaryCreateNewMappers.cs +++ b/AgileMapper.Buildable.UnitTests/Dictionaries/WhenBuildingDictionaryCreateNewMappers.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using AgileMapper.UnitTests.Common; using AgileMapper.UnitTests.Common.TestClasses; - using Buildable.Configuration; using Xunit; using GeneratedMapper = Mappers.Mapper; @@ -25,17 +24,5 @@ public void ShouldBuildANestedComplexTypeToStringDictionaryMapper() result.ShouldContainKeyAndValue("Value2.Line1", "Line 1!"); result.ShouldContainKeyAndValue("Value2.Line2", "Line 2!"); } - - #region Configuration - - public class DictionaryCreateNewMapperConfiguration : BuildableMapperConfiguration - { - protected override void Configure() - { - GetPlanFor<PublicTwoFields<int, Address>>().ToANew<Dictionary<string, string>>(); - } - } - - #endregion } } diff --git a/AgileMapper.Buildable.UnitTests/MapperConfiguration/CircularReferenceMapperConfiguration.cs b/AgileMapper.Buildable.UnitTests/MapperConfiguration/CircularReferenceMapperConfiguration.cs new file mode 100644 index 000000000..1da5c942c --- /dev/null +++ b/AgileMapper.Buildable.UnitTests/MapperConfiguration/CircularReferenceMapperConfiguration.cs @@ -0,0 +1,13 @@ +namespace AgileObjects.AgileMapper.Buildable.UnitTests.MapperConfiguration +{ + using AgileMapper.UnitTests.Common.TestClasses; + using Buildable.Configuration; + + public class CircularReferenceMapperConfiguration : BuildableMapperConfiguration + { + protected override void Configure() + { + GetPlanFor<Child>().ToANew<Child>(); + } + } +} \ No newline at end of file diff --git a/AgileMapper.Buildable.UnitTests/MapperConfiguration/ComplexTypeCreateNewMapperConfiguration.cs b/AgileMapper.Buildable.UnitTests/MapperConfiguration/ComplexTypeCreateNewMapperConfiguration.cs new file mode 100644 index 000000000..fb8f04739 --- /dev/null +++ b/AgileMapper.Buildable.UnitTests/MapperConfiguration/ComplexTypeCreateNewMapperConfiguration.cs @@ -0,0 +1,16 @@ +namespace AgileObjects.AgileMapper.Buildable.UnitTests.MapperConfiguration +{ + using AgileMapper.UnitTests.Common.TestClasses; + using Buildable.Configuration; + + public class ComplexTypeCreateNewMapperConfiguration : BuildableMapperConfiguration + { + protected override void Configure() + { + GetPlanFor<PublicProperty<string>>().ToANew<PublicField<int>>(); + + GetPlanFor<PublicField<string>>().ToANew<PublicField<int>>(); + GetPlanFor<PublicField<string>>().ToANew<PublicProperty<string>>(); + } + } +} \ No newline at end of file diff --git a/AgileMapper.Buildable.UnitTests/MapperConfiguration/ComplexTypeMergeMapperConfiguration.cs b/AgileMapper.Buildable.UnitTests/MapperConfiguration/ComplexTypeMergeMapperConfiguration.cs new file mode 100644 index 000000000..0ead3eae8 --- /dev/null +++ b/AgileMapper.Buildable.UnitTests/MapperConfiguration/ComplexTypeMergeMapperConfiguration.cs @@ -0,0 +1,13 @@ +namespace AgileObjects.AgileMapper.Buildable.UnitTests.MapperConfiguration +{ + using AgileMapper.UnitTests.Common.TestClasses; + using Buildable.Configuration; + + public class ComplexTypeMergeMapperConfiguration : BuildableMapperConfiguration + { + protected override void Configure() + { + GetPlanFor<Address>().OnTo<Address>(); + } + } +} \ No newline at end of file diff --git a/AgileMapper.Buildable.UnitTests/MapperConfiguration/ComplexTypeOverwriteMapperConfiguration.cs b/AgileMapper.Buildable.UnitTests/MapperConfiguration/ComplexTypeOverwriteMapperConfiguration.cs new file mode 100644 index 000000000..76cb87522 --- /dev/null +++ b/AgileMapper.Buildable.UnitTests/MapperConfiguration/ComplexTypeOverwriteMapperConfiguration.cs @@ -0,0 +1,13 @@ +namespace AgileObjects.AgileMapper.Buildable.UnitTests.MapperConfiguration +{ + using AgileMapper.UnitTests.Common.TestClasses; + using Buildable.Configuration; + + public class ComplexTypeOverwriteMapperConfiguration : BuildableMapperConfiguration + { + protected override void Configure() + { + GetPlanFor<Address>().Over<Address>(); + } + } +} \ No newline at end of file diff --git a/AgileMapper.Buildable.UnitTests/MapperConfiguration/Configuration/DataSources/ToTargetDataSourceMapperConfiguration.cs b/AgileMapper.Buildable.UnitTests/MapperConfiguration/Configuration/DataSources/ToTargetDataSourceMapperConfiguration.cs new file mode 100644 index 000000000..23730299b --- /dev/null +++ b/AgileMapper.Buildable.UnitTests/MapperConfiguration/Configuration/DataSources/ToTargetDataSourceMapperConfiguration.cs @@ -0,0 +1,16 @@ +namespace AgileObjects.AgileMapper.Buildable.UnitTests.MapperConfiguration.Configuration.DataSources +{ + using AgileMapper.UnitTests.Common.TestClasses; + using Buildable.Configuration; + + public class ToTargetDataSourceMapperConfiguration : BuildableMapperConfiguration + { + protected override void Configure() + { + GetPlansFor<ToTargetValueSource<int, string, int>>() + .To<PublicTwoFields<int, int>>(cfg => cfg + .Map(ctx => ctx.Source.Value) + .ToTarget()); + } + } +} \ No newline at end of file diff --git a/AgileMapper.Buildable.UnitTests/MapperConfiguration/DerivedTypeMapperConfiguration.cs b/AgileMapper.Buildable.UnitTests/MapperConfiguration/DerivedTypeMapperConfiguration.cs new file mode 100644 index 000000000..1019a0aaa --- /dev/null +++ b/AgileMapper.Buildable.UnitTests/MapperConfiguration/DerivedTypeMapperConfiguration.cs @@ -0,0 +1,14 @@ +namespace AgileObjects.AgileMapper.Buildable.UnitTests.MapperConfiguration +{ + using AgileMapper.UnitTests.Common.TestClasses; + using Buildable.Configuration; + + public class DerivedTypeMapperConfiguration : BuildableMapperConfiguration + { + protected override void Configure() + { + GetPlanFor<Product>().ToANew<ProductDto>(cfg => cfg + .Map<MegaProduct>().To<ProductDtoMega>()); + } + } +} \ No newline at end of file diff --git a/AgileMapper.Buildable.UnitTests/MapperConfiguration/Dictionaries/DictionaryCreateNewMapperConfiguration.cs b/AgileMapper.Buildable.UnitTests/MapperConfiguration/Dictionaries/DictionaryCreateNewMapperConfiguration.cs new file mode 100644 index 000000000..8c4ae87dc --- /dev/null +++ b/AgileMapper.Buildable.UnitTests/MapperConfiguration/Dictionaries/DictionaryCreateNewMapperConfiguration.cs @@ -0,0 +1,14 @@ +namespace AgileObjects.AgileMapper.Buildable.UnitTests.MapperConfiguration.Dictionaries +{ + using System.Collections.Generic; + using AgileMapper.UnitTests.Common.TestClasses; + using Buildable.Configuration; + + public class DictionaryCreateNewMapperConfiguration : BuildableMapperConfiguration + { + protected override void Configure() + { + GetPlanFor<PublicTwoFields<int, Address>>().ToANew<Dictionary<string, string>>(); + } + } +} \ No newline at end of file diff --git a/AgileMapper.Buildable.UnitTests/MapperConfiguration/EnumerableCreateNewMapperConfiguration.cs b/AgileMapper.Buildable.UnitTests/MapperConfiguration/EnumerableCreateNewMapperConfiguration.cs new file mode 100644 index 000000000..af929d309 --- /dev/null +++ b/AgileMapper.Buildable.UnitTests/MapperConfiguration/EnumerableCreateNewMapperConfiguration.cs @@ -0,0 +1,22 @@ +namespace AgileObjects.AgileMapper.Buildable.UnitTests.MapperConfiguration +{ + using System; + using System.Collections.Generic; + using System.Collections.ObjectModel; + using AgileMapper.UnitTests.Common.TestClasses; + using Buildable.Configuration; + + public class EnumerableCreateNewMapperConfiguration : BuildableMapperConfiguration + { + protected override void Configure() + { + GetPlanFor<List<string>>().ToANew<Collection<byte?>>(); + + GetPlanFor<int[]>().ToANew<ReadOnlyCollection<int>>(); + + GetPlanFor<HashSet<DateTime>>().ToANew<DateTime[]>(); + + GetPlanFor<List<ProductDto>>().ToANew<IList<ProductDto>>(); + } + } +} \ No newline at end of file diff --git a/AgileMapper.Buildable.UnitTests/MapperConfiguration/EnumerableMergeMapperConfiguration.cs b/AgileMapper.Buildable.UnitTests/MapperConfiguration/EnumerableMergeMapperConfiguration.cs new file mode 100644 index 000000000..b3744b79f --- /dev/null +++ b/AgileMapper.Buildable.UnitTests/MapperConfiguration/EnumerableMergeMapperConfiguration.cs @@ -0,0 +1,18 @@ +namespace AgileObjects.AgileMapper.Buildable.UnitTests.MapperConfiguration +{ + using System.Collections.Generic; + using AgileMapper.UnitTests.Common.TestClasses; + using Buildable.Configuration; + + public class EnumerableMergeMapperConfiguration : BuildableMapperConfiguration + { + protected override void Configure() + { + GetPlanFor<IEnumerable<int>>().OnTo<ICollection<int>>(); + + GetPlanFor<decimal[]>().OnTo<HashSet<double>>(); + + GetPlanFor<Product[]>().OnTo<IEnumerable<Product>>(); + } + } +} \ No newline at end of file diff --git a/AgileMapper.Buildable.UnitTests/MapperConfiguration/EnumerableOverwriteMapperConfiguration.cs b/AgileMapper.Buildable.UnitTests/MapperConfiguration/EnumerableOverwriteMapperConfiguration.cs new file mode 100644 index 000000000..17153628e --- /dev/null +++ b/AgileMapper.Buildable.UnitTests/MapperConfiguration/EnumerableOverwriteMapperConfiguration.cs @@ -0,0 +1,19 @@ +namespace AgileObjects.AgileMapper.Buildable.UnitTests.MapperConfiguration +{ + using System.Collections.Generic; + using System.Collections.ObjectModel; + using AgileMapper.UnitTests.Common.TestClasses; + using Buildable.Configuration; + + public class EnumerableOverwriteMapperConfiguration : BuildableMapperConfiguration + { + protected override void Configure() + { + GetPlanFor<char[]>().Over<int[]>(); + + GetPlanFor<Collection<string>>().Over<List<string>>(); + + GetPlanFor<ProductDto[]>().Over<ReadOnlyCollection<Product>>(); + } + } +} \ No newline at end of file diff --git a/AgileMapper.Buildable.UnitTests/MapperConfiguration/RootEnumMapperConfiguration.cs b/AgileMapper.Buildable.UnitTests/MapperConfiguration/RootEnumMapperConfiguration.cs new file mode 100644 index 000000000..037220e96 --- /dev/null +++ b/AgileMapper.Buildable.UnitTests/MapperConfiguration/RootEnumMapperConfiguration.cs @@ -0,0 +1,13 @@ +namespace AgileObjects.AgileMapper.Buildable.UnitTests.MapperConfiguration +{ + using AgileMapper.UnitTests.Common.TestClasses; + using Buildable.Configuration; + + public class RootEnumMapperConfiguration : BuildableMapperConfiguration + { + protected override void Configure() + { + GetPlanFor<string>().ToANew<Title>(); + } + } +} \ No newline at end of file diff --git a/AgileMapper.Buildable.UnitTests/Mappers/IntStringIntToTargetValueSourceMapper.cs b/AgileMapper.Buildable.UnitTests/Mappers/IntStringIntToTargetValueSourceMapper.cs index 9c221695a..ab63e2a18 100644 --- a/AgileMapper.Buildable.UnitTests/Mappers/IntStringIntToTargetValueSourceMapper.cs +++ b/AgileMapper.Buildable.UnitTests/Mappers/IntStringIntToTargetValueSourceMapper.cs @@ -11,7 +11,6 @@ using System; using System.CodeDom.Compiler; using AgileObjects.AgileMapper; -using AgileObjects.AgileMapper.Buildable.UnitTests.Configuration.DataSources; using AgileObjects.AgileMapper.ObjectPopulation; using AgileObjects.AgileMapper.UnitTests.Common.TestClasses; diff --git a/AgileMapper.Buildable.UnitTests/Mappers/Mapper.cs b/AgileMapper.Buildable.UnitTests/Mappers/Mapper.cs index 127a212e1..674b9f324 100644 --- a/AgileMapper.Buildable.UnitTests/Mappers/Mapper.cs +++ b/AgileMapper.Buildable.UnitTests/Mappers/Mapper.cs @@ -12,7 +12,6 @@ using System.CodeDom.Compiler; using System.Collections.Generic; using System.Collections.ObjectModel; -using AgileObjects.AgileMapper.Buildable.UnitTests.Configuration.DataSources; using AgileObjects.AgileMapper.UnitTests.Common.TestClasses; namespace AgileObjects.AgileMapper.Buildable.UnitTests.Mappers diff --git a/AgileMapper.Buildable.UnitTests/WhenBuildingCircularReferenceMappers.cs b/AgileMapper.Buildable.UnitTests/WhenBuildingCircularReferenceMappers.cs index 5170a3d81..1d880eedb 100644 --- a/AgileMapper.Buildable.UnitTests/WhenBuildingCircularReferenceMappers.cs +++ b/AgileMapper.Buildable.UnitTests/WhenBuildingCircularReferenceMappers.cs @@ -2,7 +2,6 @@ namespace AgileObjects.AgileMapper.Buildable.UnitTests { using AgileMapper.UnitTests.Common; using AgileMapper.UnitTests.Common.TestClasses; - using Buildable.Configuration; using Xunit; using GeneratedMapper = Mappers.Mapper; @@ -46,17 +45,5 @@ public void ShouldBuildACircularReferenceMapper() result.EldestParent.EldestChild.EldestParent.Name.ShouldBe("Franklin"); result.EldestParent.EldestChild.EldestParent.EldestChild.ShouldBeSameAs(result); } - - #region Configuration - - public class CircularReferenceMapperConfiguration : BuildableMapperConfiguration - { - protected override void Configure() - { - GetPlanFor<Child>().ToANew<Child>(); - } - } - - #endregion } } \ No newline at end of file diff --git a/AgileMapper.Buildable.UnitTests/WhenBuildingComplexTypeCreateNewMappers.cs b/AgileMapper.Buildable.UnitTests/WhenBuildingComplexTypeCreateNewMappers.cs index ecfb5d568..146f13f7f 100644 --- a/AgileMapper.Buildable.UnitTests/WhenBuildingComplexTypeCreateNewMappers.cs +++ b/AgileMapper.Buildable.UnitTests/WhenBuildingComplexTypeCreateNewMappers.cs @@ -3,7 +3,6 @@ namespace AgileObjects.AgileMapper.Buildable.UnitTests using System; using AgileMapper.UnitTests.Common; using AgileMapper.UnitTests.Common.TestClasses; - using Buildable.Configuration; using Xunit; using GeneratedMapper = Mappers.Mapper; @@ -39,20 +38,5 @@ public void ShouldBuildSingleSourceMultipleTargetMapper() notSupportedMessage.ShouldContain("source type 'PublicField<string>'"); notSupportedMessage.ShouldContain("target type 'PublicField<DateTime>'"); } - - #region Configuration - - public class ComplexTypeCreateNewMapperConfiguration : BuildableMapperConfiguration - { - protected override void Configure() - { - GetPlanFor<PublicProperty<string>>().ToANew<PublicField<int>>(); - - GetPlanFor<PublicField<string>>().ToANew<PublicField<int>>(); - GetPlanFor<PublicField<string>>().ToANew<PublicProperty<string>>(); - } - } - - #endregion } } \ No newline at end of file diff --git a/AgileMapper.Buildable.UnitTests/WhenBuildingComplexTypeMergeMappers.cs b/AgileMapper.Buildable.UnitTests/WhenBuildingComplexTypeMergeMappers.cs index 48629d316..ade0d1641 100644 --- a/AgileMapper.Buildable.UnitTests/WhenBuildingComplexTypeMergeMappers.cs +++ b/AgileMapper.Buildable.UnitTests/WhenBuildingComplexTypeMergeMappers.cs @@ -2,7 +2,6 @@ namespace AgileObjects.AgileMapper.Buildable.UnitTests { using AgileMapper.UnitTests.Common; using AgileObjects.AgileMapper.UnitTests.Common.TestClasses; - using Buildable.Configuration; using Xunit; using GeneratedMapper = Mappers.Mapper; @@ -19,17 +18,5 @@ public void ShouldBuildASingleSourceSingleTargetMapper() target.Line1.ShouldBe("Line 1!"); target.Line2.ShouldBe("Line 2!"); } - - #region Configuration - - public class ComplexTypeMergeMapperConfiguration : BuildableMapperConfiguration - { - protected override void Configure() - { - GetPlanFor<Address>().OnTo<Address>(); - } - } - - #endregion } } diff --git a/AgileMapper.Buildable.UnitTests/WhenBuildingComplexTypeOverwriteMappers.cs b/AgileMapper.Buildable.UnitTests/WhenBuildingComplexTypeOverwriteMappers.cs index 816afae87..d7453843c 100644 --- a/AgileMapper.Buildable.UnitTests/WhenBuildingComplexTypeOverwriteMappers.cs +++ b/AgileMapper.Buildable.UnitTests/WhenBuildingComplexTypeOverwriteMappers.cs @@ -2,7 +2,6 @@ namespace AgileObjects.AgileMapper.Buildable.UnitTests { using AgileMapper.UnitTests.Common; using AgileMapper.UnitTests.Common.TestClasses; - using Buildable.Configuration; using Xunit; using GeneratedMapper = Mappers.Mapper; @@ -19,17 +18,5 @@ public void ShouldBuildASingleSourceSingleTargetMapper() target.Line1.ShouldBe("1.1"); target.Line2.ShouldBe("1.2"); } - - #region Configuration - - public class ComplexTypeOverwriteMapperConfiguration : BuildableMapperConfiguration - { - protected override void Configure() - { - GetPlanFor<Address>().Over<Address>(); - } - } - - #endregion } } \ No newline at end of file diff --git a/AgileMapper.Buildable.UnitTests/WhenBuildingDerivedTypeMappers.cs b/AgileMapper.Buildable.UnitTests/WhenBuildingDerivedTypeMappers.cs index 797f31b1b..53995ee71 100644 --- a/AgileMapper.Buildable.UnitTests/WhenBuildingDerivedTypeMappers.cs +++ b/AgileMapper.Buildable.UnitTests/WhenBuildingDerivedTypeMappers.cs @@ -2,7 +2,6 @@ { using AgileMapper.UnitTests.Common; using AgileMapper.UnitTests.Common.TestClasses; - using Buildable.Configuration; using Xunit; using GeneratedMapper = Mappers.Mapper; @@ -41,18 +40,5 @@ public void ShouldBuildADerivedTypeCreateNewMapper() derivedTypeToDerivedTypeResult.Price.ShouldBe(119.99m); derivedTypeToDerivedTypeResult.HowMega.ShouldBe("1.0"); } - - #region Configuration - - public class DerivedTypeMapperConfiguration : BuildableMapperConfiguration - { - protected override void Configure() - { - GetPlanFor<Product>().ToANew<ProductDto>(cfg => cfg - .Map<MegaProduct>().To<ProductDtoMega>()); - } - } - - #endregion } } diff --git a/AgileMapper.Buildable.UnitTests/WhenBuildingEnumerableCreateNewMappers.cs b/AgileMapper.Buildable.UnitTests/WhenBuildingEnumerableCreateNewMappers.cs index 3d88b6f0d..44f46c0c0 100644 --- a/AgileMapper.Buildable.UnitTests/WhenBuildingEnumerableCreateNewMappers.cs +++ b/AgileMapper.Buildable.UnitTests/WhenBuildingEnumerableCreateNewMappers.cs @@ -6,7 +6,6 @@ using System.Linq; using AgileMapper.UnitTests.Common; using AgileMapper.UnitTests.Common.TestClasses; - using Buildable.Configuration; using Xunit; using GeneratedMapper = Mappers.Mapper; @@ -63,23 +62,5 @@ public void ShouldBuildAComplexTypeListToIListMapper() result.Second().ShouldBeNull(); result.Third().ShouldNotBeNull().ProductId.ShouldBe("Boomstick"); } - - #region Configuration - - public class EnumerableCreateNewMapperConfiguration : BuildableMapperConfiguration - { - protected override void Configure() - { - GetPlanFor<List<string>>().ToANew<Collection<byte?>>(); - - GetPlanFor<int[]>().ToANew<ReadOnlyCollection<int>>(); - - GetPlanFor<HashSet<DateTime>>().ToANew<DateTime[]>(); - - GetPlanFor<List<ProductDto>>().ToANew<IList<ProductDto>>(); - } - } - - #endregion } } diff --git a/AgileMapper.Buildable.UnitTests/WhenBuildingEnumerableMergeMappers.cs b/AgileMapper.Buildable.UnitTests/WhenBuildingEnumerableMergeMappers.cs index d2eff741a..543e1087d 100644 --- a/AgileMapper.Buildable.UnitTests/WhenBuildingEnumerableMergeMappers.cs +++ b/AgileMapper.Buildable.UnitTests/WhenBuildingEnumerableMergeMappers.cs @@ -4,7 +4,6 @@ using System.Collections.ObjectModel; using AgileMapper.UnitTests.Common; using AgileMapper.UnitTests.Common.TestClasses; - using Buildable.Configuration; using Xunit; using GeneratedMapper = Mappers.Mapper; @@ -50,21 +49,5 @@ public void ShouldBuildAComplexTypeArrayToIEnumerableMapper() result.ShouldNotBeNull().ShouldBe(p => p.ProductId, "Kate", "Steve"); } - - #region Configuration - - public class EnumerableMergeMapperConfiguration : BuildableMapperConfiguration - { - protected override void Configure() - { - GetPlanFor<IEnumerable<int>>().OnTo<ICollection<int>>(); - - GetPlanFor<decimal[]>().OnTo<HashSet<double>>(); - - GetPlanFor<Product[]>().OnTo<IEnumerable<Product>>(); - } - } - - #endregion } } diff --git a/AgileMapper.Buildable.UnitTests/WhenBuildingEnumerableOverwriteMappers.cs b/AgileMapper.Buildable.UnitTests/WhenBuildingEnumerableOverwriteMappers.cs index 64651f57e..801af3070 100644 --- a/AgileMapper.Buildable.UnitTests/WhenBuildingEnumerableOverwriteMappers.cs +++ b/AgileMapper.Buildable.UnitTests/WhenBuildingEnumerableOverwriteMappers.cs @@ -5,7 +5,6 @@ using System.Linq; using AgileMapper.UnitTests.Common; using AgileMapper.UnitTests.Common.TestClasses; - using Buildable.Configuration; using Xunit; using GeneratedMapper = Mappers.Mapper; @@ -58,21 +57,5 @@ public void ShouldBuildAComplexTypeArrayToReadOnlyCollectionMapper() result.Second().ProductId.ShouldBe("1"); result.Second().Price.ShouldBe(1.99); } - - #region Configuration - - public class EnumerableOverwriteMapperConfiguration : BuildableMapperConfiguration - { - protected override void Configure() - { - GetPlanFor<char[]>().Over<int[]>(); - - GetPlanFor<Collection<string>>().Over<List<string>>(); - - GetPlanFor<ProductDto[]>().Over<ReadOnlyCollection<Product>>(); - } - } - - #endregion } } diff --git a/AgileMapper.Buildable.UnitTests/WhenBuildingRootEnumMappers.cs b/AgileMapper.Buildable.UnitTests/WhenBuildingRootEnumMappers.cs index d5c7a419b..f75653a09 100644 --- a/AgileMapper.Buildable.UnitTests/WhenBuildingRootEnumMappers.cs +++ b/AgileMapper.Buildable.UnitTests/WhenBuildingRootEnumMappers.cs @@ -2,7 +2,6 @@ { using AgileMapper.UnitTests.Common; using AgileMapper.UnitTests.Common.TestClasses; - using Buildable.Configuration; using Xunit; using GeneratedMapper = Mappers.Mapper; @@ -19,17 +18,5 @@ public void ShouldBuildARootEnumMapper() var enumLabelResult = GeneratedMapper.Map(enumLabelSource).ToANew<Title>(); enumLabelResult.ShouldBe(Title.Master); } - - #region Configuration - - public class RootEnumMapperConfiguration : BuildableMapperConfiguration - { - protected override void Configure() - { - GetPlanFor<string>().ToANew<Title>(); - } - } - - #endregion } } diff --git a/AgileMapper.Buildable/MSBuild/AgileObjects.AgileMapper.Buildable.targets b/AgileMapper.Buildable/MSBuild/AgileObjects.AgileMapper.Buildable.targets index 75e4782fb..465f55abc 100644 --- a/AgileMapper.Buildable/MSBuild/AgileObjects.AgileMapper.Buildable.targets +++ b/AgileMapper.Buildable/MSBuild/AgileObjects.AgileMapper.Buildable.targets @@ -1,4 +1,10 @@ <?xml version="1.0" encoding="utf-8"?> <Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <PropertyGroup> + <XprGenerator Condition="'$(MappersGenerator)' != ''">$(MappersGenerator)</XprGenerator> + <XprGeneratorOutputProject Condition="'$(MappersOutputProject)' != ''">$(MappersOutputProject)</XprGeneratorOutputProject> + <XprGeneratorLoggerPrefix>AgileMapper.Buildable:</XprGeneratorLoggerPrefix> + </PropertyGroup> + <Import Project="$(NuGetPackageRoot)agileobjects.buildableexpressions.generator\*\build\AgileObjects.BuildableExpressions.Generator.targets" Condition="Exists('$(NuGetPackageRoot)agileobjects.buildableexpressions.generator\0.1.0-preview1\build\AgileObjects.BuildableExpressions.Generator.targets')" /> </Project> \ No newline at end of file diff --git a/AgileMapper.UnitTests.Common/TestClasses/ToTargetValueSource.cs b/AgileMapper.UnitTests.Common/TestClasses/ToTargetValueSource.cs new file mode 100644 index 000000000..6f5f74f8c --- /dev/null +++ b/AgileMapper.UnitTests.Common/TestClasses/ToTargetValueSource.cs @@ -0,0 +1,9 @@ +namespace AgileObjects.AgileMapper.UnitTests.Common.TestClasses +{ + public class ToTargetValueSource<T1, T2, T3> + { + public T1 Value1 { get; set; } + + public PublicTwoFields<T2, T3> Value { get; set; } + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.NetCore2/AgileMapper.UnitTests.NetCore2.csproj b/AgileMapper.UnitTests.NetCore2/AgileMapper.UnitTests.NetCore2.csproj index 94c8b6fa7..e0aa554d3 100644 --- a/AgileMapper.UnitTests.NetCore2/AgileMapper.UnitTests.NetCore2.csproj +++ b/AgileMapper.UnitTests.NetCore2/AgileMapper.UnitTests.NetCore2.csproj @@ -1,9 +1,7 @@ <Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> - <TargetFramework>netcoreapp2.2</TargetFramework> - <RuntimeFrameworkVersion>2.2.7</RuntimeFrameworkVersion> - <LangVersion>8.0</LangVersion> + <TargetFramework>netcoreapp2.1</TargetFramework> <AssemblyName>AgileObjects.AgileMapper.UnitTests.NetCore2</AssemblyName> <RootNamespace>AgileObjects.AgileMapper.UnitTests</RootNamespace> <TreatWarningsAsErrors>true</TreatWarningsAsErrors> diff --git a/AgileMapper.sln b/AgileMapper.sln index 4a7c736f2..1a132b130 100644 --- a/AgileMapper.sln +++ b/AgileMapper.sln @@ -61,6 +61,12 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AgileMapper.Buildable", "Ag EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AgileMapper.Buildable.UnitTests", "AgileMapper.Buildable.UnitTests\AgileMapper.Buildable.UnitTests.csproj", "{B305D680-C129-4785-B02B-10F293E19A75}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AgileMapper.Buildable.UnitTests.NetCore3", "AgileMapper.Buildable.UnitTests.NetCore3\AgileMapper.Buildable.UnitTests.NetCore3.csproj", "{C7726D80-F144-430B-8C07-E500BE75217F}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AgileMapper.Buildable.UnitTests.NetStandard2.Mappers", "AgileMapper.Buildable.UnitTests.NetStandard2.Mappers\AgileMapper.Buildable.UnitTests.NetStandard2.Mappers.csproj", "{2FD6FF7D-658B-495B-911B-6FC6D227D0B5}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AgileMapper.Buildable.UnitTests.NetStandard2.Generator", "AgileMapper.Buildable.UnitTests.NetStandard2.Generator\AgileMapper.Buildable.UnitTests.NetStandard2.Generator.csproj", "{8D62F519-4FA2-48A5-9E1E-B6E0A3A90D4E}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -151,6 +157,18 @@ Global {B305D680-C129-4785-B02B-10F293E19A75}.Debug|Any CPU.Build.0 = Debug|Any CPU {B305D680-C129-4785-B02B-10F293E19A75}.Release|Any CPU.ActiveCfg = Release|Any CPU {B305D680-C129-4785-B02B-10F293E19A75}.Release|Any CPU.Build.0 = Release|Any CPU + {C7726D80-F144-430B-8C07-E500BE75217F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C7726D80-F144-430B-8C07-E500BE75217F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C7726D80-F144-430B-8C07-E500BE75217F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C7726D80-F144-430B-8C07-E500BE75217F}.Release|Any CPU.Build.0 = Release|Any CPU + {2FD6FF7D-658B-495B-911B-6FC6D227D0B5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2FD6FF7D-658B-495B-911B-6FC6D227D0B5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2FD6FF7D-658B-495B-911B-6FC6D227D0B5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2FD6FF7D-658B-495B-911B-6FC6D227D0B5}.Release|Any CPU.Build.0 = Release|Any CPU + {8D62F519-4FA2-48A5-9E1E-B6E0A3A90D4E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8D62F519-4FA2-48A5-9E1E-B6E0A3A90D4E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8D62F519-4FA2-48A5-9E1E-B6E0A3A90D4E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8D62F519-4FA2-48A5-9E1E-B6E0A3A90D4E}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -178,6 +196,9 @@ Global {72287439-1634-4164-ABAC-E4A462235C5D} = {88D48136-E176-4AF7-ACA4-A2954F3BD55B} {F55FFD5B-EF1F-4937-B2D2-B3324F9962D6} = {C0C3194B-D7C8-470F-8B7D-47BA75E0EDAE} {B305D680-C129-4785-B02B-10F293E19A75} = {C0C3194B-D7C8-470F-8B7D-47BA75E0EDAE} + {C7726D80-F144-430B-8C07-E500BE75217F} = {C0C3194B-D7C8-470F-8B7D-47BA75E0EDAE} + {2FD6FF7D-658B-495B-911B-6FC6D227D0B5} = {C0C3194B-D7C8-470F-8B7D-47BA75E0EDAE} + {8D62F519-4FA2-48A5-9E1E-B6E0A3A90D4E} = {C0C3194B-D7C8-470F-8B7D-47BA75E0EDAE} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {C39E9BDB-0077-445C-8F09-801CAE1AF8AB} diff --git a/Directory.Build.props b/Directory.Build.props index d95ebbf86..726688721 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,6 +1,7 @@ <Project> <PropertyGroup> + <LangVersion>latest</LangVersion> <Company>AgileObjects Ltd</Company> <Product>AgileObjects.AgileMapper</Product> <Authors>Steve Wilkes</Authors> From 0915756119102ddb1901908ca4504018e9d8ed98 Mon Sep 17 00:00:00 2001 From: Steve Wilkes <steve@agileobjects.co.uk> Date: Wed, 23 Jun 2021 11:06:25 +0100 Subject: [PATCH 51/65] Surfacing MappingDataFactory / Adding CopyLocalLockFileAssemblies to Buildable targets --- ...le.UnitTests.NetStandard2.Generator.csproj | 1 - .../AgileMapper.Buildable.csproj | 6 ++- ...AgileObjects.AgileMapper.Buildable.targets | 6 ++- AgileMapper/AgileMapper.csproj | 2 +- .../ObjectPopulation/MappingDataFactory.cs | 40 ++++++++++++++++--- README.md | 3 +- 6 files changed, 47 insertions(+), 11 deletions(-) diff --git a/AgileMapper.Buildable.UnitTests.NetStandard2.Generator/AgileMapper.Buildable.UnitTests.NetStandard2.Generator.csproj b/AgileMapper.Buildable.UnitTests.NetStandard2.Generator/AgileMapper.Buildable.UnitTests.NetStandard2.Generator.csproj index af02ced33..4309960f6 100644 --- a/AgileMapper.Buildable.UnitTests.NetStandard2.Generator/AgileMapper.Buildable.UnitTests.NetStandard2.Generator.csproj +++ b/AgileMapper.Buildable.UnitTests.NetStandard2.Generator/AgileMapper.Buildable.UnitTests.NetStandard2.Generator.csproj @@ -4,7 +4,6 @@ <TargetFramework>netstandard2.0</TargetFramework> <AssemblyName>AgileObjects.AgileMapper.Buildable.UnitTests.NetStandard2.Generator</AssemblyName> <RootNamespace>AgileObjects.AgileMapper.Buildable.UnitTests.Generator</RootNamespace> - <CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies> </PropertyGroup> <PropertyGroup> diff --git a/AgileMapper.Buildable/AgileMapper.Buildable.csproj b/AgileMapper.Buildable/AgileMapper.Buildable.csproj index f9c9b8785..8c9e13826 100644 --- a/AgileMapper.Buildable/AgileMapper.Buildable.csproj +++ b/AgileMapper.Buildable/AgileMapper.Buildable.csproj @@ -15,11 +15,15 @@ <PackageId>AgileObjects.AgileMapper.Buildable</PackageId> <Title>AgileMapper.Buildable - Mapper, Mapping, Mappings, ViewModel, DTO, NetStandard + Mapper, Mapping, "Code Generation" https://github.com/AgileObjects/AgileMapper MIT ../NuGet + + true + 0.1.0-preview1 0.1.0.0 0.1.0.0 diff --git a/AgileMapper.Buildable/MSBuild/AgileObjects.AgileMapper.Buildable.targets b/AgileMapper.Buildable/MSBuild/AgileObjects.AgileMapper.Buildable.targets index 465f55abc..9aeb9a767 100644 --- a/AgileMapper.Buildable/MSBuild/AgileObjects.AgileMapper.Buildable.targets +++ b/AgileMapper.Buildable/MSBuild/AgileObjects.AgileMapper.Buildable.targets @@ -4,7 +4,11 @@ $(MappersGenerator) $(MappersOutputProject) AgileMapper.Buildable: + + + true - + \ No newline at end of file diff --git a/AgileMapper/AgileMapper.csproj b/AgileMapper/AgileMapper.csproj index 168a47d98..1c34d93ea 100644 --- a/AgileMapper/AgileMapper.csproj +++ b/AgileMapper/AgileMapper.csproj @@ -14,7 +14,7 @@ AgileObjects.AgileMapper AgileMapper A zero-configuration, highly-configurable, unopinionated object mapper with viewable execution plans. Flattens, unflattens, deep clones, merges, updates and projects queries. Targets .NET 3.5+ and .NET Standard 1.0+ - Mapper, Mapping, Mappings, ViewModel, DTO, NetStandard + Mapper, Mapping, ViewModel, DTO https://github.com/AgileObjects/AgileMapper MIT ./Icon.png diff --git a/AgileMapper/ObjectPopulation/MappingDataFactory.cs b/AgileMapper/ObjectPopulation/MappingDataFactory.cs index 99d326bc5..a211135cb 100644 --- a/AgileMapper/ObjectPopulation/MappingDataFactory.cs +++ b/AgileMapper/ObjectPopulation/MappingDataFactory.cs @@ -6,18 +6,36 @@ namespace AgileObjects.AgileMapper.ObjectPopulation using MapperKeys; using NetStandardPolyfills; - internal static class MappingDataFactory + /// + /// Factory class to create instances. This + /// class is used internally by mapping functions. + /// + public static class MappingDataFactory { private static MethodInfo _forChildMethod; private static MethodInfo _forElementMethod; - public static MethodInfo ForChildMethod => + internal static MethodInfo ForChildMethod => _forChildMethod ??= typeof(MappingDataFactory).GetPublicStaticMethod(nameof(ForChild)); - public static MethodInfo ForElementMethod + internal static MethodInfo ForElementMethod => _forElementMethod ??= typeof(MappingDataFactory).GetPublicStaticMethod(nameof(ForElement)); - public static ObjectMappingData ForChild( + /// + /// Creates an instance for the given data, + /// for the mapping of a runtime-typed child member. + /// + /// The declared type of the source object. + /// The declared type of the target object. + /// The child source object. + /// The child target object. + /// The index of the current enumerable element being mapped, if applicable. + /// The current Dictionary KeyValuePair being mapped, if applicable. + /// The name of the target member being mapped. + /// The index of the source value being used. + /// The describing the parent mapping context. + /// An instance for the given data. + public static IObjectMappingData ForChild( TSource source, TTarget target, int? elementIndex, @@ -50,7 +68,19 @@ public static ObjectMappingData ForChild( return mappingData; } - public static ObjectMappingData ForElement( + /// + /// Creates an instance for the given data, + /// for the mapping of a runtime-typed collection element. + /// + /// The declared type of the source element. + /// The declared type of the target element. + /// The source element object. + /// The target element object. + /// The index of the current enumerable element being mapped, if applicable. + /// The current Dictionary KeyValuePair being mapped, if applicable. + /// The describing the parent mapping context. + /// An instance for the given data. + public static IObjectMappingData ForElement( TSourceElement sourceElement, TTargetElement targetElement, int elementIndex, diff --git a/README.md b/README.md index f59ea4b4f..3d296135f 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,11 @@ # AgileMapper [![NuGet version](https://badge.fury.io/nu/AgileObjects.AgileMapper.svg)](https://badge.fury.io/nu/AgileObjects.AgileMapper) -[![Build status](https://ci.appveyor.com/api/projects/status/c1jlvkfjej62p8da?svg=true)](https://ci.appveyor.com/project/SteveWilkes/agilemapper) AgileMapper is a zero-configuration, [highly-configurable](https://agilemapper.readthedocs.io/configuration), unopinionated object mapper with [viewable execution plans](https://agilemapper.readthedocs.io/Using-Execution-Plans). It flattens, unflattens, deep clones, [merges](https://agilemapper.readthedocs.io/Performing-Merges), -[updates](https://agilemapper.readthedocs.io/Performing-Updates) and [projects queries](https://agilemapper.readthedocs.io/query-projection/) +[updates](https://agilemapper.readthedocs.io/Performing-Updates) and [projects queries](https://agilemapper.readthedocs.io/query-projection) via [extension methods](https://agilemapper.readthedocs.io/Mapping-Extension-Methods), or a [static or instance](https://agilemapper.readthedocs.io/Static-vs-Instance-Mappers) API. It targets .NET 3.5+ and [.NET Standard 1.0+](https://docs.microsoft.com/en-us/dotnet/articles/standard/library). From 47756464b954e3bb8cdbacacf5a832b38a883d2d Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Tue, 29 Jun 2021 13:27:46 +0100 Subject: [PATCH 52/65] Using shared AO documentation styling --- docs/src/css/styles.css | 62 ------------------------------------- docs/src/css/styles.min.css | 1 - docs/src/index.md | 6 ++-- mkdocs.yml | 4 +-- 4 files changed, 6 insertions(+), 67 deletions(-) delete mode 100644 docs/src/css/styles.css delete mode 100644 docs/src/css/styles.min.css diff --git a/docs/src/css/styles.css b/docs/src/css/styles.css deleted file mode 100644 index 452233b07..000000000 --- a/docs/src/css/styles.css +++ /dev/null @@ -1,62 +0,0 @@ -body { - font: 400 11pt 'Segoe UI', Calibri, Arial, sans-serif !important; -} - -h1, h2, h3, h4, input[type=submit] { - font-family: 'Segoe UI Light', Calibri, Arial, sans-serif !important; - margin: 0 0 .5em; -} - -h1 { - font-size: 2.5em; -} - -h2 { - font-size: 2em; -} - -h3 { - font-size: 1.6em; -} - -h4 { - font-size: 1.2em; - margin-left: .7em; -} - -p code, li code { - border: none; - padding: 2px 0; - font-size: 11pt; - color: black; -} - -pre code { - font-size: 11pt; -} - -span.caption-text { - color: #dcdcdc; - background: #1e1e1e; -} - -p a code { - text-decoration: underline; -} - -.wy-menu-vertical li.current a { - color: #1e1e1e; -} - -li.current { - border-top: 1px solid #010101; - border-bottom: 1px solid #010101; -} - -.hljs-type, .hljs-keyword, .hljs-title, .hljs-meta-string, .hljs-meta { - font-weight: normal !important; -} - -.hljs-meta { - color: #4EC9B0; -} diff --git a/docs/src/css/styles.min.css b/docs/src/css/styles.min.css deleted file mode 100644 index 1ff85e603..000000000 --- a/docs/src/css/styles.min.css +++ /dev/null @@ -1 +0,0 @@ -body{font:400 11pt 'Segoe UI',Calibri,Arial,sans-serif!important}h1,h2,h3,h4,input[type=submit]{font-family:'Segoe UI Light',Calibri,Arial,sans-serif!important;margin:0 0 .5em}h1{font-size:2.5em}h2{font-size:2em}h3{font-size:1.6em}h4{font-size:1.2em;margin-left:.7em}li code,p code,pre code{font-size:11pt}li code,p code{border:none;padding:2px 0;color:#000}span.caption-text{color:#dcdcdc;background:#1e1e1e}p a code{text-decoration:underline}.wy-menu-vertical li.current a{color:#1e1e1e}li.current{border-top:1px solid #010101;border-bottom:1px solid #010101}.hljs-keyword,.hljs-meta,.hljs-meta-string,.hljs-title,.hljs-type{font-weight:400!important}.hljs-meta{color:#4EC9B0} \ No newline at end of file diff --git a/docs/src/index.md b/docs/src/index.md index 09769ee86..5ee83a9fe 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -1,12 +1,14 @@ ## Overview -AgileMapper is a zero-configuration, [highly-configurable](/configuration) object-object mapper with [viewable execution plans](/Using-Execution-Plans), targeting [.NET Standard 1.0+](https://docs.microsoft.com/en-us/dotnet/articles/standard/library) and .NET 3.5+. It performs [query projections](/query-projection), object creation, deep clones, id-aware [updates](/Performing-Updates) and [merges](/Performing-Merges), and can be used via [extension methods](/Mapping-Extension-Methods), or a [static or instance](/Static-vs-Instance-Mappers) API. +AgileMapper is a zero-configuration, [highly-configurable](/configuration), unopinionated object-object mapper with [viewable execution plans](/Using-Execution-Plans), targeting .NET 3.5+ and [.NET Standard 1.0+](https://docs.microsoft.com/en-us/dotnet/articles/standard/library). It flattens, unflattens, deep clones, [merges](/Performing-Merges), [updates](/Performing-Updates) and [projects queries](/query-projection) via [extension methods](/Mapping-Extension-Methods), or a [static or instance](/Static-vs-Instance-Mappers) API. Mapping functions are created and cached the first time two types are mapped - no up-front configuration is necessary. You can [cache up-front](/Using-Execution-Plans) if you prefer, though. [Available via NuGet](https://www.nuget.org/packages/AgileObjects.AgileMapper) and licensed with the [MIT licence](https://github.com/agileobjects/AgileMapper/blob/master/LICENCE.md), you can install it via the [package manager console](https://docs.nuget.org/consume/package-manager-console): - PM> Install-Package AgileObjects.AgileMapper +```shell +PM> Install-Package AgileObjects.AgileMapper +``` [![NuGet version](https://badge.fury.io/nu/AgileObjects.AgileMapper.svg)](https://badge.fury.io/nu/AgileObjects.AgileMapper) diff --git a/mkdocs.yml b/mkdocs.yml index 3a1b305f7..17da1800f 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -1,5 +1,5 @@ site_name: AgileMapper -site_description: 'AgileMapper: A zero-configuration, highly-configurable object-object mapper with viewable execution plans. Projects queries, transforms, deep clones, updates and merges via extension methods, or a static or instance API. Targets .NET Standard 1.0+ and .NET 3.5+' +site_description: 'AgileMapper: A zero-configuration, highly-configurable, unopinionated object-object mapper with viewable execution plans. Flattens, unflattens, deep clones, merges, updates and projects queries via extension methods, or a static or instance API. Targets .NET 3.5+ and .NET Standard 1.0+.' site_author: Steve Wilkes repo_url: https://github.com/agileobjects/AgileMapper @@ -8,7 +8,7 @@ site_dir: 'docs/site' extra_css: - https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.13.1/styles/vs2015.min.css - - css/styles.min.css + - https://agileobjects.co.uk/assets/css/docs-styles.min.css extra_javascript: - scripts/docs.min.js From 39496973582be6b4eb341ce296ca74ffeaf4aa09 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Tue, 29 Jun 2021 14:58:15 +0100 Subject: [PATCH 53/65] Reusing shared AO dox script --- docs/src/scripts/docs.js | 25 ------------------------- docs/src/scripts/docs.min.js | 1 - docs/src/scripts/dox-cls.js | 1 + mkdocs.yml | 3 ++- 4 files changed, 3 insertions(+), 27 deletions(-) delete mode 100644 docs/src/scripts/docs.js delete mode 100644 docs/src/scripts/docs.min.js create mode 100644 docs/src/scripts/dox-cls.js diff --git a/docs/src/scripts/docs.js b/docs/src/scripts/docs.js deleted file mode 100644 index 8261aa084..000000000 --- a/docs/src/scripts/docs.js +++ /dev/null @@ -1,25 +0,0 @@ -$(function () { - var hlCode = document.querySelectorAll('pre code.cs'), - i, l, - hlLength = hlCode.length, - mapperRegex = /\bMapper\b/g, - typeRegex = /(new<\/span>\W+|class<\/span> |public<\/span>\W+|: |<)([A-Z][^& \(\[\]]+)( |{|\(|\[\]>|>)/g, - genericTypeRegex = /(I{0,1}Dictionary|IEnumerable|IReadOnlyCollection|I{0,1}Collection|I{0,1}List)</g, - observer = new MutationObserver(function (mutations) { - for (i = 0, l = mutations.length; i < l; ++i) { - var mutation = mutations[i]; - if (mutation.attributeName === 'class') { - var innerHTML = mutation.target.innerHTML - .replace(mapperRegex, 'Mapper') - .replace(typeRegex, '$1$2$3') - .replace(genericTypeRegex, '$1<'); - mutation.target.innerHTML = innerHTML; - } - } - }), - config = { attributes: true }; - - for (i = 0; i < hlLength; ++i) { - observer.observe(hlCode[i], config); - } -}); \ No newline at end of file diff --git a/docs/src/scripts/docs.min.js b/docs/src/scripts/docs.min.js deleted file mode 100644 index 4963897cb..000000000 --- a/docs/src/scripts/docs.min.js +++ /dev/null @@ -1 +0,0 @@ -$(function () { var e, a, s = document.querySelectorAll("pre code.cs"), t = s.length, l = /\bMapper\b/g, n = /(\[|new<\/span>\W+|class<\/span> |public<\/span>\W+|: |<)([A-Z][^& \(\[\]]+)(\]| |{|\(|\[\]>|>)/g, r = /(I{0,1}Dictionary|IEnumerable|IReadOnlyCollection|I{0,1}Collection|I{0,1}List)</g, p = new MutationObserver(function (s) { for (e = 0, a = s.length; e < a; ++e) { var t = s[e]; if ("class" === t.attributeName) { var p = t.target.innerHTML.replace(l, 'Mapper').replace(n, '$1$2$3').replace(r, '$1<'); t.target.innerHTML = p } } }), c = { attributes: !0 }; for (e = 0; e < t; ++e)p.observe(s[e], c) }); \ No newline at end of file diff --git a/docs/src/scripts/dox-cls.js b/docs/src/scripts/dox-cls.js new file mode 100644 index 000000000..bba0fd2ce --- /dev/null +++ b/docs/src/scripts/dox-cls.js @@ -0,0 +1 @@ +window['dox-cls'] = 'Mapper'; \ No newline at end of file diff --git a/mkdocs.yml b/mkdocs.yml index 17da1800f..cd96a9e96 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -11,7 +11,8 @@ extra_css: - https://agileobjects.co.uk/assets/css/docs-styles.min.css extra_javascript: - - scripts/docs.min.js + - assets/scripts/dox-cls.js + - https://agileobjects.co.uk/assets/js/ao-docs.min.js nav: - General Use: From 9d3260d05edaf01b08c101298680daae61b0dad6 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Tue, 29 Jun 2021 16:34:38 +0100 Subject: [PATCH 54/65] Updating release notes --- AgileMapper/AgileMapper.csproj | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/AgileMapper/AgileMapper.csproj b/AgileMapper/AgileMapper.csproj index 1c34d93ea..7ecc85903 100644 --- a/AgileMapper/AgileMapper.csproj +++ b/AgileMapper/AgileMapper.csproj @@ -18,11 +18,11 @@ https://github.com/AgileObjects/AgileMapper MIT ./Icon.png - - Adding .NET Standard 2.0 target -- Support for configuring data sources by matcher, re: #208 -- Fixing potential cache threading issue, re: #212 -- Throwing ObjectDisposed on attempt to use disposed Mapper, re: #212 -- Improving target member selection, re: #209 + - Improving surfaced mapping plans +- Improving enum mapping +- Improving non-readable member derived target type mapping +- Improving simple-type enumerable merge and overwrite mapping +- Improving performance and memory use 2.0.0-preview1 2.0.0.0 From 844c325f908ce8e5b07ff0b87a1300432aad9774 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Fri, 6 Aug 2021 09:37:02 +0100 Subject: [PATCH 55/65] Tidying project files --- AgileMapper.Buildable/AgileMapper.Buildable.csproj | 1 - .../AgileMapper.UnitTests.Net35.csproj | 1 - .../AgileMapper.UnitTests.NetCore.csproj | 1 - .../AgileMapper.UnitTests.NetCore3.csproj | 2 -- .../AgileMapper.UnitTests.NonParallel.csproj | 1 - .../AgileMapper.UnitTests.Orms.Ef5.LocalDb.csproj | 1 - .../AgileMapper.UnitTests.Orms.Ef5.csproj | 1 - .../AgileMapper.UnitTests.Orms.Ef6.LocalDb.csproj | 1 - .../AgileMapper.UnitTests.Orms.Ef6.csproj | 1 - .../AgileMapper.UnitTests.Orms.EfCore1.csproj | 1 - .../AgileMapper.UnitTests.Orms.EfCore2.csproj | 4 +--- AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj | 1 - AgileMapper.UnitTests/AgileMapper.UnitTests.csproj | 1 - AgileMapper.sln.DotSettings | 2 ++ AgileMapper/AgileMapper.csproj | 3 +-- 15 files changed, 4 insertions(+), 18 deletions(-) diff --git a/AgileMapper.Buildable/AgileMapper.Buildable.csproj b/AgileMapper.Buildable/AgileMapper.Buildable.csproj index 8c9e13826..1a1f799fd 100644 --- a/AgileMapper.Buildable/AgileMapper.Buildable.csproj +++ b/AgileMapper.Buildable/AgileMapper.Buildable.csproj @@ -2,7 +2,6 @@ net461;netstandard2.0 - 8.0 AgileObjects.AgileMapper.Buildable AgileObjects.AgileMapper.Buildable AgileObjects.AgileMapper.Buildable diff --git a/AgileMapper.UnitTests.Net35/AgileMapper.UnitTests.Net35.csproj b/AgileMapper.UnitTests.Net35/AgileMapper.UnitTests.Net35.csproj index 37b52c67a..88010025d 100644 --- a/AgileMapper.UnitTests.Net35/AgileMapper.UnitTests.Net35.csproj +++ b/AgileMapper.UnitTests.Net35/AgileMapper.UnitTests.Net35.csproj @@ -2,7 +2,6 @@ net35 - 8.0 AgileObjects.AgileMapper.UnitTests.Net35 AgileObjects.AgileMapper.UnitTests.Net35 true diff --git a/AgileMapper.UnitTests.NetCore/AgileMapper.UnitTests.NetCore.csproj b/AgileMapper.UnitTests.NetCore/AgileMapper.UnitTests.NetCore.csproj index 0d100e0e2..22fa55ed0 100644 --- a/AgileMapper.UnitTests.NetCore/AgileMapper.UnitTests.NetCore.csproj +++ b/AgileMapper.UnitTests.NetCore/AgileMapper.UnitTests.NetCore.csproj @@ -2,7 +2,6 @@ netcoreapp1.0 - 8.0 AgileObjects.AgileMapper.UnitTests.NetCore AgileObjects.AgileMapper.UnitTests true diff --git a/AgileMapper.UnitTests.NetCore3/AgileMapper.UnitTests.NetCore3.csproj b/AgileMapper.UnitTests.NetCore3/AgileMapper.UnitTests.NetCore3.csproj index 989c825d2..a2e953764 100644 --- a/AgileMapper.UnitTests.NetCore3/AgileMapper.UnitTests.NetCore3.csproj +++ b/AgileMapper.UnitTests.NetCore3/AgileMapper.UnitTests.NetCore3.csproj @@ -2,8 +2,6 @@ netcoreapp3.1 - 3.1.4 - 8.0 AgileObjects.AgileMapper.UnitTests.NetCore3 AgileObjects.AgileMapper.UnitTests true diff --git a/AgileMapper.UnitTests.NonParallel/AgileMapper.UnitTests.NonParallel.csproj b/AgileMapper.UnitTests.NonParallel/AgileMapper.UnitTests.NonParallel.csproj index 5d44d2ce7..f4d38bc3f 100644 --- a/AgileMapper.UnitTests.NonParallel/AgileMapper.UnitTests.NonParallel.csproj +++ b/AgileMapper.UnitTests.NonParallel/AgileMapper.UnitTests.NonParallel.csproj @@ -2,7 +2,6 @@ net461 - 8.0 true AgileObjects.AgileMapper.UnitTests.NonParallel AgileObjects.AgileMapper.UnitTests.NonParallel diff --git a/AgileMapper.UnitTests.Orms.Ef5.LocalDb/AgileMapper.UnitTests.Orms.Ef5.LocalDb.csproj b/AgileMapper.UnitTests.Orms.Ef5.LocalDb/AgileMapper.UnitTests.Orms.Ef5.LocalDb.csproj index a34ec4c00..4a37aa76c 100644 --- a/AgileMapper.UnitTests.Orms.Ef5.LocalDb/AgileMapper.UnitTests.Orms.Ef5.LocalDb.csproj +++ b/AgileMapper.UnitTests.Orms.Ef5.LocalDb/AgileMapper.UnitTests.Orms.Ef5.LocalDb.csproj @@ -2,7 +2,6 @@ net461 - 8.0 true AgileObjects.AgileMapper.UnitTests.Orms.Ef5.LocalDb AgileObjects.AgileMapper.UnitTests.Orms.Ef5.LocalDb diff --git a/AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj b/AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj index 9156466ca..bef174629 100644 --- a/AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj +++ b/AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj @@ -2,7 +2,6 @@ net461 - 8.0 true AgileObjects.AgileMapper.UnitTests.Orms.Ef5 AgileObjects.AgileMapper.UnitTests.Orms.Ef5 diff --git a/AgileMapper.UnitTests.Orms.Ef6.LocalDb/AgileMapper.UnitTests.Orms.Ef6.LocalDb.csproj b/AgileMapper.UnitTests.Orms.Ef6.LocalDb/AgileMapper.UnitTests.Orms.Ef6.LocalDb.csproj index 8f1068748..519a404a7 100644 --- a/AgileMapper.UnitTests.Orms.Ef6.LocalDb/AgileMapper.UnitTests.Orms.Ef6.LocalDb.csproj +++ b/AgileMapper.UnitTests.Orms.Ef6.LocalDb/AgileMapper.UnitTests.Orms.Ef6.LocalDb.csproj @@ -2,7 +2,6 @@ net461 - 8.0 true AgileObjects.AgileMapper.UnitTests.Orms.Ef6.LocalDb AgileObjects.AgileMapper.UnitTests.Orms.Ef6.LocalDb diff --git a/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj b/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj index 3b471b0e4..1e7fee258 100644 --- a/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj +++ b/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj @@ -2,7 +2,6 @@ net461 - 8.0 true AgileObjects.AgileMapper.UnitTests.Orms.Ef6 AgileObjects.AgileMapper.UnitTests.Orms.Ef6 diff --git a/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj b/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj index 850752dc3..5f6515da5 100644 --- a/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj +++ b/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj @@ -2,7 +2,6 @@ netcoreapp1.0 - 8.0 AgileObjects.AgileMapper.UnitTests.Orms.EfCore1 AgileObjects.AgileMapper.UnitTests.Orms.EfCore1 true diff --git a/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj b/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj index 12fd2962a..dc055819c 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj +++ b/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj @@ -1,9 +1,7 @@  - netcoreapp2.2 - 2.2.7 - 8.0 + netcoreapp2.1 AgileObjects.AgileMapper.UnitTests.Orms.EfCore2 AgileObjects.AgileMapper.UnitTests.Orms.EfCore2 true diff --git a/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj b/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj index 51585c10e..0365e1eff 100644 --- a/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj +++ b/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj @@ -2,7 +2,6 @@ net461;netstandard1.3 - 8.0 AgileObjects.AgileMapper.UnitTests.Orms AgileObjects.AgileMapper.UnitTests.Orms full diff --git a/AgileMapper.UnitTests/AgileMapper.UnitTests.csproj b/AgileMapper.UnitTests/AgileMapper.UnitTests.csproj index d22a0af49..b56e9d4da 100644 --- a/AgileMapper.UnitTests/AgileMapper.UnitTests.csproj +++ b/AgileMapper.UnitTests/AgileMapper.UnitTests.csproj @@ -2,7 +2,6 @@ net461 - 8.0 AgileObjects.AgileMapper.UnitTests AgileObjects.AgileMapper.UnitTests true diff --git a/AgileMapper.sln.DotSettings b/AgileMapper.sln.DotSettings index ec676422d..e1b1995be 100644 --- a/AgileMapper.sln.DotSettings +++ b/AgileMapper.sln.DotSettings @@ -44,5 +44,7 @@ <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + True + True <data><IncludeFilters /><ExcludeFilters /></data> <data><AttributeFilter ClassMask="AgileObjects.AgileMapper.ExcludeFromCodeCoverageAttribute" IsEnabled="True" /></data> \ No newline at end of file diff --git a/AgileMapper/AgileMapper.csproj b/AgileMapper/AgileMapper.csproj index 7ecc85903..d3023f5bf 100644 --- a/AgileMapper/AgileMapper.csproj +++ b/AgileMapper/AgileMapper.csproj @@ -2,7 +2,6 @@ net35;net40;netstandard1.0;netstandard1.3;netstandard2.0 - latest AgileObjects.AgileMapper AgileObjects.AgileMapper AgileObjects.AgileMapper @@ -39,7 +38,7 @@ - + From 3d30c7c2cc890832bdfaff2607843d6721c21867 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Fri, 6 Aug 2021 19:14:01 +0100 Subject: [PATCH 56/65] Fixing local variable removal in derived type query projection --- AgileMapper/ObjectPopulation/MappingExpressionFactoryBase.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/AgileMapper/ObjectPopulation/MappingExpressionFactoryBase.cs b/AgileMapper/ObjectPopulation/MappingExpressionFactoryBase.cs index 13e861dca..7b3106705 100644 --- a/AgileMapper/ObjectPopulation/MappingExpressionFactoryBase.cs +++ b/AgileMapper/ObjectPopulation/MappingExpressionFactoryBase.cs @@ -458,9 +458,10 @@ private static bool TryAdjustForUnusedLocalVariableIfApplicable( { var mapperData = context.MapperData; - if (!mapperData.Context.UseLocalVariable || + if (!mapperData.RuleSet.Settings.UseSingleRootMappingExpression && ( + !mapperData.Context.UseLocalVariable || mapperData.ReturnLabelUsed || - context.ToTargetDataSources.Any()) + context.ToTargetDataSources.Any())) { localVariableUnused = false; returnExpression = null; From 25de9b5f903c17fa50ba1970870dbecb64ccf81b Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Sat, 7 Aug 2021 11:31:54 +0100 Subject: [PATCH 57/65] Updating packages --- .../AgileMapper.PerformanceTesting.csproj | 2 +- .../AgileMapper.UnitTests.Orms.Ef5.csproj | 2 +- .../AgileMapper.UnitTests.Orms.Ef6.csproj | 2 +- AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj | 2 +- AgileMapper.UnitTests/AgileMapper.UnitTests.csproj | 4 ++-- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/AgileMapper.PerformanceTesting/AgileMapper.PerformanceTesting.csproj b/AgileMapper.PerformanceTesting/AgileMapper.PerformanceTesting.csproj index 8c7a7569b..a16bd6d14 100644 --- a/AgileMapper.PerformanceTesting/AgileMapper.PerformanceTesting.csproj +++ b/AgileMapper.PerformanceTesting/AgileMapper.PerformanceTesting.csproj @@ -9,7 +9,7 @@ - + diff --git a/AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj b/AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj index bef174629..32d6274c0 100644 --- a/AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj +++ b/AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj @@ -15,7 +15,7 @@ - + diff --git a/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj b/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj index 1e7fee258..1409e233b 100644 --- a/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj +++ b/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj @@ -15,7 +15,7 @@ - + diff --git a/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj b/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj index 0365e1eff..2572eb1f9 100644 --- a/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj +++ b/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj @@ -13,7 +13,7 @@ - + diff --git a/AgileMapper.UnitTests/AgileMapper.UnitTests.csproj b/AgileMapper.UnitTests/AgileMapper.UnitTests.csproj index b56e9d4da..0eabf13ac 100644 --- a/AgileMapper.UnitTests/AgileMapper.UnitTests.csproj +++ b/AgileMapper.UnitTests/AgileMapper.UnitTests.csproj @@ -19,8 +19,8 @@ - - + + all From a28518144d39033f9e8dbaf572e8aa4799d144d3 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Sat, 7 Aug 2021 11:47:30 +0100 Subject: [PATCH 58/65] Generating mapping extension methods --- .../Mappers/Extensions/MappingExtensions.cs | 167 ++++++++++++++++++ .../Mappers/Extensions/MappingExtensions.cs | 167 ++++++++++++++++++ ...WhenBuildingComplexTypeCreateNewMappers.cs | 3 +- .../WhenBuildingRootEnumMappers.cs | 13 +- .../BuildableMapperExtensions.cs | 32 ++++ 5 files changed, 379 insertions(+), 3 deletions(-) create mode 100644 AgileMapper.Buildable.UnitTests.NetStandard2.Mappers/Mappers/Extensions/MappingExtensions.cs create mode 100644 AgileMapper.Buildable.UnitTests/Mappers/Extensions/MappingExtensions.cs diff --git a/AgileMapper.Buildable.UnitTests.NetStandard2.Mappers/Mappers/Extensions/MappingExtensions.cs b/AgileMapper.Buildable.UnitTests.NetStandard2.Mappers/Mappers/Extensions/MappingExtensions.cs new file mode 100644 index 000000000..25395eef7 --- /dev/null +++ b/AgileMapper.Buildable.UnitTests.NetStandard2.Mappers/Mappers/Extensions/MappingExtensions.cs @@ -0,0 +1,167 @@ +// ------------------------------------------------------------------------------ +// +// This code was generated by AgileObjects.AgileMapper.Buildable. +// Runtime Version: 0.1.0.0 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +// ------------------------------------------------------------------------------ + +using System; +using System.CodeDom.Compiler; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using AgileObjects.AgileMapper.Buildable.UnitTests.Mappers; +using AgileObjects.AgileMapper.UnitTests.Common.TestClasses; + +namespace AgileObjects.AgileMapper.Buildable.UnitTests.Mappers.Extensions +{ + [GeneratedCode("AgileObjects.AgileMapper.Buildable", "0.1.0.0")] + public static class MappingExtensions + { + public static AddressMapper Map + ( + this Address source + ) + { + return new AddressMapper(source); + } + + public static CharArrayMapper Map + ( + this char[] source + ) + { + return new CharArrayMapper(source); + } + + public static ChildMapper Map + ( + this Child source + ) + { + return new ChildMapper(source); + } + + public static DateTimeHashSetMapper Map + ( + this HashSet source + ) + { + return new DateTimeHashSetMapper(source); + } + + public static DecimalArrayMapper Map + ( + this decimal[] source + ) + { + return new DecimalArrayMapper(source); + } + + public static IntAddressPublicTwoFieldsMapper Map + ( + this PublicTwoFields source + ) + { + return new IntAddressPublicTwoFieldsMapper(source); + } + + public static IntArrayMapper Map + ( + this int[] source + ) + { + return new IntArrayMapper(source); + } + + public static IntIEnumerableMapper Map + ( + this IEnumerable source + ) + { + return new IntIEnumerableMapper(source); + } + + public static IntStringIntToTargetValueSourceMapper Map + ( + this ToTargetValueSource source + ) + { + return new IntStringIntToTargetValueSourceMapper(source); + } + + public static ProductArrayMapper Map + ( + this Product[] source + ) + { + return new ProductArrayMapper(source); + } + + public static ProductDtoArrayMapper Map + ( + this ProductDto[] source + ) + { + return new ProductDtoArrayMapper(source); + } + + public static ProductDtoListMapper Map + ( + this List source + ) + { + return new ProductDtoListMapper(source); + } + + public static ProductMapper Map + ( + this Product source + ) + { + return new ProductMapper(source); + } + + public static StringCollectionMapper Map + ( + this Collection source + ) + { + return new StringCollectionMapper(source); + } + + public static StringListMapper Map + ( + this List source + ) + { + return new StringListMapper(source); + } + + public static StringMapper Map + ( + this string source + ) + { + return new StringMapper(source); + } + + public static StringPublicFieldMapper Map + ( + this PublicField source + ) + { + return new StringPublicFieldMapper(source); + } + + public static StringPublicPropertyMapper Map + ( + this PublicProperty source + ) + { + return new StringPublicPropertyMapper(source); + } + } +} \ No newline at end of file diff --git a/AgileMapper.Buildable.UnitTests/Mappers/Extensions/MappingExtensions.cs b/AgileMapper.Buildable.UnitTests/Mappers/Extensions/MappingExtensions.cs new file mode 100644 index 000000000..25395eef7 --- /dev/null +++ b/AgileMapper.Buildable.UnitTests/Mappers/Extensions/MappingExtensions.cs @@ -0,0 +1,167 @@ +// ------------------------------------------------------------------------------ +// +// This code was generated by AgileObjects.AgileMapper.Buildable. +// Runtime Version: 0.1.0.0 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +// ------------------------------------------------------------------------------ + +using System; +using System.CodeDom.Compiler; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using AgileObjects.AgileMapper.Buildable.UnitTests.Mappers; +using AgileObjects.AgileMapper.UnitTests.Common.TestClasses; + +namespace AgileObjects.AgileMapper.Buildable.UnitTests.Mappers.Extensions +{ + [GeneratedCode("AgileObjects.AgileMapper.Buildable", "0.1.0.0")] + public static class MappingExtensions + { + public static AddressMapper Map + ( + this Address source + ) + { + return new AddressMapper(source); + } + + public static CharArrayMapper Map + ( + this char[] source + ) + { + return new CharArrayMapper(source); + } + + public static ChildMapper Map + ( + this Child source + ) + { + return new ChildMapper(source); + } + + public static DateTimeHashSetMapper Map + ( + this HashSet source + ) + { + return new DateTimeHashSetMapper(source); + } + + public static DecimalArrayMapper Map + ( + this decimal[] source + ) + { + return new DecimalArrayMapper(source); + } + + public static IntAddressPublicTwoFieldsMapper Map + ( + this PublicTwoFields source + ) + { + return new IntAddressPublicTwoFieldsMapper(source); + } + + public static IntArrayMapper Map + ( + this int[] source + ) + { + return new IntArrayMapper(source); + } + + public static IntIEnumerableMapper Map + ( + this IEnumerable source + ) + { + return new IntIEnumerableMapper(source); + } + + public static IntStringIntToTargetValueSourceMapper Map + ( + this ToTargetValueSource source + ) + { + return new IntStringIntToTargetValueSourceMapper(source); + } + + public static ProductArrayMapper Map + ( + this Product[] source + ) + { + return new ProductArrayMapper(source); + } + + public static ProductDtoArrayMapper Map + ( + this ProductDto[] source + ) + { + return new ProductDtoArrayMapper(source); + } + + public static ProductDtoListMapper Map + ( + this List source + ) + { + return new ProductDtoListMapper(source); + } + + public static ProductMapper Map + ( + this Product source + ) + { + return new ProductMapper(source); + } + + public static StringCollectionMapper Map + ( + this Collection source + ) + { + return new StringCollectionMapper(source); + } + + public static StringListMapper Map + ( + this List source + ) + { + return new StringListMapper(source); + } + + public static StringMapper Map + ( + this string source + ) + { + return new StringMapper(source); + } + + public static StringPublicFieldMapper Map + ( + this PublicField source + ) + { + return new StringPublicFieldMapper(source); + } + + public static StringPublicPropertyMapper Map + ( + this PublicProperty source + ) + { + return new StringPublicPropertyMapper(source); + } + } +} \ No newline at end of file diff --git a/AgileMapper.Buildable.UnitTests/WhenBuildingComplexTypeCreateNewMappers.cs b/AgileMapper.Buildable.UnitTests/WhenBuildingComplexTypeCreateNewMappers.cs index 146f13f7f..297dc00aa 100644 --- a/AgileMapper.Buildable.UnitTests/WhenBuildingComplexTypeCreateNewMappers.cs +++ b/AgileMapper.Buildable.UnitTests/WhenBuildingComplexTypeCreateNewMappers.cs @@ -3,6 +3,7 @@ namespace AgileObjects.AgileMapper.Buildable.UnitTests using System; using AgileMapper.UnitTests.Common; using AgileMapper.UnitTests.Common.TestClasses; + using Mappers.Extensions; using Xunit; using GeneratedMapper = Mappers.Mapper; @@ -23,7 +24,7 @@ public void ShouldBuildSingleSourceMultipleTargetMapper() var publicFieldResult = GeneratedMapper.Map(source).ToANew>(); publicFieldResult.Value.ShouldBe(456); - var publicPropertyResult = GeneratedMapper.Map(source).ToANew>(); + var publicPropertyResult = source.Map().ToANew>(); publicPropertyResult.Value.ShouldBe("456"); var notSupportedEx = Should.Throw(() => diff --git a/AgileMapper.Buildable.UnitTests/WhenBuildingRootEnumMappers.cs b/AgileMapper.Buildable.UnitTests/WhenBuildingRootEnumMappers.cs index f75653a09..410e0cf87 100644 --- a/AgileMapper.Buildable.UnitTests/WhenBuildingRootEnumMappers.cs +++ b/AgileMapper.Buildable.UnitTests/WhenBuildingRootEnumMappers.cs @@ -2,6 +2,7 @@ { using AgileMapper.UnitTests.Common; using AgileMapper.UnitTests.Common.TestClasses; + using Mappers.Extensions; using Xunit; using GeneratedMapper = Mappers.Mapper; @@ -14,9 +15,17 @@ public void ShouldBuildARootEnumMapper() var enumIdResult = GeneratedMapper.Map(enumIdSource).ToANew(); enumIdResult.ShouldBe(Title.Mrs); - var enumLabelSource = Title.Master.ToString(); - var enumLabelResult = GeneratedMapper.Map(enumLabelSource).ToANew<Title>(); + const string ENUM_LABEL_SOURCE = nameof(Title.Master); + var enumLabelResult = GeneratedMapper.Map(ENUM_LABEL_SOURCE).ToANew<Title>(); enumLabelResult.ShouldBe(Title.Master); } + + [Fact] + public void ShouldBuildARootEnumMappingExtensionMethod() + { + const string ENUM_LABEL_SOURCE = nameof(Title.Count); + var enumLabelResult = ENUM_LABEL_SOURCE.Map().ToANew<Title>(); + enumLabelResult.ShouldBe(Title.Count); + } } } diff --git a/AgileMapper.Buildable/BuildableMapperExtensions.cs b/AgileMapper.Buildable/BuildableMapperExtensions.cs index 2f0ea5c42..57705a68f 100644 --- a/AgileMapper.Buildable/BuildableMapperExtensions.cs +++ b/AgileMapper.Buildable/BuildableMapperExtensions.cs @@ -136,6 +136,38 @@ public static IEnumerable<SourceCodeExpression> GetPlanSourceCodeInCache( } }); }); + + yield return BuildableExpression.SourceCode(sourceCode => + { + sourceCode.AddGeneratedCodeHeader(); + sourceCode.SetNamespace(mappersNamespace+ ".Extensions"); + + sourceCode.AddClass("MappingExtensions", mappingExtensionsClass => + { + mappingExtensionsClass.AddGeneratedCodeAttribute(); + mappingExtensionsClass.SetStatic(); + + foreach (var mapperClassGroup in mapperClassGroups) + { + var sourceType = mapperClassGroup.SourceType; + var mapperClass = mapperClassGroup.MapperClass; + + mappingExtensionsClass.AddMethod("Map", mapMethod => + { + mapMethod.SetExtensionMethod(); + + var sourceParameter = mapMethod + .AddParameter(sourceType, "source"); + + var newMapper = New( + mapperClass.Type.GetPublicInstanceConstructor(sourceType), + sourceParameter); + + mapMethod.SetBody(newMapper); + }); + } + }); + }); } private static void AddMapMethodsFor( From 2ab8fe575c9e59d68e5530be777a73a80319da19 Mon Sep 17 00:00:00 2001 From: Steve Wilkes <steve@agileobjects.co.uk> Date: Sat, 7 Aug 2021 13:42:26 +0100 Subject: [PATCH 59/65] Removing dictionary key-to-itself assigning / Support for generating source dictionary mappers --- .../Mappers/Extensions/MappingExtensions.cs | 8 ++ .../Mappers/Mapper.cs | 8 ++ .../Mappers/StringStringDictionaryMapper.cs | 120 ++++++++++++++++++ ...ildingSourceDictionaryEnumerableMappers.cs | 42 ++++++ .../DictionaryCreateNewMapperConfiguration.cs | 1 + .../Mappers/Extensions/MappingExtensions.cs | 8 ++ .../Mappers/Mapper.cs | 8 ++ .../Mappers/StringStringDictionaryMapper.cs | 120 ++++++++++++++++++ .../Dictionaries/ElementKeyPartFactory.cs | 17 ++- .../DerivedComplexTypeDataSourcesFactory.cs | 7 +- .../Internal/ExpressionExtensions.cs | 5 + ...rceElementsDictionaryPopulationLoopData.cs | 5 +- .../IndexedSourcePopulationLoopData.cs | 9 +- 13 files changed, 349 insertions(+), 9 deletions(-) create mode 100644 AgileMapper.Buildable.UnitTests.NetStandard2.Mappers/Mappers/StringStringDictionaryMapper.cs create mode 100644 AgileMapper.Buildable.UnitTests/Dictionaries/WhenBuildingSourceDictionaryEnumerableMappers.cs create mode 100644 AgileMapper.Buildable.UnitTests/Mappers/StringStringDictionaryMapper.cs diff --git a/AgileMapper.Buildable.UnitTests.NetStandard2.Mappers/Mappers/Extensions/MappingExtensions.cs b/AgileMapper.Buildable.UnitTests.NetStandard2.Mappers/Mappers/Extensions/MappingExtensions.cs index 25395eef7..7d887b92e 100644 --- a/AgileMapper.Buildable.UnitTests.NetStandard2.Mappers/Mappers/Extensions/MappingExtensions.cs +++ b/AgileMapper.Buildable.UnitTests.NetStandard2.Mappers/Mappers/Extensions/MappingExtensions.cs @@ -163,5 +163,13 @@ this PublicProperty<string> source { return new StringPublicPropertyMapper(source); } + + public static StringStringDictionaryMapper Map + ( + this Dictionary<string, string> source + ) + { + return new StringStringDictionaryMapper(source); + } } } \ No newline at end of file diff --git a/AgileMapper.Buildable.UnitTests.NetStandard2.Mappers/Mappers/Mapper.cs b/AgileMapper.Buildable.UnitTests.NetStandard2.Mappers/Mappers/Mapper.cs index 674b9f324..6d9d251b1 100644 --- a/AgileMapper.Buildable.UnitTests.NetStandard2.Mappers/Mappers/Mapper.cs +++ b/AgileMapper.Buildable.UnitTests.NetStandard2.Mappers/Mappers/Mapper.cs @@ -162,5 +162,13 @@ PublicProperty<string> source { return new StringPublicPropertyMapper(source); } + + public static StringStringDictionaryMapper Map + ( + Dictionary<string, string> source + ) + { + return new StringStringDictionaryMapper(source); + } } } \ No newline at end of file diff --git a/AgileMapper.Buildable.UnitTests.NetStandard2.Mappers/Mappers/StringStringDictionaryMapper.cs b/AgileMapper.Buildable.UnitTests.NetStandard2.Mappers/Mappers/StringStringDictionaryMapper.cs new file mode 100644 index 000000000..48eb050e9 --- /dev/null +++ b/AgileMapper.Buildable.UnitTests.NetStandard2.Mappers/Mappers/StringStringDictionaryMapper.cs @@ -0,0 +1,120 @@ +// ------------------------------------------------------------------------------ +// <auto-generated> +// This code was generated by AgileObjects.AgileMapper.Buildable. +// Runtime Version: 0.1.0.0 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// </auto-generated> +// ------------------------------------------------------------------------------ + +using System; +using System.CodeDom.Compiler; +using System.Collections.Generic; +using System.Linq; +using System.Text.RegularExpressions; +using AgileObjects.AgileMapper; +using AgileObjects.AgileMapper.Extensions; +using AgileObjects.AgileMapper.Extensions.Internal; +using AgileObjects.AgileMapper.ObjectPopulation; +using AgileObjects.AgileMapper.UnitTests.Common.TestClasses; +using AgileObjects.ReadableExpressions; +using AgileObjects.ReadableExpressions.Extensions; + +namespace AgileObjects.AgileMapper.Buildable.UnitTests.Mappers +{ + [GeneratedCode("AgileObjects.AgileMapper.Buildable", "0.1.0.0")] + public class StringStringDictionaryMapper : MappingExecutionContextBase<Dictionary<string, string>> + { + public StringStringDictionaryMapper + ( + Dictionary<string, string> source + ) + : base(source) + { + } + + public TTarget ToANew<TTarget>() + { + if (typeof(TTarget) == typeof(Address[])) + { + return (TTarget)((object)StringStringDictionaryMapper.CreateNew(this.CreateRootMappingData(default(Address[])))); + } + + throw new NotSupportedException( + "Unable to perform a 'CreateNew' mapping from source type 'Dictionary<string, string>' to target type '" + typeof(TTarget).GetFriendlyName(null) + "'"); + } + + private static Address[] CreateNew + ( + IObjectMappingData<Dictionary<string, string>, Address[]> ssdToAaData + ) + { + Dictionary<string, string> sourceStringStringDictionary; + try + { + sourceStringStringDictionary = ssdToAaData.Source; + + var stringStringDictionary_ValueCollection = sourceStringStringDictionary.Values; + var addressList = new List<Address>(stringStringDictionary_ValueCollection.Count); + var i = 0; + while (true) + { + var targetKey = "[" + i + "]"; + + if (sourceStringStringDictionary.Keys.None(key => key.StartsWith(targetKey, StringComparison.OrdinalIgnoreCase))) + { + break; + } + + addressList.Add(StringStringDictionaryMapper.GetAddress(i, sourceStringStringDictionary)); + ++i; + } + + return addressList.ToArray(); + } + catch (Exception ex) + { + throw MappingException.For( + "CreateNew", + "Dictionary<string, string>", + "Address[]", + ex); + } + } + + private static Address GetAddress + ( + int i, + Dictionary<string, string> sourceStringStringDictionary + ) + { + try + { + var address = new Address(); + var line1Key = "[" + i + "].Line1"; + + if ((line1Key = sourceStringStringDictionary.Keys.FirstOrDefault(key => key.MatchesKey(line1Key, ".", new Regex("\\[[0-9]+\\]")))) != null) + { + address.Line1 = sourceStringStringDictionary[line1Key]; + } + var line2Key = "[" + i + "].Line2"; + + if ((line2Key = sourceStringStringDictionary.Keys.FirstOrDefault(key => key.MatchesKey(line2Key, ".", new Regex("\\[[0-9]+\\]")))) != null) + { + address.Line2 = sourceStringStringDictionary[line2Key]; + } + + return address; + } + catch (Exception ex) + { + throw MappingException.For( + "CreateNew", + "Dictionary<string, string>['Target'].[i]", + "Address[][i]", + ex); + } + } + } +} \ No newline at end of file diff --git a/AgileMapper.Buildable.UnitTests/Dictionaries/WhenBuildingSourceDictionaryEnumerableMappers.cs b/AgileMapper.Buildable.UnitTests/Dictionaries/WhenBuildingSourceDictionaryEnumerableMappers.cs new file mode 100644 index 000000000..460eebb13 --- /dev/null +++ b/AgileMapper.Buildable.UnitTests/Dictionaries/WhenBuildingSourceDictionaryEnumerableMappers.cs @@ -0,0 +1,42 @@ +namespace AgileObjects.AgileMapper.Buildable.UnitTests.Dictionaries +{ + using System.Collections.Generic; + using System.Linq; + using AgileMapper.UnitTests.Common; + using AgileMapper.UnitTests.Common.TestClasses; + using Mappers.Extensions; + using Xunit; + + public class WhenBuildingSourceDictionaryEnumerableMappers + { + [Fact] + public void ShouldBuildAStringDictionaryToAddressArrayMapper() + { + var source = new Dictionary<string, string> + { + ["[0].Line1"] = "Line 1.1", + ["[0].Line2"] = "Line 1.2", + ["[1].Line1"] = "Line 2.1", + ["[2].Line1"] = "Line 3.1", + ["[2].Line2"] = "Line 3.2", + ["[3].Line2"] = "Line 4.2", + }; + + var result = source.Map().ToANew<Address[]>(); + + result.ShouldNotBeNull(); + result.Length.ShouldBe(4); + result.First().Line1.ShouldBe("Line 1.1"); + result.First().Line2.ShouldBe("Line 1.2"); + + result.Second().Line1.ShouldBe("Line 2.1"); + result.Second().Line2.ShouldBeNull(); + + result.Third().Line1.ShouldBe("Line 3.1"); + result.Third().Line2.ShouldBe("Line 3.2"); + + result.Fourth().Line1.ShouldBeNull(); + result.Fourth().Line2.ShouldBe("Line 4.2"); + } + } +} \ No newline at end of file diff --git a/AgileMapper.Buildable.UnitTests/MapperConfiguration/Dictionaries/DictionaryCreateNewMapperConfiguration.cs b/AgileMapper.Buildable.UnitTests/MapperConfiguration/Dictionaries/DictionaryCreateNewMapperConfiguration.cs index 8c4ae87dc..9026e24f8 100644 --- a/AgileMapper.Buildable.UnitTests/MapperConfiguration/Dictionaries/DictionaryCreateNewMapperConfiguration.cs +++ b/AgileMapper.Buildable.UnitTests/MapperConfiguration/Dictionaries/DictionaryCreateNewMapperConfiguration.cs @@ -9,6 +9,7 @@ public class DictionaryCreateNewMapperConfiguration : BuildableMapperConfigurati protected override void Configure() { GetPlanFor<PublicTwoFields<int, Address>>().ToANew<Dictionary<string, string>>(); + GetPlanFor<Dictionary<string, string>>().ToANew<Address[]>(); } } } \ No newline at end of file diff --git a/AgileMapper.Buildable.UnitTests/Mappers/Extensions/MappingExtensions.cs b/AgileMapper.Buildable.UnitTests/Mappers/Extensions/MappingExtensions.cs index 25395eef7..7d887b92e 100644 --- a/AgileMapper.Buildable.UnitTests/Mappers/Extensions/MappingExtensions.cs +++ b/AgileMapper.Buildable.UnitTests/Mappers/Extensions/MappingExtensions.cs @@ -163,5 +163,13 @@ this PublicProperty<string> source { return new StringPublicPropertyMapper(source); } + + public static StringStringDictionaryMapper Map + ( + this Dictionary<string, string> source + ) + { + return new StringStringDictionaryMapper(source); + } } } \ No newline at end of file diff --git a/AgileMapper.Buildable.UnitTests/Mappers/Mapper.cs b/AgileMapper.Buildable.UnitTests/Mappers/Mapper.cs index 674b9f324..6d9d251b1 100644 --- a/AgileMapper.Buildable.UnitTests/Mappers/Mapper.cs +++ b/AgileMapper.Buildable.UnitTests/Mappers/Mapper.cs @@ -162,5 +162,13 @@ PublicProperty<string> source { return new StringPublicPropertyMapper(source); } + + public static StringStringDictionaryMapper Map + ( + Dictionary<string, string> source + ) + { + return new StringStringDictionaryMapper(source); + } } } \ No newline at end of file diff --git a/AgileMapper.Buildable.UnitTests/Mappers/StringStringDictionaryMapper.cs b/AgileMapper.Buildable.UnitTests/Mappers/StringStringDictionaryMapper.cs new file mode 100644 index 000000000..48eb050e9 --- /dev/null +++ b/AgileMapper.Buildable.UnitTests/Mappers/StringStringDictionaryMapper.cs @@ -0,0 +1,120 @@ +// ------------------------------------------------------------------------------ +// <auto-generated> +// This code was generated by AgileObjects.AgileMapper.Buildable. +// Runtime Version: 0.1.0.0 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// </auto-generated> +// ------------------------------------------------------------------------------ + +using System; +using System.CodeDom.Compiler; +using System.Collections.Generic; +using System.Linq; +using System.Text.RegularExpressions; +using AgileObjects.AgileMapper; +using AgileObjects.AgileMapper.Extensions; +using AgileObjects.AgileMapper.Extensions.Internal; +using AgileObjects.AgileMapper.ObjectPopulation; +using AgileObjects.AgileMapper.UnitTests.Common.TestClasses; +using AgileObjects.ReadableExpressions; +using AgileObjects.ReadableExpressions.Extensions; + +namespace AgileObjects.AgileMapper.Buildable.UnitTests.Mappers +{ + [GeneratedCode("AgileObjects.AgileMapper.Buildable", "0.1.0.0")] + public class StringStringDictionaryMapper : MappingExecutionContextBase<Dictionary<string, string>> + { + public StringStringDictionaryMapper + ( + Dictionary<string, string> source + ) + : base(source) + { + } + + public TTarget ToANew<TTarget>() + { + if (typeof(TTarget) == typeof(Address[])) + { + return (TTarget)((object)StringStringDictionaryMapper.CreateNew(this.CreateRootMappingData(default(Address[])))); + } + + throw new NotSupportedException( + "Unable to perform a 'CreateNew' mapping from source type 'Dictionary<string, string>' to target type '" + typeof(TTarget).GetFriendlyName(null) + "'"); + } + + private static Address[] CreateNew + ( + IObjectMappingData<Dictionary<string, string>, Address[]> ssdToAaData + ) + { + Dictionary<string, string> sourceStringStringDictionary; + try + { + sourceStringStringDictionary = ssdToAaData.Source; + + var stringStringDictionary_ValueCollection = sourceStringStringDictionary.Values; + var addressList = new List<Address>(stringStringDictionary_ValueCollection.Count); + var i = 0; + while (true) + { + var targetKey = "[" + i + "]"; + + if (sourceStringStringDictionary.Keys.None(key => key.StartsWith(targetKey, StringComparison.OrdinalIgnoreCase))) + { + break; + } + + addressList.Add(StringStringDictionaryMapper.GetAddress(i, sourceStringStringDictionary)); + ++i; + } + + return addressList.ToArray(); + } + catch (Exception ex) + { + throw MappingException.For( + "CreateNew", + "Dictionary<string, string>", + "Address[]", + ex); + } + } + + private static Address GetAddress + ( + int i, + Dictionary<string, string> sourceStringStringDictionary + ) + { + try + { + var address = new Address(); + var line1Key = "[" + i + "].Line1"; + + if ((line1Key = sourceStringStringDictionary.Keys.FirstOrDefault(key => key.MatchesKey(line1Key, ".", new Regex("\\[[0-9]+\\]")))) != null) + { + address.Line1 = sourceStringStringDictionary[line1Key]; + } + var line2Key = "[" + i + "].Line2"; + + if ((line2Key = sourceStringStringDictionary.Keys.FirstOrDefault(key => key.MatchesKey(line2Key, ".", new Regex("\\[[0-9]+\\]")))) != null) + { + address.Line2 = sourceStringStringDictionary[line2Key]; + } + + return address; + } + catch (Exception ex) + { + throw MappingException.For( + "CreateNew", + "Dictionary<string, string>['Target'].[i]", + "Address[][i]", + ex); + } + } + } +} \ No newline at end of file diff --git a/AgileMapper/Configuration/Dictionaries/ElementKeyPartFactory.cs b/AgileMapper/Configuration/Dictionaries/ElementKeyPartFactory.cs index eb2c9c211..f921139ed 100644 --- a/AgileMapper/Configuration/Dictionaries/ElementKeyPartFactory.cs +++ b/AgileMapper/Configuration/Dictionaries/ElementKeyPartFactory.cs @@ -132,13 +132,28 @@ public Expression GetElementKeyPartMatcher() private Regex CreateKeyPartRegex() { return new Regex( - _prefixString + "[0-9]+" + _suffixString + GetTokenForRegex(_prefixString) + "[0-9]+" + GetTokenForRegex(_suffixString) #if !NETSTANDARD1_0 , RegexOptions.Compiled #endif ); } + private static string GetTokenForRegex(string value) + { + switch (value) + { + case "[": + case "]": + case "(": + case ")": + return '\\' + value; + + default: + return value; + } + } + public Expression GetElementKeyPrefixOrNull() => _prefix; public override bool ConflictsWith(UserConfiguredItemBase otherConfiguredItem) diff --git a/AgileMapper/DataSources/Factories/DerivedComplexTypeDataSourcesFactory.cs b/AgileMapper/DataSources/Factories/DerivedComplexTypeDataSourcesFactory.cs index 140fc871b..27c7d3721 100644 --- a/AgileMapper/DataSources/Factories/DerivedComplexTypeDataSourcesFactory.cs +++ b/AgileMapper/DataSources/Factories/DerivedComplexTypeDataSourcesFactory.cs @@ -5,10 +5,8 @@ using System.Linq; #if NET35 using Microsoft.Scripting.Ast; - using static Microsoft.Scripting.Ast.Expression; #else using System.Linq.Expressions; - using static System.Linq.Expressions.Expression; #endif using Configuration; using Extensions; @@ -17,6 +15,11 @@ using NetStandardPolyfills; using ObjectPopulation; using ReadableExpressions; +#if NET35 + using static Microsoft.Scripting.Ast.Expression; +#else + using static System.Linq.Expressions.Expression; +#endif using static Constants; using static TypeComparer; diff --git a/AgileMapper/Extensions/Internal/ExpressionExtensions.cs b/AgileMapper/Extensions/Internal/ExpressionExtensions.cs index 016f6eb0e..5d37f438d 100644 --- a/AgileMapper/Extensions/Internal/ExpressionExtensions.cs +++ b/AgileMapper/Extensions/Internal/ExpressionExtensions.cs @@ -96,6 +96,11 @@ public static LoopExpression InsertAssignment( ParameterExpression variable, Expression value) { + if (variable == value) + { + return loop; + } + var loopBody = (BlockExpression)loop.Body; var loopBodyExpressions = new Expression[loopBody.Expressions.Count + 1]; var expressionOffset = 0; diff --git a/AgileMapper/ObjectPopulation/Enumerables/Dictionaries/Looping/SourceElementsDictionaryPopulationLoopData.cs b/AgileMapper/ObjectPopulation/Enumerables/Dictionaries/Looping/SourceElementsDictionaryPopulationLoopData.cs index deb7f5483..50a2f872a 100644 --- a/AgileMapper/ObjectPopulation/Enumerables/Dictionaries/Looping/SourceElementsDictionaryPopulationLoopData.cs +++ b/AgileMapper/ObjectPopulation/Enumerables/Dictionaries/Looping/SourceElementsDictionaryPopulationLoopData.cs @@ -12,6 +12,7 @@ namespace AgileObjects.AgileMapper.ObjectPopulation.Enumerables.Dictionaries.Loo using Extensions; using Extensions.Internal; using NetStandardPolyfills; + using static Constants; internal class SourceElementsDictionaryPopulationLoopData : IPopulationLoopData { @@ -186,8 +187,8 @@ private Expression GetElementMappingBlock(Expression elementMapping) public Expression Adapt(LoopExpression loop) { loop = loop - .InsertAssignment(Constants.BeforeLoopExitCheck, _dictionaryVariables.Key, _targetElementKey) - .InsertAssignment(Constants.BeforeLoopExitCheck, _targetElementKey, _targetMemberKey); + .InsertAssignment(BeforeLoopExitCheck, _dictionaryVariables.Key, _targetElementKey) + .InsertAssignment(BeforeLoopExitCheck, _targetElementKey, _targetMemberKey); var loopBody = (BlockExpression)loop.Body; diff --git a/AgileMapper/ObjectPopulation/Enumerables/Looping/IndexedSourcePopulationLoopData.cs b/AgileMapper/ObjectPopulation/Enumerables/Looping/IndexedSourcePopulationLoopData.cs index ac6ddd1a3..754f6c227 100644 --- a/AgileMapper/ObjectPopulation/Enumerables/Looping/IndexedSourcePopulationLoopData.cs +++ b/AgileMapper/ObjectPopulation/Enumerables/Looping/IndexedSourcePopulationLoopData.cs @@ -6,6 +6,7 @@ namespace AgileObjects.AgileMapper.ObjectPopulation.Enumerables.Looping using System.Linq.Expressions; #endif using Extensions.Internal; + using static Constants; internal class IndexedSourcePopulationLoopData : IPopulationLoopData { @@ -50,10 +51,10 @@ public Expression Adapt(LoopExpression loop) return loop; } - return loop.InsertAssignment( - Constants.AfterLoopExitCheck, - (ParameterExpression)_sourceElement, - _indexedSourceAccess); + var sourceVariable = (ParameterExpression)_sourceElement; + + return loop + .InsertAssignment(AfterLoopExitCheck, sourceVariable, _indexedSourceAccess); } } } \ No newline at end of file From 66b311cb6a68949ab1e5af0f8908158e3c2afad7 Mon Sep 17 00:00:00 2001 From: Steve Wilkes <steve@agileobjects.co.uk> Date: Sat, 7 Aug 2021 14:26:23 +0100 Subject: [PATCH 60/65] Passing a regex pattern instead of a regex to dictionary MatchesKey() helper method --- .../Mappers/StringStringDictionaryMapper.cs | 5 +- .../Mappers/StringStringDictionaryMapper.cs | 5 +- .../BuildableMapperExtensions.cs | 240 ++++++++++-------- AgileMapper.Buildable/BuildableMapperGroup.cs | 2 +- .../Dictionaries/ElementKeyPartFactory.cs | 13 +- .../Internal/PublicStringExtensions.cs | 20 +- .../Internal/StringExpressionExtensions.cs | 3 +- 7 files changed, 150 insertions(+), 138 deletions(-) diff --git a/AgileMapper.Buildable.UnitTests.NetStandard2.Mappers/Mappers/StringStringDictionaryMapper.cs b/AgileMapper.Buildable.UnitTests.NetStandard2.Mappers/Mappers/StringStringDictionaryMapper.cs index 48eb050e9..a7ce1675a 100644 --- a/AgileMapper.Buildable.UnitTests.NetStandard2.Mappers/Mappers/StringStringDictionaryMapper.cs +++ b/AgileMapper.Buildable.UnitTests.NetStandard2.Mappers/Mappers/StringStringDictionaryMapper.cs @@ -12,7 +12,6 @@ using System.CodeDom.Compiler; using System.Collections.Generic; using System.Linq; -using System.Text.RegularExpressions; using AgileObjects.AgileMapper; using AgileObjects.AgileMapper.Extensions; using AgileObjects.AgileMapper.Extensions.Internal; @@ -94,13 +93,13 @@ Dictionary<string, string> sourceStringStringDictionary var address = new Address(); var line1Key = "[" + i + "].Line1"; - if ((line1Key = sourceStringStringDictionary.Keys.FirstOrDefault(key => key.MatchesKey(line1Key, ".", new Regex("\\[[0-9]+\\]")))) != null) + if ((line1Key = sourceStringStringDictionary.Keys.FirstOrDefault(key => key.MatchesKey(line1Key, ".", "\\[[0-9]+\\]"))) != null) { address.Line1 = sourceStringStringDictionary[line1Key]; } var line2Key = "[" + i + "].Line2"; - if ((line2Key = sourceStringStringDictionary.Keys.FirstOrDefault(key => key.MatchesKey(line2Key, ".", new Regex("\\[[0-9]+\\]")))) != null) + if ((line2Key = sourceStringStringDictionary.Keys.FirstOrDefault(key => key.MatchesKey(line2Key, ".", "\\[[0-9]+\\]"))) != null) { address.Line2 = sourceStringStringDictionary[line2Key]; } diff --git a/AgileMapper.Buildable.UnitTests/Mappers/StringStringDictionaryMapper.cs b/AgileMapper.Buildable.UnitTests/Mappers/StringStringDictionaryMapper.cs index 48eb050e9..a7ce1675a 100644 --- a/AgileMapper.Buildable.UnitTests/Mappers/StringStringDictionaryMapper.cs +++ b/AgileMapper.Buildable.UnitTests/Mappers/StringStringDictionaryMapper.cs @@ -12,7 +12,6 @@ using System.CodeDom.Compiler; using System.Collections.Generic; using System.Linq; -using System.Text.RegularExpressions; using AgileObjects.AgileMapper; using AgileObjects.AgileMapper.Extensions; using AgileObjects.AgileMapper.Extensions.Internal; @@ -94,13 +93,13 @@ Dictionary<string, string> sourceStringStringDictionary var address = new Address(); var line1Key = "[" + i + "].Line1"; - if ((line1Key = sourceStringStringDictionary.Keys.FirstOrDefault(key => key.MatchesKey(line1Key, ".", new Regex("\\[[0-9]+\\]")))) != null) + if ((line1Key = sourceStringStringDictionary.Keys.FirstOrDefault(key => key.MatchesKey(line1Key, ".", "\\[[0-9]+\\]"))) != null) { address.Line1 = sourceStringStringDictionary[line1Key]; } var line2Key = "[" + i + "].Line2"; - if ((line2Key = sourceStringStringDictionary.Keys.FirstOrDefault(key => key.MatchesKey(line2Key, ".", new Regex("\\[[0-9]+\\]")))) != null) + if ((line2Key = sourceStringStringDictionary.Keys.FirstOrDefault(key => key.MatchesKey(line2Key, ".", "\\[[0-9]+\\]"))) != null) { address.Line2 = sourceStringStringDictionary[line2Key]; } diff --git a/AgileMapper.Buildable/BuildableMapperExtensions.cs b/AgileMapper.Buildable/BuildableMapperExtensions.cs index 57705a68f..2f330a082 100644 --- a/AgileMapper.Buildable/BuildableMapperExtensions.cs +++ b/AgileMapper.Buildable/BuildableMapperExtensions.cs @@ -30,147 +30,99 @@ public static IEnumerable<SourceCodeExpression> GetPlanSourceCodeInCache( string rootNamespace) { var mappersNamespace = rootNamespace + ".Mappers"; + var mapperClassGroups = mapper.GetMapperClassGroups(); - var mapperClassGroups = mapper + if (mapperClassGroups.Count == 0) + { + yield break; + } + + foreach (var mapperGroup in mapperClassGroups) + { + yield return mapperGroup.BuildInstanceMapperClass(mappersNamespace); + } + + yield return mapperClassGroups.BuildStaticMapperClass(mappersNamespace); + yield return mapperClassGroups.BuildMappingExtensionsClass(mappersNamespace); + } + + private static List<BuildableMapperGroup> GetMapperClassGroups(this IMapper mapper) + { + return mapper .GetPlansInCache() .GroupBy(plan => plan.Root.SourceType) .Project(grp => new BuildableMapperGroup(grp.Key, grp.AsEnumerable())) .OrderBy(grp => grp.MapperName) .ToList(); + } - if (mapperClassGroups.Count == 0) + private static SourceCodeExpression BuildInstanceMapperClass( + this BuildableMapperGroup mapperGroup, + string mappersNamespace) + { + return BuildableExpression.SourceCode(sourceCode => { - yield break; - } + sourceCode.AddGeneratedCodeHeader(); + sourceCode.SetNamespace(mappersNamespace); - foreach (var mapperGroup in mapperClassGroups) - { - yield return BuildableExpression.SourceCode(sourceCode => + mapperGroup.MapperClass = sourceCode.AddClass(mapperGroup.MapperName, mapperClass => { - sourceCode.AddGeneratedCodeHeader(); - sourceCode.SetNamespace(mappersNamespace); + mapperGroup.MapperInstance = mapperClass.ThisInstanceExpression; - mapperGroup.MapperClass = sourceCode.AddClass(mapperGroup.MapperName, mapperClass => - { - mapperGroup.MapperInstance = mapperClass.ThisInstanceExpression; + mapperClass.AddGeneratedCodeAttribute(); + mapperClass.SetBaseType(mapperGroup.MapperBaseType); - mapperClass.AddGeneratedCodeAttribute(); - mapperClass.SetBaseType(mapperGroup.MapperBaseType); + mapperClass.AddConstructor(ctor => + { + ctor.SetConstructorCall( + mapperGroup.MapperBaseTypeConstructor, + ctor.AddParameter(mapperGroup.SourceType, "source")); - mapperClass.AddConstructor(ctor => - { - ctor.SetConstructorCall( - mapperGroup.MapperBaseTypeConstructor, - ctor.AddParameter(mapperGroup.SourceType, "source")); + ctor.SetBody(Empty()); + }); - ctor.SetBody(Empty()); - }); + foreach (var planAndMappingMethods in mapperGroup.MappingMethodsByPlan) + { + var plan = planAndMappingMethods.Key; + var mappingMethods = planAndMappingMethods.Value; - foreach (var planAndMappingMethods in mapperGroup.MappingMethodsByPlan) + foreach (var repeatPlan in plan.Skip(1)) { - var plan = planAndMappingMethods.Key; - var mappingMethods = planAndMappingMethods.Value; - - foreach (var repeatPlan in plan.Skip(1)) - { - mappingMethods.Add(mapperClass.AddMethod(MapRepeated, doMapping => - { - doMapping.SetVisibility(Private); - doMapping.SetStatic(); - doMapping.SetBody(repeatPlan.Mapping); - })); - } - - mappingMethods.Insert(0, mapperClass.AddMethod(plan.RuleSetName, doMapping => + mappingMethods.Add(mapperClass.AddMethod(MapRepeated, doMapping => { doMapping.SetVisibility(Private); doMapping.SetStatic(); - doMapping.SetBody(plan.Root.Mapping); + doMapping.SetBody(repeatPlan.Mapping); })); } - RepeatMappingCallReplacer.Replace(mapperGroup); - - var allRuleSetMapMethodInfos = mapperGroup - .MappingMethodsByPlan.Values - .SelectMany(methods => methods) - .Filter(method => method.Name != MapRepeated) - .Project(method => new MapMethodInfo(mapperGroup, method)) - .GroupBy(m => m.RuleSetName) - .Select(methodGroup => methodGroup.ToList()); - - foreach (var ruleSetMapMethodInfos in allRuleSetMapMethodInfos) + mappingMethods.Insert(0, mapperClass.AddMethod(plan.RuleSetName, doMapping => { - AddMapMethodsFor(mapperClass, ruleSetMapMethodInfos); - } - }); - }); - } - - yield return BuildableExpression.SourceCode(sourceCode => - { - sourceCode.AddGeneratedCodeHeader(); - sourceCode.SetNamespace(mappersNamespace); - - sourceCode.AddClass("Mapper", staticMapperClass => - { - staticMapperClass.AddGeneratedCodeAttribute(); - staticMapperClass.SetStatic(); - - foreach (var mapperClassGroup in mapperClassGroups) - { - var sourceType = mapperClassGroup.SourceType; - var mapperClass = mapperClassGroup.MapperClass; - - staticMapperClass.AddMethod("Map", mapMethod => - { - var sourceParameter = mapMethod - .AddParameter(sourceType, "source"); - - var newMapper = New( - mapperClass.Type.GetPublicInstanceConstructor(sourceType), - sourceParameter); - - mapMethod.SetBody(newMapper); - }); + doMapping.SetVisibility(Private); + doMapping.SetStatic(); + doMapping.SetBody(plan.Root.Mapping); + })); } - }); - }); - yield return BuildableExpression.SourceCode(sourceCode => - { - sourceCode.AddGeneratedCodeHeader(); - sourceCode.SetNamespace(mappersNamespace+ ".Extensions"); + RepeatMappingCallReplacer.Replace(mapperGroup); - sourceCode.AddClass("MappingExtensions", mappingExtensionsClass => - { - mappingExtensionsClass.AddGeneratedCodeAttribute(); - mappingExtensionsClass.SetStatic(); + var allRuleSetMapMethodInfos = mapperGroup + .MappingMethodsByPlan.Values + .SelectMany(methods => methods) + .Filter(method => method.Name != MapRepeated) + .Project(method => new MapMethodInfo(mapperGroup, method)) + .GroupBy(m => m.RuleSetName) + .Select(methodGroup => methodGroup.ToList()); - foreach (var mapperClassGroup in mapperClassGroups) + foreach (var ruleSetMapMethodInfos in allRuleSetMapMethodInfos) { - var sourceType = mapperClassGroup.SourceType; - var mapperClass = mapperClassGroup.MapperClass; - - mappingExtensionsClass.AddMethod("Map", mapMethod => - { - mapMethod.SetExtensionMethod(); - - var sourceParameter = mapMethod - .AddParameter(sourceType, "source"); - - var newMapper = New( - mapperClass.Type.GetPublicInstanceConstructor(sourceType), - sourceParameter); - - mapMethod.SetBody(newMapper); - }); + AddMappingMethodsFor(mapperClass, ruleSetMapMethodInfos); } }); }); } - private static void AddMapMethodsFor( + private static void AddMappingMethodsFor( IClassMemberConfigurator mapperClass, IList<MapMethodInfo> mapMethodInfos) { @@ -179,7 +131,7 @@ private static void AddMapMethodsFor( switch (ruleSetName) { case "CreateNew": - AddCreateNewMapMethod(mapperClass, mapMethodInfos); + AddCreateNewMethod(mapperClass, mapMethodInfos); return; case "Merge": @@ -195,7 +147,7 @@ private static void AddMapMethodsFor( } } - private static void AddCreateNewMapMethod( + private static void AddCreateNewMethod( IClassMemberConfigurator mapperClass, IList<MapMethodInfo> mapMethodInfos) { @@ -302,6 +254,74 @@ private static void AddUpdateInstanceMapMethod( } } + private static SourceCodeExpression BuildStaticMapperClass( + this IEnumerable<BuildableMapperGroup> mapperClassGroups, + string mappersNamespace) + { + return BuildableExpression.SourceCode(sourceCode => + { + sourceCode.AddGeneratedCodeHeader(); + sourceCode.SetNamespace(mappersNamespace); + + sourceCode.AddClass("Mapper", staticMapperClass => + { + staticMapperClass.AddGeneratedCodeAttribute(); + staticMapperClass.SetStatic(); + + foreach (var mapperClassGroup in mapperClassGroups) + { + staticMapperClass.AddMethod("Map", mapMethod => + { + mapperClassGroup.Configure(mapMethod); + }); + } + }); + }); + } + + private static SourceCodeExpression BuildMappingExtensionsClass( + this IEnumerable<BuildableMapperGroup> mapperClassGroups, + string mappersNamespace) + { + return BuildableExpression.SourceCode(sourceCode => + { + sourceCode.AddGeneratedCodeHeader(); + sourceCode.SetNamespace(mappersNamespace + ".Extensions"); + + sourceCode.AddClass("MappingExtensions", mappingExtensionsClass => + { + mappingExtensionsClass.AddGeneratedCodeAttribute(); + mappingExtensionsClass.SetStatic(); + + foreach (var mapperClassGroup in mapperClassGroups) + { + mappingExtensionsClass.AddMethod("Map", mapMethod => + { + mapMethod.SetExtensionMethod(); + mapperClassGroup.Configure(mapMethod); + }); + } + }); + }); + } + + private static void Configure( + this BuildableMapperGroup mapperClassGroup, + IConcreteTypeMethodExpressionConfigurator mapMethod) + { + var sourceType = mapperClassGroup.SourceType; + var mapperClass = mapperClassGroup.MapperClass; + + var sourceParameter = mapMethod + .AddParameter(sourceType, "source"); + + var newMapper = New( + mapperClass.Type.GetPublicInstanceConstructor(sourceType), + sourceParameter); + + mapMethod.SetBody(newMapper); + } + private static void AddGeneratedCodeHeader( this ISourceCodeExpressionConfigurator sourceCode) { diff --git a/AgileMapper.Buildable/BuildableMapperGroup.cs b/AgileMapper.Buildable/BuildableMapperGroup.cs index 9a7925e23..565f6cc85 100644 --- a/AgileMapper.Buildable/BuildableMapperGroup.cs +++ b/AgileMapper.Buildable/BuildableMapperGroup.cs @@ -24,7 +24,7 @@ public BuildableMapperGroup( MapperBaseTypeConstructor = MapperBaseType.GetNonPublicInstanceConstructor(sourceType); CreateRootMappingDataMethod = MapperBaseType.GetNonPublicInstanceMethod("CreateRootMappingData"); MapperName = sourceType.GetVariableNameInPascalCase() + "Mapper"; - MappingMethodsByPlan = plans.ToDictionary(p => p, p => new List<MethodExpression>()); + MappingMethodsByPlan = plans.ToDictionary(p => p, _ => new List<MethodExpression>()); } public Type SourceType { get; } diff --git a/AgileMapper/Configuration/Dictionaries/ElementKeyPartFactory.cs b/AgileMapper/Configuration/Dictionaries/ElementKeyPartFactory.cs index f921139ed..d4a2ac7c4 100644 --- a/AgileMapper/Configuration/Dictionaries/ElementKeyPartFactory.cs +++ b/AgileMapper/Configuration/Dictionaries/ElementKeyPartFactory.cs @@ -127,17 +127,10 @@ private static MappingConfigurationException InvalidPattern() private string Pattern => _prefixString + "i" + _suffixString; public Expression GetElementKeyPartMatcher() - => _keyPartMatcher ??= CreateKeyPartRegex().ToConstantExpression(); + => _keyPartMatcher ??= CreateKeyPartRegexPattern().ToConstantExpression(); - private Regex CreateKeyPartRegex() - { - return new Regex( - GetTokenForRegex(_prefixString) + "[0-9]+" + GetTokenForRegex(_suffixString) -#if !NETSTANDARD1_0 - , RegexOptions.Compiled -#endif - ); - } + private string CreateKeyPartRegexPattern() + => GetTokenForRegex(_prefixString) + "[0-9]+" + GetTokenForRegex(_suffixString); private static string GetTokenForRegex(string value) { diff --git a/AgileMapper/Extensions/Internal/PublicStringExtensions.cs b/AgileMapper/Extensions/Internal/PublicStringExtensions.cs index 5c6407e37..a362ed2cb 100644 --- a/AgileMapper/Extensions/Internal/PublicStringExtensions.cs +++ b/AgileMapper/Extensions/Internal/PublicStringExtensions.cs @@ -13,8 +13,8 @@ public static class PublicStringExtensions /// </summary> /// <param name="value">The value from which to get the first character.</param> /// <returns> - /// The first character of the value if it has a length of greater than one, otherwise returns - /// <paramref name="value"/>. + /// The first character of the value if it has a length of greater than one, otherwise + /// returns <paramref name="value"/>. /// </returns> public static string FirstOrDefault(this string value) { @@ -27,23 +27,25 @@ public static string FirstOrDefault(this string value) } /// <summary> - /// Determines if the <paramref name="subjectKey"/> matches the given <paramref name="queryKey"/>, - /// given the given <paramref name="separator"/> and <paramref name="elementKeyPartMatcher"/>. + /// Determines if the <paramref name="subjectKey"/> matches the given + /// <paramref name="queryKey"/>, given the given <paramref name="separator"/> and Regex + /// <paramref name="elementKeyPartMatcherPattern"/>. /// </summary> /// <param name="subjectKey">The subject key for which to make the determination.</param> /// <param name="queryKey">The query key for which to make the determination.</param> /// <param name="separator">The separator to use to separate key parts while making the determination.</param> - /// <param name="elementKeyPartMatcher"> - /// A Regex with which to match element key parts while making the determination. + /// <param name="elementKeyPartMatcherPattern"> + /// A Regex pattern with which to match element key parts while making the determination. /// </param> /// <returns> - /// True if the <paramref name="subjectKey"/> matches the given <paramref name="queryKey"/>, otherwise false. + /// True if the <paramref name="subjectKey"/> matches the given <paramref name="queryKey"/>, + /// otherwise false. /// </returns> public static bool MatchesKey( this string subjectKey, string queryKey, string separator, - Regex elementKeyPartMatcher) + string elementKeyPartMatcherPattern) { if (queryKey == null) { @@ -64,7 +66,7 @@ public static bool MatchesKey( return false; } - var elementKeyParts = elementKeyPartMatcher.Matches(queryKey); + var elementKeyParts = Regex.Matches(queryKey, elementKeyPartMatcherPattern); var searchEndIndex = queryKey.Length; diff --git a/AgileMapper/Extensions/Internal/StringExpressionExtensions.cs b/AgileMapper/Extensions/Internal/StringExpressionExtensions.cs index ed54c48fc..efea523af 100644 --- a/AgileMapper/Extensions/Internal/StringExpressionExtensions.cs +++ b/AgileMapper/Extensions/Internal/StringExpressionExtensions.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using System.Linq; using System.Reflection; - using System.Text.RegularExpressions; using NetStandardPolyfills; #if NET35 using Microsoft.Scripting.Ast; @@ -152,7 +151,7 @@ public static Expression GetMatchesKeyCall( return GetMatchesKeyWithSeparatorCall(stringAccess, keyValue, separator); } - var matcherPattern = ((Regex)((ConstantExpression)elementKeyPartMatcher).Value).ToString(); + var matcherPattern = (string)((ConstantExpression)elementKeyPartMatcher).Value; if (matcherPattern == "[0-9]+") { From 73e75c192547bd098dd1b0e8b27070fe91bc60b8 Mon Sep 17 00:00:00 2001 From: Steve Wilkes <steve@agileobjects.co.uk> Date: Sat, 7 Aug 2021 15:11:08 +0100 Subject: [PATCH 61/65] Adding DeepClone methods to generated mappers where appropriate --- .../AddressAddressPublicTwoFieldsMapper.cs | 118 ++++++++++++++++++ .../Mappers/Extensions/MappingExtensions.cs | 24 ++++ .../Mappers/Mapper.cs | 24 ++++ ...ComplexTypeDeepCloneMapperConfiguration.cs | 13 ++ .../AddressAddressPublicTwoFieldsMapper.cs | 118 ++++++++++++++++++ .../Mappers/Extensions/MappingExtensions.cs | 24 ++++ .../Mappers/Mapper.cs | 24 ++++ ...WhenBuildingComplexTypeCreateNewMappers.cs | 18 +++ .../BuildableMapperExtensions.cs | 70 ++++++++--- AgileMapper.Buildable/BuildableMapperGroup.cs | 11 ++ .../FluentAssertionExtensions.cs | 6 +- 11 files changed, 429 insertions(+), 21 deletions(-) create mode 100644 AgileMapper.Buildable.UnitTests.NetStandard2.Mappers/Mappers/AddressAddressPublicTwoFieldsMapper.cs create mode 100644 AgileMapper.Buildable.UnitTests/MapperConfiguration/ComplexTypeDeepCloneMapperConfiguration.cs create mode 100644 AgileMapper.Buildable.UnitTests/Mappers/AddressAddressPublicTwoFieldsMapper.cs diff --git a/AgileMapper.Buildable.UnitTests.NetStandard2.Mappers/Mappers/AddressAddressPublicTwoFieldsMapper.cs b/AgileMapper.Buildable.UnitTests.NetStandard2.Mappers/Mappers/AddressAddressPublicTwoFieldsMapper.cs new file mode 100644 index 000000000..a980cbae6 --- /dev/null +++ b/AgileMapper.Buildable.UnitTests.NetStandard2.Mappers/Mappers/AddressAddressPublicTwoFieldsMapper.cs @@ -0,0 +1,118 @@ +// ------------------------------------------------------------------------------ +// <auto-generated> +// This code was generated by AgileObjects.AgileMapper.Buildable. +// Runtime Version: 0.1.0.0 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// </auto-generated> +// ------------------------------------------------------------------------------ + +using System; +using System.CodeDom.Compiler; +using AgileObjects.AgileMapper; +using AgileObjects.AgileMapper.ObjectPopulation; +using AgileObjects.AgileMapper.UnitTests.Common.TestClasses; + +namespace AgileObjects.AgileMapper.Buildable.UnitTests.Mappers +{ + [GeneratedCode("AgileObjects.AgileMapper.Buildable", "0.1.0.0")] + public class AddressAddressPublicTwoFieldsMapper : MappingExecutionContextBase<PublicTwoFields<Address, Address>> + { + public AddressAddressPublicTwoFieldsMapper + ( + PublicTwoFields<Address, Address> source + ) + : base(source) + { + } + + public PublicTwoFields<Address, Address> ToANew<TTarget>() + where TTarget : PublicTwoFields<Address, Address> + { + return AddressAddressPublicTwoFieldsMapper.CreateNew(this.CreateRootMappingData(default(PublicTwoFields<Address, Address>))); + } + + private static PublicTwoFields<Address, Address> CreateNew + ( + IObjectMappingData<PublicTwoFields<Address, Address>, PublicTwoFields<Address, Address>> aaptfToAaptfData + ) + { + PublicTwoFields<Address, Address> sourceAddressAddressPublicTwoFields; + try + { + sourceAddressAddressPublicTwoFields = aaptfToAaptfData.Source; + + var addressAddressPublicTwoFields = new PublicTwoFields<Address, Address>(); + + if (sourceAddressAddressPublicTwoFields.Value1 != null) + { + addressAddressPublicTwoFields.Value1 = AddressAddressPublicTwoFieldsMapper.GetAddress1(addressAddressPublicTwoFields, sourceAddressAddressPublicTwoFields); + } + + if (sourceAddressAddressPublicTwoFields.Value2 != null) + { + addressAddressPublicTwoFields.Value2 = AddressAddressPublicTwoFieldsMapper.GetAddress2(addressAddressPublicTwoFields, sourceAddressAddressPublicTwoFields); + } + + return addressAddressPublicTwoFields; + } + catch (Exception ex) + { + throw MappingException.For( + "CreateNew", + "PublicTwoFields<Address, Address>", + "PublicTwoFields<Address, Address>", + ex); + } + } + + private static Address GetAddress1 + ( + PublicTwoFields<Address, Address> addressAddressPublicTwoFields, + PublicTwoFields<Address, Address> sourceAddressAddressPublicTwoFields + ) + { + try + { + var address = addressAddressPublicTwoFields.Value1 ?? new Address(); + address.Line1 = sourceAddressAddressPublicTwoFields.Value1.Line1; + address.Line2 = sourceAddressAddressPublicTwoFields.Value1.Line2; + + return address; + } + catch (Exception ex) + { + throw MappingException.For( + "CreateNew", + "PublicTwoFields<Address, Address>.Value1", + "PublicTwoFields<Address, Address>.Value1", + ex); + } + } + + private static Address GetAddress2 + ( + PublicTwoFields<Address, Address> addressAddressPublicTwoFields, + PublicTwoFields<Address, Address> sourceAddressAddressPublicTwoFields + ) + { + try + { + var address = addressAddressPublicTwoFields.Value2 ?? new Address(); + address.Line1 = sourceAddressAddressPublicTwoFields.Value2.Line1; + address.Line2 = sourceAddressAddressPublicTwoFields.Value2.Line2; + + return address; + } + catch (Exception ex) + { + throw MappingException.For( + "CreateNew", + "PublicTwoFields<Address, Address>.Value2", + "PublicTwoFields<Address, Address>.Value2", + ex); + } + } + } +} \ No newline at end of file diff --git a/AgileMapper.Buildable.UnitTests.NetStandard2.Mappers/Mappers/Extensions/MappingExtensions.cs b/AgileMapper.Buildable.UnitTests.NetStandard2.Mappers/Mappers/Extensions/MappingExtensions.cs index 7d887b92e..987d137ce 100644 --- a/AgileMapper.Buildable.UnitTests.NetStandard2.Mappers/Mappers/Extensions/MappingExtensions.cs +++ b/AgileMapper.Buildable.UnitTests.NetStandard2.Mappers/Mappers/Extensions/MappingExtensions.cs @@ -20,6 +20,22 @@ namespace AgileObjects.AgileMapper.Buildable.UnitTests.Mappers.Extensions [GeneratedCode("AgileObjects.AgileMapper.Buildable", "0.1.0.0")] public static class MappingExtensions { + public static AddressAddressPublicTwoFieldsMapper Map + ( + this PublicTwoFields<Address, Address> source + ) + { + return new AddressAddressPublicTwoFieldsMapper(source); + } + + public static PublicTwoFields<Address, Address> DeepClone + ( + this PublicTwoFields<Address, Address> source + ) + { + return new AddressAddressPublicTwoFieldsMapper(source).ToANew<PublicTwoFields<Address, Address>>(); + } + public static AddressMapper Map ( this Address source @@ -44,6 +60,14 @@ this Child source return new ChildMapper(source); } + public static Child DeepClone + ( + this Child source + ) + { + return new ChildMapper(source).ToANew<Child>(); + } + public static DateTimeHashSetMapper Map ( this HashSet<DateTime> source diff --git a/AgileMapper.Buildable.UnitTests.NetStandard2.Mappers/Mappers/Mapper.cs b/AgileMapper.Buildable.UnitTests.NetStandard2.Mappers/Mappers/Mapper.cs index 6d9d251b1..b029798ae 100644 --- a/AgileMapper.Buildable.UnitTests.NetStandard2.Mappers/Mappers/Mapper.cs +++ b/AgileMapper.Buildable.UnitTests.NetStandard2.Mappers/Mappers/Mapper.cs @@ -19,6 +19,22 @@ namespace AgileObjects.AgileMapper.Buildable.UnitTests.Mappers [GeneratedCode("AgileObjects.AgileMapper.Buildable", "0.1.0.0")] public static class Mapper { + public static AddressAddressPublicTwoFieldsMapper Map + ( + PublicTwoFields<Address, Address> source + ) + { + return new AddressAddressPublicTwoFieldsMapper(source); + } + + public static PublicTwoFields<Address, Address> DeepClone + ( + PublicTwoFields<Address, Address> source + ) + { + return new AddressAddressPublicTwoFieldsMapper(source).ToANew<PublicTwoFields<Address, Address>>(); + } + public static AddressMapper Map ( Address source @@ -43,6 +59,14 @@ Child source return new ChildMapper(source); } + public static Child DeepClone + ( + Child source + ) + { + return new ChildMapper(source).ToANew<Child>(); + } + public static DateTimeHashSetMapper Map ( HashSet<DateTime> source diff --git a/AgileMapper.Buildable.UnitTests/MapperConfiguration/ComplexTypeDeepCloneMapperConfiguration.cs b/AgileMapper.Buildable.UnitTests/MapperConfiguration/ComplexTypeDeepCloneMapperConfiguration.cs new file mode 100644 index 000000000..dae7577c0 --- /dev/null +++ b/AgileMapper.Buildable.UnitTests/MapperConfiguration/ComplexTypeDeepCloneMapperConfiguration.cs @@ -0,0 +1,13 @@ +namespace AgileObjects.AgileMapper.Buildable.UnitTests.MapperConfiguration +{ + using AgileMapper.UnitTests.Common.TestClasses; + using Buildable.Configuration; + + public class ComplexTypeDeepCloneMapperConfiguration : BuildableMapperConfiguration + { + protected override void Configure() + { + GetPlanFor<PublicTwoFields<Address, Address>>().ToANew<PublicTwoFields<Address, Address>>(); + } + } +} \ No newline at end of file diff --git a/AgileMapper.Buildable.UnitTests/Mappers/AddressAddressPublicTwoFieldsMapper.cs b/AgileMapper.Buildable.UnitTests/Mappers/AddressAddressPublicTwoFieldsMapper.cs new file mode 100644 index 000000000..a980cbae6 --- /dev/null +++ b/AgileMapper.Buildable.UnitTests/Mappers/AddressAddressPublicTwoFieldsMapper.cs @@ -0,0 +1,118 @@ +// ------------------------------------------------------------------------------ +// <auto-generated> +// This code was generated by AgileObjects.AgileMapper.Buildable. +// Runtime Version: 0.1.0.0 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// </auto-generated> +// ------------------------------------------------------------------------------ + +using System; +using System.CodeDom.Compiler; +using AgileObjects.AgileMapper; +using AgileObjects.AgileMapper.ObjectPopulation; +using AgileObjects.AgileMapper.UnitTests.Common.TestClasses; + +namespace AgileObjects.AgileMapper.Buildable.UnitTests.Mappers +{ + [GeneratedCode("AgileObjects.AgileMapper.Buildable", "0.1.0.0")] + public class AddressAddressPublicTwoFieldsMapper : MappingExecutionContextBase<PublicTwoFields<Address, Address>> + { + public AddressAddressPublicTwoFieldsMapper + ( + PublicTwoFields<Address, Address> source + ) + : base(source) + { + } + + public PublicTwoFields<Address, Address> ToANew<TTarget>() + where TTarget : PublicTwoFields<Address, Address> + { + return AddressAddressPublicTwoFieldsMapper.CreateNew(this.CreateRootMappingData(default(PublicTwoFields<Address, Address>))); + } + + private static PublicTwoFields<Address, Address> CreateNew + ( + IObjectMappingData<PublicTwoFields<Address, Address>, PublicTwoFields<Address, Address>> aaptfToAaptfData + ) + { + PublicTwoFields<Address, Address> sourceAddressAddressPublicTwoFields; + try + { + sourceAddressAddressPublicTwoFields = aaptfToAaptfData.Source; + + var addressAddressPublicTwoFields = new PublicTwoFields<Address, Address>(); + + if (sourceAddressAddressPublicTwoFields.Value1 != null) + { + addressAddressPublicTwoFields.Value1 = AddressAddressPublicTwoFieldsMapper.GetAddress1(addressAddressPublicTwoFields, sourceAddressAddressPublicTwoFields); + } + + if (sourceAddressAddressPublicTwoFields.Value2 != null) + { + addressAddressPublicTwoFields.Value2 = AddressAddressPublicTwoFieldsMapper.GetAddress2(addressAddressPublicTwoFields, sourceAddressAddressPublicTwoFields); + } + + return addressAddressPublicTwoFields; + } + catch (Exception ex) + { + throw MappingException.For( + "CreateNew", + "PublicTwoFields<Address, Address>", + "PublicTwoFields<Address, Address>", + ex); + } + } + + private static Address GetAddress1 + ( + PublicTwoFields<Address, Address> addressAddressPublicTwoFields, + PublicTwoFields<Address, Address> sourceAddressAddressPublicTwoFields + ) + { + try + { + var address = addressAddressPublicTwoFields.Value1 ?? new Address(); + address.Line1 = sourceAddressAddressPublicTwoFields.Value1.Line1; + address.Line2 = sourceAddressAddressPublicTwoFields.Value1.Line2; + + return address; + } + catch (Exception ex) + { + throw MappingException.For( + "CreateNew", + "PublicTwoFields<Address, Address>.Value1", + "PublicTwoFields<Address, Address>.Value1", + ex); + } + } + + private static Address GetAddress2 + ( + PublicTwoFields<Address, Address> addressAddressPublicTwoFields, + PublicTwoFields<Address, Address> sourceAddressAddressPublicTwoFields + ) + { + try + { + var address = addressAddressPublicTwoFields.Value2 ?? new Address(); + address.Line1 = sourceAddressAddressPublicTwoFields.Value2.Line1; + address.Line2 = sourceAddressAddressPublicTwoFields.Value2.Line2; + + return address; + } + catch (Exception ex) + { + throw MappingException.For( + "CreateNew", + "PublicTwoFields<Address, Address>.Value2", + "PublicTwoFields<Address, Address>.Value2", + ex); + } + } + } +} \ No newline at end of file diff --git a/AgileMapper.Buildable.UnitTests/Mappers/Extensions/MappingExtensions.cs b/AgileMapper.Buildable.UnitTests/Mappers/Extensions/MappingExtensions.cs index 7d887b92e..987d137ce 100644 --- a/AgileMapper.Buildable.UnitTests/Mappers/Extensions/MappingExtensions.cs +++ b/AgileMapper.Buildable.UnitTests/Mappers/Extensions/MappingExtensions.cs @@ -20,6 +20,22 @@ namespace AgileObjects.AgileMapper.Buildable.UnitTests.Mappers.Extensions [GeneratedCode("AgileObjects.AgileMapper.Buildable", "0.1.0.0")] public static class MappingExtensions { + public static AddressAddressPublicTwoFieldsMapper Map + ( + this PublicTwoFields<Address, Address> source + ) + { + return new AddressAddressPublicTwoFieldsMapper(source); + } + + public static PublicTwoFields<Address, Address> DeepClone + ( + this PublicTwoFields<Address, Address> source + ) + { + return new AddressAddressPublicTwoFieldsMapper(source).ToANew<PublicTwoFields<Address, Address>>(); + } + public static AddressMapper Map ( this Address source @@ -44,6 +60,14 @@ this Child source return new ChildMapper(source); } + public static Child DeepClone + ( + this Child source + ) + { + return new ChildMapper(source).ToANew<Child>(); + } + public static DateTimeHashSetMapper Map ( this HashSet<DateTime> source diff --git a/AgileMapper.Buildable.UnitTests/Mappers/Mapper.cs b/AgileMapper.Buildable.UnitTests/Mappers/Mapper.cs index 6d9d251b1..b029798ae 100644 --- a/AgileMapper.Buildable.UnitTests/Mappers/Mapper.cs +++ b/AgileMapper.Buildable.UnitTests/Mappers/Mapper.cs @@ -19,6 +19,22 @@ namespace AgileObjects.AgileMapper.Buildable.UnitTests.Mappers [GeneratedCode("AgileObjects.AgileMapper.Buildable", "0.1.0.0")] public static class Mapper { + public static AddressAddressPublicTwoFieldsMapper Map + ( + PublicTwoFields<Address, Address> source + ) + { + return new AddressAddressPublicTwoFieldsMapper(source); + } + + public static PublicTwoFields<Address, Address> DeepClone + ( + PublicTwoFields<Address, Address> source + ) + { + return new AddressAddressPublicTwoFieldsMapper(source).ToANew<PublicTwoFields<Address, Address>>(); + } + public static AddressMapper Map ( Address source @@ -43,6 +59,14 @@ Child source return new ChildMapper(source); } + public static Child DeepClone + ( + Child source + ) + { + return new ChildMapper(source).ToANew<Child>(); + } + public static DateTimeHashSetMapper Map ( HashSet<DateTime> source diff --git a/AgileMapper.Buildable.UnitTests/WhenBuildingComplexTypeCreateNewMappers.cs b/AgileMapper.Buildable.UnitTests/WhenBuildingComplexTypeCreateNewMappers.cs index 297dc00aa..539759b2c 100644 --- a/AgileMapper.Buildable.UnitTests/WhenBuildingComplexTypeCreateNewMappers.cs +++ b/AgileMapper.Buildable.UnitTests/WhenBuildingComplexTypeCreateNewMappers.cs @@ -39,5 +39,23 @@ public void ShouldBuildSingleSourceMultipleTargetMapper() notSupportedMessage.ShouldContain("source type 'PublicField<string>'"); notSupportedMessage.ShouldContain("target type 'PublicField<DateTime>'"); } + + [Fact] + public void ShouldBuildADeepCloneMapper() + { + var source = new PublicTwoFields<Address, Address> + { + Value1 = new Address { Line1 = "Line 1.1" }, + Value2 = new Address { Line2 = "Line 2.2" } + }; + + var result = source.DeepClone(); + + result.ShouldNotBeSameAs(source); + result.Value1.Line1.ShouldBe("Line 1.1"); + result.Value1.Line2.ShouldBeNull(); + result.Value2.Line1.ShouldBeNull(); + result.Value2.Line2.ShouldBe("Line 2.2"); + } } } \ No newline at end of file diff --git a/AgileMapper.Buildable/BuildableMapperExtensions.cs b/AgileMapper.Buildable/BuildableMapperExtensions.cs index 2f330a082..2f76efe0c 100644 --- a/AgileMapper.Buildable/BuildableMapperExtensions.cs +++ b/AgileMapper.Buildable/BuildableMapperExtensions.cs @@ -267,14 +267,7 @@ private static SourceCodeExpression BuildStaticMapperClass( { staticMapperClass.AddGeneratedCodeAttribute(); staticMapperClass.SetStatic(); - - foreach (var mapperClassGroup in mapperClassGroups) - { - staticMapperClass.AddMethod("Map", mapMethod => - { - mapperClassGroup.Configure(mapMethod); - }); - } + staticMapperClass.AddStaticMappingMethods(mapperClassGroups); }); }); } @@ -293,21 +286,62 @@ private static SourceCodeExpression BuildMappingExtensionsClass( mappingExtensionsClass.AddGeneratedCodeAttribute(); mappingExtensionsClass.SetStatic(); - foreach (var mapperClassGroup in mapperClassGroups) - { - mappingExtensionsClass.AddMethod("Map", mapMethod => - { - mapMethod.SetExtensionMethod(); - mapperClassGroup.Configure(mapMethod); - }); - } + mappingExtensionsClass.AddStaticMappingMethods( + mapperClassGroups, + method => method.SetExtensionMethod()); }); }); } + private static void AddStaticMappingMethods( + this IClassMemberConfigurator mappingClass, + IEnumerable<BuildableMapperGroup> mapperClassGroups, + Action<IClassMethodExpressionConfigurator> methodConfig = null) + { + foreach (var mapperClassGroup in mapperClassGroups) + { + mappingClass.AddMethod("Map", mapMethod => + { + methodConfig?.Invoke(mapMethod); + mapperClassGroup.Configure(mapMethod); + }); + + if (mapperClassGroup.IncludeDeepClone) + { + mappingClass.AddMethod("DeepClone", mapMethod => + { + methodConfig?.Invoke(mapMethod); + mapperClassGroup.ConfigureDeepClone(mapMethod); + }); + } + } + } + private static void Configure( this BuildableMapperGroup mapperClassGroup, IConcreteTypeMethodExpressionConfigurator mapMethod) + { + mapMethod.SetBody(GetMapperInstantiation(mapperClassGroup, mapMethod)); + } + + private static void ConfigureDeepClone( + this BuildableMapperGroup mapperClassGroup, + IConcreteTypeMethodExpressionConfigurator mapMethod) + { + var newMapper = GetMapperInstantiation(mapperClassGroup, mapMethod); + + var createNewCall = Call( + newMapper, + newMapper.Type + .GetMethod("ToANew")! + .MakeGenericMethod(mapperClassGroup.SourceType)); + + mapMethod.SetBody(createNewCall); + } + + private static NewExpression GetMapperInstantiation( + BuildableMapperGroup mapperClassGroup, + IMethodExpressionBaseConfigurator mapMethod) { var sourceType = mapperClassGroup.SourceType; var mapperClass = mapperClassGroup.MapperClass; @@ -318,8 +352,8 @@ private static void Configure( var newMapper = New( mapperClass.Type.GetPublicInstanceConstructor(sourceType), sourceParameter); - - mapMethod.SetBody(newMapper); + + return newMapper; } private static void AddGeneratedCodeHeader( diff --git a/AgileMapper.Buildable/BuildableMapperGroup.cs b/AgileMapper.Buildable/BuildableMapperGroup.cs index 565f6cc85..099aa39db 100644 --- a/AgileMapper.Buildable/BuildableMapperGroup.cs +++ b/AgileMapper.Buildable/BuildableMapperGroup.cs @@ -13,6 +13,7 @@ internal class BuildableMapperGroup { private bool? _hasDerivedTypes; + private bool? _includeDeepClone; private MethodInfo _createChildMappingDataMethod; public BuildableMapperGroup( @@ -38,6 +39,16 @@ private bool DetermineIfHasDerivedTypes() .Any(plan => plan.Any(p => p.HasDerivedTypes)); } + public bool IncludeDeepClone + => _includeDeepClone ??= DetermineIfIncludeDeepClone(); + + private bool DetermineIfIncludeDeepClone() + { + return MappingMethodsByPlan.Keys.Any(plan => + plan.RuleSetName == "CreateNew" && + plan.Root.TargetType == SourceType); + } + public Type MapperBaseType { get; } public ConstructorInfo MapperBaseTypeConstructor { get; } diff --git a/AgileMapper.UnitTests.Common/FluentAssertionExtensions.cs b/AgileMapper.UnitTests.Common/FluentAssertionExtensions.cs index 7bbbce535..65089107c 100644 --- a/AgileMapper.UnitTests.Common/FluentAssertionExtensions.cs +++ b/AgileMapper.UnitTests.Common/FluentAssertionExtensions.cs @@ -170,11 +170,11 @@ public static void ShouldBe<T1, T2>(this IEnumerable<T1> actualValues, IEnumerab actualValues.Project(converter).SequenceEqual(expectedValues).ShouldBeTrue(); } - public static void ShouldNotBe<TActual, TExpected>(this TActual value, TExpected expectedValue) + public static void ShouldNotBe<TActual, TExpected>(this TActual value, TExpected unexpectedValue) { - if (AreEqual(expectedValue, value)) + if (AreEqual(unexpectedValue, value)) { - Asplode("Not " + expectedValue, value.ToString()); + Asplode("Not " + unexpectedValue, value.ToString()); } } From 3b024964cc3ce2cd39e7cd807970c190d010568e Mon Sep 17 00:00:00 2001 From: Steve Wilkes <steve@agileobjects.co.uk> Date: Sat, 7 Aug 2021 15:23:03 +0100 Subject: [PATCH 62/65] Updating README --- README.md | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 3d296135f..a99dc7bc7 100644 --- a/README.md +++ b/README.md @@ -2,8 +2,9 @@ [![NuGet version](https://badge.fury.io/nu/AgileObjects.AgileMapper.svg)](https://badge.fury.io/nu/AgileObjects.AgileMapper) -AgileMapper is a zero-configuration, [highly-configurable](https://agilemapper.readthedocs.io/configuration), unopinionated object -mapper with [viewable execution plans](https://agilemapper.readthedocs.io/Using-Execution-Plans). +AgileMapper is a zero-configuration, [highly-configurable](https://agilemapper.readthedocs.io/configuration), +unopinionated object mapper with execution plans you can [view](https://agilemapper.readthedocs.io/Using-Execution-Plans), +or [generate and build](https://agilemapper.readthedocs.io/mapper-generation) into your source code. It flattens, unflattens, deep clones, [merges](https://agilemapper.readthedocs.io/Performing-Merges), [updates](https://agilemapper.readthedocs.io/Performing-Updates) and [projects queries](https://agilemapper.readthedocs.io/query-projection) via [extension methods](https://agilemapper.readthedocs.io/Mapping-Extension-Methods), or a @@ -37,6 +38,7 @@ Mapper.Map(customerViewModel).Over(customer); Mapper.Map(customerOne).OnTo(customerTwo); ``` -It's [available via NuGet](https://www.nuget.org/packages/AgileObjects.AgileMapper) and licensed with the -[MIT licence](https://github.com/agileobjects/AgileMapper/blob/master/LICENCE.md). Check out -[the documentation](https://agilemapper.readthedocs.io) for more! +It's [available via NuGet](https://www.nuget.org/packages/AgileObjects.AgileMapper), with code +generation performed by [this extension](https://www.nuget.org/packages/AgileObjects.AgileMapper.Buildable), +both licensed with the [MIT licence](https://github.com/agileobjects/AgileMapper/blob/master/LICENCE.md). +Check out [the documentation](https://agilemapper.readthedocs.io) for more! From 1d6cabb901dcf8e863ce1b7e8098d2516b15f065 Mon Sep 17 00:00:00 2001 From: Steve Wilkes <steve@agileobjects.co.uk> Date: Tue, 10 Aug 2021 18:32:53 +0100 Subject: [PATCH 63/65] Adding documentation --- docs/src/Building-Mappers.md | 0 docs/src/configuration/Classes.md | 2 +- docs/src/index.md | 18 +++++--- docs/src/mapper-generation/Configuration.md | 15 +++++++ docs/src/mapper-generation/Quickstart.md | 47 +++++++++++++++++++++ docs/src/mapper-generation/index.md | 19 +++++++++ mkdocs.yml | 4 ++ 7 files changed, 99 insertions(+), 6 deletions(-) create mode 100644 docs/src/Building-Mappers.md create mode 100644 docs/src/mapper-generation/Configuration.md create mode 100644 docs/src/mapper-generation/Quickstart.md create mode 100644 docs/src/mapper-generation/index.md diff --git a/docs/src/Building-Mappers.md b/docs/src/Building-Mappers.md new file mode 100644 index 000000000..e69de29bb diff --git a/docs/src/configuration/Classes.md b/docs/src/configuration/Classes.md index 282b0495e..d85b5dd89 100644 --- a/docs/src/configuration/Classes.md +++ b/docs/src/configuration/Classes.md @@ -11,7 +11,7 @@ public class ProductMappingConfiguration : MapperConfiguration { protected override void Configure() { - // Configure default Mapper ProductDto -> Productmapping: + // Configure default Mapper ProductDto -> Product mapping: WhenMapping .From<ProductDto>() .To<Product>() diff --git a/docs/src/index.md b/docs/src/index.md index 5ee83a9fe..47e5ed3ea 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -1,10 +1,18 @@ ## Overview -AgileMapper is a zero-configuration, [highly-configurable](/configuration), unopinionated object-object mapper with [viewable execution plans](/Using-Execution-Plans), targeting .NET 3.5+ and [.NET Standard 1.0+](https://docs.microsoft.com/en-us/dotnet/articles/standard/library). It flattens, unflattens, deep clones, [merges](/Performing-Merges), [updates](/Performing-Updates) and [projects queries](/query-projection) via [extension methods](/Mapping-Extension-Methods), or a [static or instance](/Static-vs-Instance-Mappers) API. - -Mapping functions are created and cached the first time two types are mapped - no up-front configuration is necessary. You can [cache up-front](/Using-Execution-Plans) if you prefer, though. - -[Available via NuGet](https://www.nuget.org/packages/AgileObjects.AgileMapper) and licensed with the [MIT licence](https://github.com/agileobjects/AgileMapper/blob/master/LICENCE.md), you can install it via the [package manager console](https://docs.nuget.org/consume/package-manager-console): +AgileMapper is a zero-configuration, [highly-configurable](/configuration), unopinionated object +mapper with execution plans you can [view](/Using-Execution-Plans), or +[generate and build](/mapper-generation) into your source code. It flattens, unflattens, deep clones, +[merges](/Performing-Merges), [updates](/Performing-Updates) and [projects queries](/query-projection) +via [extension methods](/Mapping-Extension-Methods), or a [static or instance](/Static-vs-Instance-Mappers) +API. It targets .NET 3.5+ and [.NET Standard 1.0+](https://docs.microsoft.com/en-us/dotnet/articles/standard/library). + +Mapping functions are created and cached the first time two types are mapped - no up-front +configuration is necessary. You can [cache up-front](/Using-Execution-Plans) if you prefer, though. + +[Available via NuGet](https://www.nuget.org/packages/AgileObjects.AgileMapper) and licensed with the +[MIT licence](https://github.com/agileobjects/AgileMapper/blob/master/LICENCE.md), you can install it +via the [package manager console](https://docs.nuget.org/consume/package-manager-console) with: ```shell PM> Install-Package AgileObjects.AgileMapper diff --git a/docs/src/mapper-generation/Configuration.md b/docs/src/mapper-generation/Configuration.md new file mode 100644 index 000000000..d8220150f --- /dev/null +++ b/docs/src/mapper-generation/Configuration.md @@ -0,0 +1,15 @@ +Mapper source code generation can be configured in the following ways. + +## Change the Output Project + +To output your mappers to a different project, add the following to your mapper configuration project: + +```xml +<PropertyGroup> + <MappersOutputProject>[PathToTargetCsProjFile]</MappersOutputProject> +</PropertyGroup> +``` + +Where `PathToTargetCsProjFile` is the fully-qualified or relative path to the `.csproj` file to which +mappers should be added. + diff --git a/docs/src/mapper-generation/Quickstart.md b/docs/src/mapper-generation/Quickstart.md new file mode 100644 index 000000000..149f29fe6 --- /dev/null +++ b/docs/src/mapper-generation/Quickstart.md @@ -0,0 +1,47 @@ +## Add the Package + +Add the AgileMapper.Buildable package to the project which will contain your configuration (see +below), and generate your mappers when built: + +```shell +PM> Install-Package AgileObjects.AgileMapper.Buildable -Pre +``` +The `-Pre` flag is required as AgileMapper.Buildable is currently in preview. + +## Add Configuration + +To specify which mapper source code should be generated, add one or more +[configuration classes](/configuration/Classes) to a project, derived from AgileMapper.Buildable's +`BuildableMapperConfiguration` class: + +```cs +public class ProductMappingConfiguration : BuildableMapperConfiguration +{ + protected override void Configure() + { + // Configure default Mapper ProductDto -> Product mapping: + WhenMapping + .From<ProductDto>() + .To<Product>() + .Map((dto, p) => dto.Spec) + .To(p => p.Specification) + .And + .Ignore(p => p.Price, p => p.CatNum); + + // Generate all Product -> ProductDto mappers: + GetPlansFor<Product>().To<ProductDto>(); + } +} +``` + +AgileMapper.Buildable configuration classes work the same as +[AgileMapper configuration classes](/configuration/Classes), but instead of configuring mappers built +at runtime, they configure mappers generated at build time. + +AgileMapper.Buildable auto-discovers its configuration, and does not need it to be registered using +the `Mapper.WhenMapping.UseConfigurations.From*` methods. + +## Build the Project + +When the project is built, it will generate mapper source code based on your configuration. Mappers +are output to a `Mappers` folder and namespace at the root of the project. \ No newline at end of file diff --git a/docs/src/mapper-generation/index.md b/docs/src/mapper-generation/index.md new file mode 100644 index 000000000..0df47c269 --- /dev/null +++ b/docs/src/mapper-generation/index.md @@ -0,0 +1,19 @@ +AgileMapper.Buildable is an AgileMapper extension which uses a custom MSBuild target to generates +mapper source code at build time. This provides the best mapping performance, and enables you to step +into and debug your mappers at runtime. + +It targets .NET 4.6.1+ and [.NET Standard 2.0+](https://docs.microsoft.com/en-us/dotnet/articles/standard/library), +and supports [dotnet build](https://docs.microsoft.com/en-us/dotnet/core/tools/dotnet-build) and +[MSBuild](https://docs.microsoft.com/en-us/visualstudio/msbuild/msbuild), +[SDK](https://docs.microsoft.com/en-us/dotnet/core/project-sdk/overview) and non-SDK projects. + +[Available via NuGet](https://www.nuget.org/packages/AgileObjects.AgileMapper.Buildable) and licensed +with the [MIT licence](https://github.com/agileobjects/AgileMapper/blob/master/LICENCE.md), you can +install it via the [package manager console](https://docs.nuget.org/consume/package-manager-console) +with: + +```shell +PM> Install-Package AgileObjects.AgileMapper.Buildable -Pre +``` + +The `-Pre` flag is required as AgileMapper.Buildable is currently in preview. \ No newline at end of file diff --git a/mkdocs.yml b/mkdocs.yml index cd96a9e96..7a4e37cfb 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -42,6 +42,10 @@ nav: - Recursion: query-projection/Recursive-Relationships.md - Join entities: query-projection/Join-Entities.md - Entity Framework: query-projection/Entity-Framework.md + - Source Code Generation: + - Overview: mapper-generation/index.md + - Quickstart: mapper-generation/Quickstart.md + - Configuration: mapper-generation/Configuration.md - Configuration: - Overview: configuration/index.md - Configuring inline: configuration/Inline.md From 802c71bcd9f834a4c3b92f1928da503544ad300f Mon Sep 17 00:00:00 2001 From: Steve Wilkes <steve@agileobjects.co.uk> Date: Tue, 10 Aug 2021 19:02:08 +0100 Subject: [PATCH 64/65] Dox --- docs/src/mapper-generation/Configuration.md | 3 +- docs/src/mapper-generation/Quickstart.md | 23 +++-- docs/src/mapper-generation/index.md | 2 +- mkdocs.yml | 96 ++++++++++----------- 4 files changed, 68 insertions(+), 56 deletions(-) diff --git a/docs/src/mapper-generation/Configuration.md b/docs/src/mapper-generation/Configuration.md index d8220150f..91c71d681 100644 --- a/docs/src/mapper-generation/Configuration.md +++ b/docs/src/mapper-generation/Configuration.md @@ -2,7 +2,8 @@ Mapper source code generation can be configured in the following ways. ## Change the Output Project -To output your mappers to a different project, add the following to your mapper configuration project: +To output your mappers to a different project, add the following to +[your mapper configuration](quickstart) project: ```xml <PropertyGroup> diff --git a/docs/src/mapper-generation/Quickstart.md b/docs/src/mapper-generation/Quickstart.md index 149f29fe6..d17cd44d4 100644 --- a/docs/src/mapper-generation/Quickstart.md +++ b/docs/src/mapper-generation/Quickstart.md @@ -11,7 +11,7 @@ The `-Pre` flag is required as AgileMapper.Buildable is currently in preview. ## Add Configuration To specify which mapper source code should be generated, add one or more -[configuration classes](/configuration/Classes) to a project, derived from AgileMapper.Buildable's +[configuration classes](../configuration/classes) to a project, derived from AgileMapper.Buildable's `BuildableMapperConfiguration` class: ```cs @@ -35,13 +35,24 @@ public class ProductMappingConfiguration : BuildableMapperConfiguration ``` AgileMapper.Buildable configuration classes work the same as -[AgileMapper configuration classes](/configuration/Classes), but instead of configuring mappers built -at runtime, they configure mappers generated at build time. +[AgileMapper configuration classes](../configuration/classes), but instead of configuring mappers +built at runtime, they configure mappers generated at build time. AgileMapper.Buildable auto-discovers its configuration, and does not need it to be registered using the `Mapper.WhenMapping.UseConfigurations.From*` methods. -## Build the Project +## Generate Mappers -When the project is built, it will generate mapper source code based on your configuration. Mappers -are output to a `Mappers` folder and namespace at the root of the project. \ No newline at end of file +When the project is built, it will generate the following source code based on your configuration: + +- One instance-scoped mapper per configured source type +- A static `Mapper` class +- A set of mapping extension methods. + +These files are output to a `Mappers` folder and namespace at the root of the project. The static +mapper and extension methods use the instance mappers to perform mappings. + +The generated static Mapper and extension methods have the same signatures as AgileMapper's +equivalent classes, so you can switch from AgileMapper's mapping to your generated mappers simply by +changing the `using AgileObjects.AgileMapper;` namespace import to +`using [YourProject.Namespace].Mappers;`. \ No newline at end of file diff --git a/docs/src/mapper-generation/index.md b/docs/src/mapper-generation/index.md index 0df47c269..b17d8ee0d 100644 --- a/docs/src/mapper-generation/index.md +++ b/docs/src/mapper-generation/index.md @@ -1,4 +1,4 @@ -AgileMapper.Buildable is an AgileMapper extension which uses a custom MSBuild target to generates +AgileMapper.Buildable is an AgileMapper extension which uses a custom MSBuild target to generate mapper source code at build time. This provides the best mapping performance, and enables you to step into and debug your mappers at runtime. diff --git a/mkdocs.yml b/mkdocs.yml index 7a4e37cfb..5906f6143 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -16,59 +16,59 @@ extra_javascript: nav: - General Use: - - Member matching: Member-Matching.md - - Object construction: Object-Construction.md - - Type conversion: Type-Conversion.md - - Collections: Collections.md - - Entity mapping: Entity-Mapping.md - - Dictionary mapping: Dictionary-Mapping.md - - Dynamic mapping: Dynamic-Mapping.md - - Meta members: Meta-Members.md - - Enum mapping: Enum-Mapping.md - - Object updating: Performing-Updates.md - - Object merging: Performing-Merges.md - - Object flattening: Object-Flattening.md - - Object unflattening: Object-Unflattening.md - - Using execution plans: Using-Execution-Plans.md - - Derived types: Derived-Types.md - - Object tracking: Mapped-Object-Tracking.md - - Static vs Instance API: Static-vs-Instance-Mappers.md - - Mapping extension methods: Mapping-Extension-Methods.md - - Mapping validation: Validating-Mappings.md + - Member matching: member-matching.md + - Object construction: object-construction.md + - Type conversion: type-tonversion.md + - Collections: collections.md + - Entity mapping: entity-mapping.md + - Dictionary mapping: dictionary-mapping.md + - Dynamic mapping: dynamic-mapping.md + - Meta members: meta-members.md + - Enum mapping: enum-mapping.md + - Object updating: performing-updates.md + - Object merging: performing-merges.md + - Object flattening: object-flattening.md + - Object unflattening: object-unflattening.md + - Using execution plans: using-execution-plans.md + - Derived types: derived-types.md + - Object tracking: mapped-object-tracking.md + - Static vs Instance API: static-vs-instance-mappers.md + - Mapping extension methods: mapping-extension-methods.md + - Mapping validation: validating-mappings.md - Query Projection: - Overview: query-projection/index.md - - Configuration: query-projection/Configuration.md - - Derived Types: query-projection/Derived-Types.md - - Recursion: query-projection/Recursive-Relationships.md - - Join entities: query-projection/Join-Entities.md - - Entity Framework: query-projection/Entity-Framework.md + - Configuration: query-projection/configuration.md + - Derived Types: query-projection/derived-types.md + - Recursion: query-projection/recursive-relationships.md + - Join entities: query-projection/join-entities.md + - Entity Framework: query-projection/entity-framework.md - Source Code Generation: - Overview: mapper-generation/index.md - - Quickstart: mapper-generation/Quickstart.md - - Configuration: mapper-generation/Configuration.md + - Quickstart: mapper-generation/quickstart.md + - Configuration: mapper-generation/configuration.md - Configuration: - Overview: configuration/index.md - - Configuring inline: configuration/Inline.md - - Configuration classes: configuration/Classes.md - - Dependency injection: configuration/Dependency-Injection.md - - Member values: configuration/Member-Values.md - - Constructor arguments: configuration/Constructor-Arguments.md - - Entity mapping: configuration/Entity-Mapping.md - - Member name patterns: configuration/Member-Name-Patterns.md - - To-string formatting: configuration/To-String-Formatting.md + - Configuring inline: configuration/inline.md + - Configuration classes: configuration/classes.md + - Dependency injection: configuration/dependency-injection.md + - Member values: configuration/member-values.md + - Constructor arguments: configuration/constructor-arguments.md + - Entity mapping: configuration/entity-mapping.md + - Member name patterns: configuration/member-name-patterns.md + - To-string formatting: configuration/to-string-formatting.md - Enum pairing: Enum-Mapping/#configuring-enum-pairs - - Dictionaries: configuration/Dictionary-Mapping.md - - Dynamics: configuration/Dynamic-Mapping.md - - Ignoring source values: configuration/Ignoring-Source-Values.md - - Ignoring source members: configuration/Ignoring-Source-Members.md - - Ignoring target members: configuration/Ignoring-Target-Members.md - - Object mapping: configuration/Object-Mapping.md - - Object identifiers: configuration/Object-Identifiers.md - - Object construction: configuration/Object-Construction.md - - Null results: configuration/Null-Results.md - - Derived type pairing: configuration/Pairing-Derived-Types.md - - Cloning Mappers: configuration/Cloning-Mappers.md - - Assembly scanning: configuration/Assembly-Scanning.md - - Exception handling: configuration/Exception-Handling.md - - Mapping callbacks: configuration/Mapping-Callbacks.md + - Dictionaries: configuration/dictionary-mapping.md + - Dynamics: configuration/dynamic-mapping.md + - Ignoring source values: configuration/ignoring-source-Values.md + - Ignoring source members: configuration/ignoring-source-Members.md + - Ignoring target members: configuration/ignoring-target-Members.md + - Object mapping: configuration/object-mapping.md + - Object identifiers: configuration/object-identifiers.md + - Object construction: configuration/object-construction.md + - Null results: configuration/null-results.md + - Derived type pairing: configuration/pairing-derived-types.md + - Cloning Mappers: configuration/cloning-mappers.md + - Assembly scanning: configuration/assembly-scanning.md + - Exception handling: configuration/exception-handling.md + - Mapping callbacks: configuration/mapping-callbacks.md theme: readthedocs \ No newline at end of file From 6cd119c53ce98cb6dacb71377dffdc5adf2c5da5 Mon Sep 17 00:00:00 2001 From: Steve Wilkes <steve@agileobjects.co.uk> Date: Tue, 10 Aug 2021 19:09:26 +0100 Subject: [PATCH 65/65] Dox --- mkdocs.yml | 96 +++++++++++++++++++++++++++--------------------------- 1 file changed, 48 insertions(+), 48 deletions(-) diff --git a/mkdocs.yml b/mkdocs.yml index 5906f6143..7a4e37cfb 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -16,59 +16,59 @@ extra_javascript: nav: - General Use: - - Member matching: member-matching.md - - Object construction: object-construction.md - - Type conversion: type-tonversion.md - - Collections: collections.md - - Entity mapping: entity-mapping.md - - Dictionary mapping: dictionary-mapping.md - - Dynamic mapping: dynamic-mapping.md - - Meta members: meta-members.md - - Enum mapping: enum-mapping.md - - Object updating: performing-updates.md - - Object merging: performing-merges.md - - Object flattening: object-flattening.md - - Object unflattening: object-unflattening.md - - Using execution plans: using-execution-plans.md - - Derived types: derived-types.md - - Object tracking: mapped-object-tracking.md - - Static vs Instance API: static-vs-instance-mappers.md - - Mapping extension methods: mapping-extension-methods.md - - Mapping validation: validating-mappings.md + - Member matching: Member-Matching.md + - Object construction: Object-Construction.md + - Type conversion: Type-Conversion.md + - Collections: Collections.md + - Entity mapping: Entity-Mapping.md + - Dictionary mapping: Dictionary-Mapping.md + - Dynamic mapping: Dynamic-Mapping.md + - Meta members: Meta-Members.md + - Enum mapping: Enum-Mapping.md + - Object updating: Performing-Updates.md + - Object merging: Performing-Merges.md + - Object flattening: Object-Flattening.md + - Object unflattening: Object-Unflattening.md + - Using execution plans: Using-Execution-Plans.md + - Derived types: Derived-Types.md + - Object tracking: Mapped-Object-Tracking.md + - Static vs Instance API: Static-vs-Instance-Mappers.md + - Mapping extension methods: Mapping-Extension-Methods.md + - Mapping validation: Validating-Mappings.md - Query Projection: - Overview: query-projection/index.md - - Configuration: query-projection/configuration.md - - Derived Types: query-projection/derived-types.md - - Recursion: query-projection/recursive-relationships.md - - Join entities: query-projection/join-entities.md - - Entity Framework: query-projection/entity-framework.md + - Configuration: query-projection/Configuration.md + - Derived Types: query-projection/Derived-Types.md + - Recursion: query-projection/Recursive-Relationships.md + - Join entities: query-projection/Join-Entities.md + - Entity Framework: query-projection/Entity-Framework.md - Source Code Generation: - Overview: mapper-generation/index.md - - Quickstart: mapper-generation/quickstart.md - - Configuration: mapper-generation/configuration.md + - Quickstart: mapper-generation/Quickstart.md + - Configuration: mapper-generation/Configuration.md - Configuration: - Overview: configuration/index.md - - Configuring inline: configuration/inline.md - - Configuration classes: configuration/classes.md - - Dependency injection: configuration/dependency-injection.md - - Member values: configuration/member-values.md - - Constructor arguments: configuration/constructor-arguments.md - - Entity mapping: configuration/entity-mapping.md - - Member name patterns: configuration/member-name-patterns.md - - To-string formatting: configuration/to-string-formatting.md + - Configuring inline: configuration/Inline.md + - Configuration classes: configuration/Classes.md + - Dependency injection: configuration/Dependency-Injection.md + - Member values: configuration/Member-Values.md + - Constructor arguments: configuration/Constructor-Arguments.md + - Entity mapping: configuration/Entity-Mapping.md + - Member name patterns: configuration/Member-Name-Patterns.md + - To-string formatting: configuration/To-String-Formatting.md - Enum pairing: Enum-Mapping/#configuring-enum-pairs - - Dictionaries: configuration/dictionary-mapping.md - - Dynamics: configuration/dynamic-mapping.md - - Ignoring source values: configuration/ignoring-source-Values.md - - Ignoring source members: configuration/ignoring-source-Members.md - - Ignoring target members: configuration/ignoring-target-Members.md - - Object mapping: configuration/object-mapping.md - - Object identifiers: configuration/object-identifiers.md - - Object construction: configuration/object-construction.md - - Null results: configuration/null-results.md - - Derived type pairing: configuration/pairing-derived-types.md - - Cloning Mappers: configuration/cloning-mappers.md - - Assembly scanning: configuration/assembly-scanning.md - - Exception handling: configuration/exception-handling.md - - Mapping callbacks: configuration/mapping-callbacks.md + - Dictionaries: configuration/Dictionary-Mapping.md + - Dynamics: configuration/Dynamic-Mapping.md + - Ignoring source values: configuration/Ignoring-Source-Values.md + - Ignoring source members: configuration/Ignoring-Source-Members.md + - Ignoring target members: configuration/Ignoring-Target-Members.md + - Object mapping: configuration/Object-Mapping.md + - Object identifiers: configuration/Object-Identifiers.md + - Object construction: configuration/Object-Construction.md + - Null results: configuration/Null-Results.md + - Derived type pairing: configuration/Pairing-Derived-Types.md + - Cloning Mappers: configuration/Cloning-Mappers.md + - Assembly scanning: configuration/Assembly-Scanning.md + - Exception handling: configuration/Exception-Handling.md + - Mapping callbacks: configuration/Mapping-Callbacks.md theme: readthedocs \ No newline at end of file