From c855205524a59a07c34e239d99ddd5db38785738 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Sat, 29 Jun 2019 08:34:16 +0100 Subject: [PATCH 1/7] ToTarget changes --- .../AgileMapper.UnitTests.csproj | 4 ++ .../WhenConfiguringDataSources.cs | 16 ++++- .../TestClasses/PublicDictionary.cs | 62 +++++++++++++++++++ AgileMapper.UnitTests/packages.config | 1 + .../Configuration/ParametersSwapper.cs | 2 +- .../Finders/DataSourceFindContext.cs | 13 ++-- AgileMapper/DataSources/IDataSource.cs | 4 +- 7 files changed, 90 insertions(+), 12 deletions(-) create mode 100644 AgileMapper.UnitTests/TestClasses/PublicDictionary.cs diff --git a/AgileMapper.UnitTests/AgileMapper.UnitTests.csproj b/AgileMapper.UnitTests/AgileMapper.UnitTests.csproj index 28d4c8701..ff9527024 100644 --- a/AgileMapper.UnitTests/AgileMapper.UnitTests.csproj +++ b/AgileMapper.UnitTests/AgileMapper.UnitTests.csproj @@ -50,6 +50,9 @@ ..\packages\Microsoft.Extensions.Primitives.2.0.0\lib\netstandard2.0\Microsoft.Extensions.Primitives.dll + + ..\packages\Newtonsoft.Json.12.0.2\lib\net45\Newtonsoft.Json.dll + @@ -220,6 +223,7 @@ + diff --git a/AgileMapper.UnitTests/Configuration/WhenConfiguringDataSources.cs b/AgileMapper.UnitTests/Configuration/WhenConfiguringDataSources.cs index b03c7d7c2..3057db1c0 100644 --- a/AgileMapper.UnitTests/Configuration/WhenConfiguringDataSources.cs +++ b/AgileMapper.UnitTests/Configuration/WhenConfiguringDataSources.cs @@ -8,6 +8,7 @@ using AgileMapper.Extensions; using AgileMapper.Members; using Common; + using Newtonsoft.Json.Linq; using TestClasses; #if !NET35 using Xunit; @@ -536,7 +537,7 @@ public void ShouldApplyAConfiguredExpressionToAnArray() .From>() .To>() #if NETCOREAPP2_0 - .Map(ctx => ctx.Source.Value.Split(':', System.StringSplitOptions.None)) + .Map(ctx => ctx.Source.Value.Split(':', StringSplitOptions.None)) #else .Map(ctx => ctx.Source.Value.Split(':')) #endif @@ -1619,6 +1620,19 @@ public void ShouldHandleDeepNestedRuntimeTypedMembersWithACachedMappingPlan() } } + [Fact] + public void ShouldMapToTargetUsingASimpleToComplexTypeFactoryMethod() + { + using (var mapper = Mapper.CreateNew()) + { + mapper.WhenMapping + .From().To() + .Map(ctx => JToken.Parse(ctx.Source)).ToTarget(); + + mapper.GetPlanFor>().ToANew>(); + } + } + #region Helper Classes internal class IdTester diff --git a/AgileMapper.UnitTests/TestClasses/PublicDictionary.cs b/AgileMapper.UnitTests/TestClasses/PublicDictionary.cs new file mode 100644 index 000000000..cfd65c354 --- /dev/null +++ b/AgileMapper.UnitTests/TestClasses/PublicDictionary.cs @@ -0,0 +1,62 @@ +namespace AgileObjects.AgileMapper.UnitTests.TestClasses +{ + using System.Collections; + using System.Collections.Generic; + + internal class PublicDictionary : IDictionary + { + private readonly Dictionary _data; + + public PublicDictionary() + { + _data = new Dictionary(); + } + + public static PublicDictionary Deserialize(string data) + => new PublicDictionary(); + + private ICollection> DataAsCollection => _data; + + public object this[string key] + { + get => _data[key]; + set => _data[key] = value; + } + + public ICollection Keys => _data.Keys; + + public ICollection Values => _data.Values; + + public int Count => _data.Count; + + public bool IsReadOnly => false; + + public void Add(string key, object value) => _data.Add(key, value); + + public void Add(KeyValuePair item) => DataAsCollection.Add(item); + + public void Clear() => _data.Clear(); + + public bool Contains(KeyValuePair item) + => DataAsCollection.Contains(item); + + public bool ContainsKey(string key) => _data.ContainsKey(key); + + public void CopyTo(KeyValuePair[] array, int arrayIndex) + => DataAsCollection.CopyTo(array, arrayIndex); + + public IEnumerator> GetEnumerator() + => DataAsCollection.GetEnumerator(); + + public bool Remove(string key) => _data.Remove(key); + + public bool Remove(KeyValuePair item) + => DataAsCollection.Remove(item); + + public bool TryGetValue(string key, out object value) + => _data.TryGetValue(key, out value); + + IEnumerator IEnumerable.GetEnumerator() + => DataAsCollection.GetEnumerator(); + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests/packages.config b/AgileMapper.UnitTests/packages.config index 09b6deb71..0ba304c53 100644 --- a/AgileMapper.UnitTests/packages.config +++ b/AgileMapper.UnitTests/packages.config @@ -3,6 +3,7 @@ + diff --git a/AgileMapper/Configuration/ParametersSwapper.cs b/AgileMapper/Configuration/ParametersSwapper.cs index 88e1c8507..465846248 100644 --- a/AgileMapper/Configuration/ParametersSwapper.cs +++ b/AgileMapper/Configuration/ParametersSwapper.cs @@ -170,7 +170,7 @@ private static Expression ReplaceParameters( private static MappingContextInfo GetAppropriateMappingContext(SwapArgs swapArgs) { - if (swapArgs.ContextTypes.All(t => t.IsSimple())) + if (swapArgs.ContextTypes[0].IsSimple()) { return GetSimpleTypesMappingContextInfo(swapArgs); } diff --git a/AgileMapper/DataSources/Finders/DataSourceFindContext.cs b/AgileMapper/DataSources/Finders/DataSourceFindContext.cs index 27c0a0a01..5103d28cd 100644 --- a/AgileMapper/DataSources/Finders/DataSourceFindContext.cs +++ b/AgileMapper/DataSources/Finders/DataSourceFindContext.cs @@ -26,18 +26,15 @@ public DataSourceFindContext(IChildMemberMappingData childMappingData) GetConfiguredDataSources(originalChildMapperData)); } - private IList GetConfiguredDataSources(IMemberMapperData mapperData) - { - return MapperData - .MapperContext - .UserConfigurations - .GetDataSources(mapperData); - } + private IList GetConfiguredDataSources(IMemberMapperData mapperData) + => MapperContext.UserConfigurations.GetDataSources(mapperData); public IChildMemberMappingData ChildMappingData { get; } public IMemberMapperData MapperData => ChildMappingData.MapperData; + public MapperContext MapperContext => MapperData.MapperContext; + public int DataSourceIndex { get; set; } public bool StopFind { get; set; } @@ -59,7 +56,7 @@ public IDataSource GetFinalDataSource(IDataSource foundDataSource, IChildMemberM return new ComplexTypeMappingDataSource(foundDataSource, DataSourceIndex, mappingData); } - if (childTargetMember.IsEnumerable) + if (childTargetMember.IsEnumerable && foundDataSource.SourceMember.IsEnumerable) { return new EnumerableMappingDataSource(foundDataSource, DataSourceIndex, mappingData); } diff --git a/AgileMapper/DataSources/IDataSource.cs b/AgileMapper/DataSources/IDataSource.cs index f343b67ee..218c1249b 100644 --- a/AgileMapper/DataSources/IDataSource.cs +++ b/AgileMapper/DataSources/IDataSource.cs @@ -1,13 +1,13 @@ namespace AgileObjects.AgileMapper.DataSources { using System.Collections.Generic; - using Extensions.Internal; - using Members; #if NET35 using Microsoft.Scripting.Ast; #else using System.Linq.Expressions; #endif + using Extensions.Internal; + using Members; internal interface IDataSource : IConditionallyChainable { From 35cd77e6eea85c49b2be57d29a679cf5176a154d Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Sat, 29 Jun 2019 10:41:18 +0100 Subject: [PATCH 2/7] Tidying --- .../Inline/InlineMapperContextSet.cs | 4 ---- .../Configuration/ParametersSwapper.cs | 24 +++++++++++-------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/AgileMapper/Configuration/Inline/InlineMapperContextSet.cs b/AgileMapper/Configuration/Inline/InlineMapperContextSet.cs index b87004884..2b880d6a2 100644 --- a/AgileMapper/Configuration/Inline/InlineMapperContextSet.cs +++ b/AgileMapper/Configuration/Inline/InlineMapperContextSet.cs @@ -3,14 +3,10 @@ using System; using System.Collections; using System.Collections.Generic; -#if NET35 - using System.Linq; -#endif using System.Linq.Expressions; using Api.Configuration; using Api.Configuration.Projection; using Caching; - using Extensions; #if NET35 using Extensions.Internal; #endif diff --git a/AgileMapper/Configuration/ParametersSwapper.cs b/AgileMapper/Configuration/ParametersSwapper.cs index 465846248..45d657d4b 100644 --- a/AgileMapper/Configuration/ParametersSwapper.cs +++ b/AgileMapper/Configuration/ParametersSwapper.cs @@ -3,16 +3,16 @@ namespace AgileObjects.AgileMapper.Configuration using System; using System.Collections.Generic; using System.Linq; - using Extensions; - using Extensions.Internal; - using Members; - using NetStandardPolyfills; - using ObjectPopulation; #if NET35 using Microsoft.Scripting.Ast; #else using System.Linq.Expressions; #endif + using Extensions; + using Extensions.Internal; + using Members; + using NetStandardPolyfills; + using ObjectPopulation; using static Members.Member; internal class ParametersSwapper @@ -170,7 +170,7 @@ private static Expression ReplaceParameters( private static MappingContextInfo GetAppropriateMappingContext(SwapArgs swapArgs) { - if (swapArgs.ContextTypes[0].IsSimple()) + if (swapArgs.SourceType.IsSimple()) { return GetSimpleTypesMappingContextInfo(swapArgs); } @@ -298,8 +298,8 @@ public MappingContextInfo(SwapArgs swapArgs, Expression contextAccess) : this( swapArgs, contextAccess, - GetValueAccess(swapArgs.GetSourceAccess(contextAccess), swapArgs.ContextTypes[0]), - GetValueAccess(swapArgs.GetTargetAccess(contextAccess), swapArgs.ContextTypes[1])) + GetValueAccess(swapArgs.GetSourceAccess(contextAccess), swapArgs.SourceType), + GetValueAccess(swapArgs.GetTargetAccess(contextAccess), swapArgs.TargetType)) { } @@ -370,6 +370,10 @@ public SwapArgs( public Type[] ContextTypes { get; } + public Type SourceType => ContextTypes[0]; + + public Type TargetType => ContextTypes[1]; + public IMemberMapperData MapperData { get; } public Func TargetValueFactory { get; } @@ -386,10 +390,10 @@ public Expression GetTypedContextAccess(Expression contextAccess) => MapperData.GetTypedContextAccess(contextAccess, ContextTypes); public Expression GetSourceAccess(Expression contextAccess) - => MapperData.GetSourceAccess(contextAccess, ContextTypes[0]); + => MapperData.GetSourceAccess(contextAccess, SourceType); public Expression GetTargetAccess(Expression contextAccess) - => TargetValueFactory.Invoke(MapperData, contextAccess, ContextTypes[1]); + => TargetValueFactory.Invoke(MapperData, contextAccess, TargetType); } #endregion From 247140556578e5b14b2edf29f5c7028c55a61f45 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Sat, 29 Jun 2019 10:50:57 +0100 Subject: [PATCH 3/7] Removing Newtonsoft dependency --- .../AgileMapper.UnitTests.Net35.csproj | 595 +++++++++++++++++- .../AgileMapper.UnitTests.csproj | 5 +- .../WhenConfiguringDataSources.cs | 7 +- .../TestClasses/PublicDictionary.cs | 62 -- .../TestClasses/PublicEnumerable.cs | 34 + AgileMapper.UnitTests/packages.config | 1 - 6 files changed, 631 insertions(+), 73 deletions(-) delete mode 100644 AgileMapper.UnitTests/TestClasses/PublicDictionary.cs create mode 100644 AgileMapper.UnitTests/TestClasses/PublicEnumerable.cs diff --git a/AgileMapper.UnitTests.Net35/AgileMapper.UnitTests.Net35.csproj b/AgileMapper.UnitTests.Net35/AgileMapper.UnitTests.Net35.csproj index 70e14fb2b..c014778c5 100644 --- a/AgileMapper.UnitTests.Net35/AgileMapper.UnitTests.Net35.csproj +++ b/AgileMapper.UnitTests.Net35/AgileMapper.UnitTests.Net35.csproj @@ -66,8 +66,599 @@ - - %(RecursiveDir)%(Filename)%(Extension) + + Caching\WhenCachingWithHashCodes.cs + + + Configuration\AssemblyScanningTestClassBase.cs + + + Configuration\Inline\InlineMappingExtensions.cs + + + Configuration\Inline\WhenConfiguringCallbacksInline.cs + + + Configuration\Inline\WhenConfiguringConstructorDataSourcesInline.cs + + + Configuration\Inline\WhenConfiguringDataSourcesInline.cs + + + Configuration\Inline\WhenConfiguringDataSourcesInlineIncorrectly.cs + + + Configuration\Inline\WhenConfiguringDerivedTypesInline.cs + + + Configuration\Inline\WhenConfiguringEntityMappingInline.cs + + + Configuration\Inline\WhenConfiguringEnumMappingInline.cs + + + Configuration\Inline\WhenConfiguringNameMatchingInline.cs + + + Configuration\Inline\WhenConfiguringObjectCreationInline.cs + + + Configuration\Inline\WhenConfiguringObjectTrackingInline.cs + + + Configuration\Inline\WhenConfiguringStringFormattingInline.cs + + + Configuration\Inline\WhenConfiguringTypeIdentifiersInline.cs + + + Configuration\Inline\WhenIgnoringMembersInline.cs + + + Configuration\Inline\WhenIgnoringMembersInlineIncorrectly.cs + + + Configuration\Inline\WhenMappingToNullInline.cs + + + Configuration\Inline\WhenValidatingMappingsInline.cs + + + Configuration\Inline\WhenViewingMappingPlans.cs + + + Configuration\WhenApplyingMapperConfigurations.cs + + + Configuration\WhenApplyingMapperConfigurationsIncorrectly.cs + + + Configuration\WhenConfiguringConstructorDataSources.cs + + + Configuration\WhenConfiguringDataSources.cs + + + Configuration\WhenConfiguringDataSourcesIncorrectly.cs + + + Configuration\WhenConfiguringDerivedTypes.cs + + + Configuration\WhenConfiguringDerivedTypesIncorrectly.cs + + + Configuration\WhenConfiguringEntityMapping.cs + + + Configuration\WhenConfiguringEnumMapping.cs + + + Configuration\WhenConfiguringExceptionHandling.cs + + + Configuration\WhenConfiguringMappingCallbacks.cs + + + Configuration\WhenConfiguringNameMatching.cs + + + Configuration\WhenConfiguringObjectCreation.cs + + + Configuration\WhenConfiguringObjectCreationCallbacks.cs + + + Configuration\WhenConfiguringObjectTracking.cs + + + Configuration\WhenConfiguringObjectTrackingIncorrectly.cs + + + Configuration\WhenConfiguringReverseDataSources.cs + + + Configuration\WhenConfiguringReverseDataSourcesIncorrectly.cs + + + Configuration\WhenConfiguringStringFormatting.cs + + + Configuration\WhenConfiguringTypeIdentifiers.cs + + + Configuration\WhenIgnoringMembers.cs + + + Configuration\WhenIgnoringMembersByFilter.cs + + + Configuration\WhenIgnoringMembersByGlobalFilter.cs + + + Configuration\WhenIgnoringMembersIncorrectly.cs + + + Configuration\WhenMappingToNull.cs + + + Configuration\WhenResolvingServices.cs + + + Configuration\WhenViewingMappingPlans.cs + + + Dictionaries\Configuration\WhenConfiguringDictionaryMappingIncorrectly.cs + + + Dictionaries\Configuration\WhenConfiguringNestedDictionaryMapping.cs + + + Dictionaries\Configuration\WhenConfiguringSourceDictionaryMapping.cs + + + Dictionaries\Configuration\WhenConfiguringTargetDictionaryMapping.cs + + + Dictionaries\WhenCreatingRootDictionaryMembers.cs + + + Dictionaries\WhenFlatteningToDictionaries.cs + + + Dictionaries\WhenMappingFromDictionariesOnToComplexTypes.cs + + + Dictionaries\WhenMappingFromDictionariesOnToEnumerableMembers.cs + + + Dictionaries\WhenMappingFromDictionariesOverComplexTypes.cs + + + Dictionaries\WhenMappingFromDictionariesToNewComplexTypeMembers.cs + + + Dictionaries\WhenMappingFromDictionariesToNewComplexTypes.cs + + + Dictionaries\WhenMappingFromDictionariesToNewEnumerableMembers.cs + + + Dictionaries\WhenMappingFromDictionariesToNewEnumerables.cs + + + Dictionaries\WhenMappingFromDictionaryMembers.cs + + + Dictionaries\WhenMappingOnToDictionaries.cs + + + Dictionaries\WhenMappingOnToDictionaryMembers.cs + + + Dictionaries\WhenMappingOverDictionaries.cs + + + Dictionaries\WhenMappingOverDictionaryMembers.cs + + + Dictionaries\WhenMappingToNewDictionaries.cs + + + Dictionaries\WhenMappingToNewDictionaryMembers.cs + + + Dictionaries\WhenUnflatteningFromDictionaries.cs + + + Dictionaries\WhenViewingDictionaryMappingPlans.cs + + + Extensions\Internal\WhenEquatingExpressions.cs + + + Extensions\Internal\WhenGeneratingVariableNames.cs + + + Extensions\WhenFlatteningToQueryStringViaExtensionMethods.cs + + + Extensions\WhenFlatteningViaExtensionMethods.cs + + + Extensions\WhenMappingViaExtensionMethods.cs + + + Extensions\WhenUnflatteningFromQueryStringsViaExtensionMethods.cs + + + Extensions\WhenUnflatteningViaExtensionMethods.cs + + + MapperCloning\WhenCloningConstructorDataSources.cs + + + MapperCloning\WhenCloningDataSources.cs + + + MapperCloning\WhenCloningDictionarySettings.cs + + + MapperCloning\WhenCloningMemberIgnores.cs + + + MapperCloning\WhenCloningObjectFactories.cs + + + MapperCloning\WhenCloningStringFormatting.cs + + + Members\MemberTestsBase.cs + + + Members\WhenCreatingTargetMembersFromExpressions.cs + + + Members\WhenDeterminingATypeIdentifier.cs + + + Members\WhenDeterminingRecursion.cs + + + Members\WhenFindingDataSources.cs + + + Members\WhenFindingSourceMembers.cs + + + Members\WhenFindingTargetMembers.cs + + + Members\WhenMatchingSourceToTargetMembers.cs + + + Properties\AssemblyInfo.cs + + + Reflection\WhenAccessingTypeInformation.cs + + + SimpleTypeConversion\WhenConvertingToBools.cs + + + SimpleTypeConversion\WhenConvertingToBytes.cs + + + SimpleTypeConversion\WhenConvertingToCharacters.cs + + + SimpleTypeConversion\WhenConvertingToDateTimes.cs + + + SimpleTypeConversion\WhenConvertingToDecimals.cs + + + SimpleTypeConversion\WhenConvertingToDoubles.cs + + + SimpleTypeConversion\WhenConvertingToEnums.cs + + + SimpleTypeConversion\WhenConvertingToFlagsEnums.cs + + + SimpleTypeConversion\WhenConvertingToGuids.cs + + + SimpleTypeConversion\WhenConvertingToInts.cs + + + SimpleTypeConversion\WhenConvertingToLongs.cs + + + SimpleTypeConversion\WhenConvertingToShorts.cs + + + SimpleTypeConversion\WhenConvertingToStrings.cs + + + Structs\Configuration\WhenConfiguringStructCreationCallbacks.cs + + + Structs\Configuration\WhenConfiguringStructDataSources.cs + + + Structs\Configuration\WhenConfiguringStructMappingCallbacks.cs + + + Structs\Dictionaries\WhenMappingFromDictionariesToStructs.cs + + + Structs\WhenMappingOnToStructMembers.cs + + + Structs\WhenMappingOnToStructs.cs + + + Structs\WhenMappingOverStructMembers.cs + + + Structs\WhenMappingOverStructs.cs + + + Structs\WhenMappingToNewStructMembers.cs + + + Structs\WhenMappingToNewStructs.cs + + + Structs\WhenMappingToStructEnumerables.cs + + + Structs\WhenMappingToUnmappableStructMembers.cs + + + TestClasses\Address.cs + + + TestClasses\CategoryDto.cs + + + TestClasses\CategoryEntity.cs + + + TestClasses\Child.cs + + + TestClasses\Customer.cs + + + TestClasses\CustomerViewModel.cs + + + TestClasses\DtoBase.cs + + + TestClasses\Earthworm.cs + + + TestClasses\EntityBase.cs + + + TestClasses\FacebookUser.cs + + + TestClasses\InternalField.cs + + + TestClasses\IPublicInterface.cs + + + TestClasses\MegaProduct.cs + + + TestClasses\MysteryCustomer.cs + + + TestClasses\MysteryCustomerViewModel.cs + + + TestClasses\Order.cs + + + TestClasses\OrderDto.cs + + + TestClasses\OrderEntity.cs + + + TestClasses\OrderItem.cs + + + TestClasses\OrderItemDto.cs + + + TestClasses\OrderItemEntity.cs + + + TestClasses\OrderUk.cs + + + TestClasses\OrderUs.cs + + + TestClasses\Parent.cs + + + TestClasses\PaymentTypeUk.cs + + + TestClasses\PaymentTypeUs.cs + + + TestClasses\Person.cs + + + TestClasses\PersonViewModel.cs + + + TestClasses\Product.cs + + + TestClasses\ProductDto.cs + + + TestClasses\ProductDtoMega.cs + + + TestClasses\ProductEntity.cs + + + TestClasses\PublicCtor.cs + + + TestClasses\PublicCtorStruct.cs + + + TestClasses\PublicEnumerable.cs + + + TestClasses\PublicField.cs + + + TestClasses\PublicGetMethod.cs + + + TestClasses\PublicImplementation.cs + + + TestClasses\PublicIndex.cs + + + TestClasses\PublicProperty.cs + + + TestClasses\PublicPropertyStruct.cs + + + TestClasses\PublicReadOnlyField.cs + + + TestClasses\PublicReadOnlyProperty.cs + + + TestClasses\PublicSealed.cs + + + TestClasses\PublicSetMethod.cs + + + TestClasses\PublicTwoFields.cs + + + TestClasses\PublicTwoFieldsStruct.cs + + + TestClasses\PublicTwoParamCtor.cs + + + TestClasses\PublicUnconstructable.cs + + + TestClasses\PublicWriteOnlyProperty.cs + + + TestClasses\SaveOrderItemRequest.cs + + + TestClasses\SaveOrderRequest.cs + + + TestClasses\Status.cs + + + TestClasses\StringKeyedDictionary.cs + + + TestClasses\Title.cs + + + TestClasses\TitleShortlist.cs + + + TestClasses\Wedding.cs + + + TestClasses\WeddingDto.cs + + + WhenAnalysingCollections.cs + + + WhenMappingCircularReferences.cs + + + WhenMappingDerivedTypes.cs + + + WhenMappingEntities.cs + + + WhenMappingOnToComplexTypeMembers.cs + + + WhenMappingOnToComplexTypes.cs + + + WhenMappingOnToEnumerableMembers.cs + + + WhenMappingOnToEnumerables.cs + + + WhenMappingOverComplexTypeMembers.cs + + + WhenMappingOverComplexTypes.cs + + + WhenMappingOverEnumerableMembers.cs + + + WhenMappingOverEnumerables.cs + + + WhenMappingToConstructors.cs + + + WhenMappingToMetaMembers.cs + + + WhenMappingToNewComplexTypeMembers.cs + + + WhenMappingToNewComplexTypes.cs + + + WhenMappingToNewEnumerableMembers.cs + + + WhenMappingToNewEnumerables.cs + + + WhenUnflatteningFromQueryStrings.cs + + + WhenUsingFactoryMethods.cs + + + WhenValidatingMappings.cs + + + WhenViewingMappingPlans.cs + + + WhenWorkingWithQueryStrings.cs diff --git a/AgileMapper.UnitTests/AgileMapper.UnitTests.csproj b/AgileMapper.UnitTests/AgileMapper.UnitTests.csproj index ff9527024..39082fdd9 100644 --- a/AgileMapper.UnitTests/AgileMapper.UnitTests.csproj +++ b/AgileMapper.UnitTests/AgileMapper.UnitTests.csproj @@ -50,9 +50,6 @@ ..\packages\Microsoft.Extensions.Primitives.2.0.0\lib\netstandard2.0\Microsoft.Extensions.Primitives.dll - - ..\packages\Newtonsoft.Json.12.0.2\lib\net45\Newtonsoft.Json.dll - @@ -223,7 +220,7 @@ - + diff --git a/AgileMapper.UnitTests/Configuration/WhenConfiguringDataSources.cs b/AgileMapper.UnitTests/Configuration/WhenConfiguringDataSources.cs index 3057db1c0..44aea029c 100644 --- a/AgileMapper.UnitTests/Configuration/WhenConfiguringDataSources.cs +++ b/AgileMapper.UnitTests/Configuration/WhenConfiguringDataSources.cs @@ -8,7 +8,6 @@ using AgileMapper.Extensions; using AgileMapper.Members; using Common; - using Newtonsoft.Json.Linq; using TestClasses; #if !NET35 using Xunit; @@ -1626,10 +1625,10 @@ public void ShouldMapToTargetUsingASimpleToComplexTypeFactoryMethod() using (var mapper = Mapper.CreateNew()) { mapper.WhenMapping - .From().To() - .Map(ctx => JToken.Parse(ctx.Source)).ToTarget(); + .From().To>() + .Map(ctx => PublicEnumerable.Parse(ctx.Source)).ToTarget(); - mapper.GetPlanFor>().ToANew>(); + mapper.GetPlanFor>().ToANew>>(); } } diff --git a/AgileMapper.UnitTests/TestClasses/PublicDictionary.cs b/AgileMapper.UnitTests/TestClasses/PublicDictionary.cs deleted file mode 100644 index cfd65c354..000000000 --- a/AgileMapper.UnitTests/TestClasses/PublicDictionary.cs +++ /dev/null @@ -1,62 +0,0 @@ -namespace AgileObjects.AgileMapper.UnitTests.TestClasses -{ - using System.Collections; - using System.Collections.Generic; - - internal class PublicDictionary : IDictionary - { - private readonly Dictionary _data; - - public PublicDictionary() - { - _data = new Dictionary(); - } - - public static PublicDictionary Deserialize(string data) - => new PublicDictionary(); - - private ICollection> DataAsCollection => _data; - - public object this[string key] - { - get => _data[key]; - set => _data[key] = value; - } - - public ICollection Keys => _data.Keys; - - public ICollection Values => _data.Values; - - public int Count => _data.Count; - - public bool IsReadOnly => false; - - public void Add(string key, object value) => _data.Add(key, value); - - public void Add(KeyValuePair item) => DataAsCollection.Add(item); - - public void Clear() => _data.Clear(); - - public bool Contains(KeyValuePair item) - => DataAsCollection.Contains(item); - - public bool ContainsKey(string key) => _data.ContainsKey(key); - - public void CopyTo(KeyValuePair[] array, int arrayIndex) - => DataAsCollection.CopyTo(array, arrayIndex); - - public IEnumerator> GetEnumerator() - => DataAsCollection.GetEnumerator(); - - public bool Remove(string key) => _data.Remove(key); - - public bool Remove(KeyValuePair item) - => DataAsCollection.Remove(item); - - public bool TryGetValue(string key, out object value) - => _data.TryGetValue(key, out value); - - IEnumerator IEnumerable.GetEnumerator() - => DataAsCollection.GetEnumerator(); - } -} \ No newline at end of file diff --git a/AgileMapper.UnitTests/TestClasses/PublicEnumerable.cs b/AgileMapper.UnitTests/TestClasses/PublicEnumerable.cs new file mode 100644 index 000000000..f8d385787 --- /dev/null +++ b/AgileMapper.UnitTests/TestClasses/PublicEnumerable.cs @@ -0,0 +1,34 @@ +namespace AgileObjects.AgileMapper.UnitTests.TestClasses +{ + using System; + using System.Collections; + using System.Collections.Generic; + using System.Linq; + + internal class PublicEnumerable : IEnumerable + { + private readonly List _items; + + public PublicEnumerable() + : this(new List()) + { + } + + private PublicEnumerable(List items) + { + _items = items; + } + + public static PublicEnumerable Parse(string values) + { + return new PublicEnumerable(values + .Split(',') + .Select(v => (T)Convert.ChangeType(v, typeof(T))) + .ToList()); + } + + public IEnumerator GetEnumerator() => _items.GetEnumerator(); + + IEnumerator IEnumerable.GetEnumerator() => _items.GetEnumerator(); + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests/packages.config b/AgileMapper.UnitTests/packages.config index 0ba304c53..09b6deb71 100644 --- a/AgileMapper.UnitTests/packages.config +++ b/AgileMapper.UnitTests/packages.config @@ -3,7 +3,6 @@ - From 0664f2d08fc095e85f4d01b2f9f57198a54bda63 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Sat, 29 Jun 2019 15:53:42 +0100 Subject: [PATCH 4/7] Optimising deep clone member-matching --- .../WhenConfiguringDataSources.cs | 43 ++++++++++++++----- AgileMapper/Members/SourceMemberMatcher.cs | 16 +++++-- 2 files changed, 45 insertions(+), 14 deletions(-) diff --git a/AgileMapper.UnitTests/Configuration/WhenConfiguringDataSources.cs b/AgileMapper.UnitTests/Configuration/WhenConfiguringDataSources.cs index 44aea029c..e9e20dc12 100644 --- a/AgileMapper.UnitTests/Configuration/WhenConfiguringDataSources.cs +++ b/AgileMapper.UnitTests/Configuration/WhenConfiguringDataSources.cs @@ -177,7 +177,7 @@ public void ShouldAllowConditionTypeTestsWhenMappingFromAnInterface() // See https://github.com/agileobjects/AgileMapper/issues/111 [Fact] - public void ShouldConditionallyApplyAToTargetConfiguredSimpleTypeConstant() + public void ShouldConditionallyApplyAToTargetSimpleTypeConstant() { using (var mapper = Mapper.CreateNew()) { @@ -195,7 +195,7 @@ public void ShouldConditionallyApplyAToTargetConfiguredSimpleTypeConstant() } [Fact] - public void ShouldConditionallyApplyAToTargetConfiguredSimpleType() + public void ShouldConditionallyApplyAToTargetSimpleType() { using (var mapper = Mapper.CreateNew()) { @@ -212,7 +212,7 @@ public void ShouldConditionallyApplyAToTargetConfiguredSimpleType() } [Fact] - public void ShouldConditionallyApplyAToTargetConfiguredNestedSimpleTypeExpression() + public void ShouldConditionallyApplyAToTargetNestedSimpleTypeExpression() { using (var mapper = Mapper.CreateNew()) { @@ -236,7 +236,7 @@ public void ShouldConditionallyApplyAToTargetConfiguredNestedSimpleTypeExpressio } [Fact] - public void ShouldConditionallyApplyAToTargetConfiguredSimpleTypeExpressionInAComplexTypeList() + public void ShouldConditionallyApplyAToTargetSimpleTypeExpressionToAComplexTypeListMember() { using (var mapper = Mapper.CreateNew()) { @@ -1213,7 +1213,7 @@ public void ShouldApplyAConfiguredSourceInterfaceMember() // See https://github.com/agileobjects/AgileMapper/issues/64 [Fact] - public void ShouldApplyAConfiguredRootSource() + public void ShouldApplyAConfiguredToTargetDataSource() { using (var mapper = Mapper.CreateNew()) { @@ -1235,7 +1235,7 @@ public void ShouldApplyAConfiguredRootSource() } [Fact] - public void ShouldApplyANestedOverwriteConfiguredRootSource() + public void ShouldApplyANestedOverwriteConfiguredToTargetDataSource() { using (var mapper = Mapper.CreateNew()) { @@ -1395,7 +1395,7 @@ public void ShouldApplyAConfiguredRootSourceToANestedMember() } [Fact] - public void ShouldApplyAConfiguredRootSourceToAnEnumerableElement() + public void ShouldApplyAToTargetComplexTypeToAComplexTypeEnumerableElement() { using (var mapper = Mapper.CreateNew()) { @@ -1426,7 +1426,7 @@ public void ShouldApplyAConfiguredRootSourceToAnEnumerableElement() } [Fact] - public void ShouldApplyAConfiguredEnumerableRootSource() + public void ShouldApplyAToTargetComplexTypeEnumerable() { using (var mapper = Mapper.CreateNew()) { @@ -1463,7 +1463,7 @@ public void ShouldApplyAConfiguredEnumerableRootSource() } [Fact] - public void ShouldApplyMultipleConfiguredComplexTypeRootSources() + public void ShouldApplyMultipleToTargetComplexTypes() { using (var mapper = Mapper.CreateNew()) { @@ -1490,7 +1490,7 @@ public void ShouldApplyMultipleConfiguredComplexTypeRootSources() } [Fact] - public void ShouldApplyMultipleConfiguredEnumerableRootSources() + public void ShouldApplyMultipleToTargetSimpleTypeEnumerables() { using (var mapper = Mapper.CreateNew()) { @@ -1620,7 +1620,7 @@ public void ShouldHandleDeepNestedRuntimeTypedMembersWithACachedMappingPlan() } [Fact] - public void ShouldMapToTargetUsingASimpleToComplexTypeFactoryMethod() + public void ShouldApplyAToTargetSimpleTypeToANestedComplexTypeMember() { using (var mapper = Mapper.CreateNew()) { @@ -1629,6 +1629,27 @@ public void ShouldMapToTargetUsingASimpleToComplexTypeFactoryMethod() .Map(ctx => PublicEnumerable.Parse(ctx.Source)).ToTarget(); mapper.GetPlanFor>().ToANew>>(); + + var source = new PublicField { Value = "1,2,3" }; + var result = mapper.Map(source).ToANew>>(); + + result.ShouldNotBeNull(); + result.Value.ShouldNotBeNull(); + result.Value.ShouldBe(1, 2, 3); + } + } + + [Fact] + public void ShouldConditionallyApplyAToTargetSimpleTypeToANestedComplexTypeMember() + { + using (var mapper = Mapper.CreateNew()) + { + mapper.WhenMapping + .From().To>() + .If(cxt => cxt.Source.Contains(',')) + .Map(ctx => PublicEnumerable.Parse(ctx.Source)).ToTarget(); + + mapper.GetPlanFor>().ToANew>>(); } } diff --git a/AgileMapper/Members/SourceMemberMatcher.cs b/AgileMapper/Members/SourceMemberMatcher.cs index fa5e0a1d2..991bf7191 100644 --- a/AgileMapper/Members/SourceMemberMatcher.cs +++ b/AgileMapper/Members/SourceMemberMatcher.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Linq; using Extensions; + using NetStandardPolyfills; internal static class SourceMemberMatcher { @@ -81,13 +82,19 @@ private static bool ExactMatchingSourceMemberExists( IChildMemberMappingData targetData, out IQualifiedMember matchingMember) { + var mapperData = targetData.MapperData; + + var matcher = mapperData.TargetType.IsAssignableTo(mapperData.SourceType) + ? (Func)MembersAreTheSame + : MembersMatch; + var sourceMember = QuerySourceMembers( parentSourceMember, targetData, - MembersMatch) + matcher) .FirstOrDefault(); - if ((sourceMember == null) || !TypesAreCompatible(sourceMember.Type, targetData.MapperData)) + if ((sourceMember == null) || !TypesAreCompatible(sourceMember.Type, mapperData)) { matchingMember = null; return false; @@ -99,7 +106,7 @@ private static bool ExactMatchingSourceMemberExists( private static bool MembersMatch(IChildMemberMappingData mappingData, Member sourceMember) { - if (mappingData.MapperData.TargetMember.LeafMember.Equals(sourceMember)) + if (MembersAreTheSame(mappingData, sourceMember)) { return true; } @@ -111,6 +118,9 @@ private static bool MembersMatch(IChildMemberMappingData mappingData, Member sou .Matches(mappingData.MapperData.TargetMember); } + private static bool MembersAreTheSame(IChildMemberMappingData mappingData, Member sourceMember) + => mappingData.MapperData.TargetMember.LeafMember.Equals(sourceMember); + private static IEnumerable QuerySourceMembers( IQualifiedMember parentMember, IChildMemberMappingData mappingData, From 493ae2dccb9a2986e7a1b95aa03887bb7387dcda Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Mon, 1 Jul 2019 21:08:41 +0100 Subject: [PATCH 5/7] Support for simple type to enumerable ToTarget data sources --- .../Members/WhenFindingDataSources.cs | 2 +- .../CustomDataSourceTargetMemberSpecifier.cs | 6 +- AgileMapper/DataSources/AdHocDataSource.cs | 2 +- .../Finders/MetaMemberDataSourceFinder.cs | 7 +- .../Finders/SourceMemberDataSourceFinder.cs | 35 ++-- .../DataSources/SourceMemberDataSource.cs | 5 +- AgileMapper/Members/SourceMemberMatch.cs | 38 ++++ AgileMapper/Members/SourceMemberMatcher.cs | 166 +++++++++++------- 8 files changed, 170 insertions(+), 91 deletions(-) create mode 100644 AgileMapper/Members/SourceMemberMatch.cs diff --git a/AgileMapper.UnitTests/Members/WhenFindingDataSources.cs b/AgileMapper.UnitTests/Members/WhenFindingDataSources.cs index 0057b8f72..c2f20b8d1 100644 --- a/AgileMapper.UnitTests/Members/WhenFindingDataSources.cs +++ b/AgileMapper.UnitTests/Members/WhenFindingDataSources.cs @@ -53,7 +53,7 @@ private IQualifiedMember GetMatchingSourceMember( var childMapperData = new ChildMemberMapperData(targetMember, rootMapperData); var childMappingContext = rootMappingData.GetChildMappingData(childMapperData); - return SourceMemberMatcher.GetMatchFor(childMappingContext, out _); + return SourceMemberMatcher.GetMatchFor(childMappingContext).SourceMember; } #region Helper Classes diff --git a/AgileMapper/Api/Configuration/CustomDataSourceTargetMemberSpecifier.cs b/AgileMapper/Api/Configuration/CustomDataSourceTargetMemberSpecifier.cs index aba1e77f6..4eb2ed3b5 100644 --- a/AgileMapper/Api/Configuration/CustomDataSourceTargetMemberSpecifier.cs +++ b/AgileMapper/Api/Configuration/CustomDataSourceTargetMemberSpecifier.cs @@ -116,16 +116,16 @@ private void ThrowIfRedundantSourceMember(ConfiguredLambdaInfo valueLambdaInfo, var targetMemberMapperData = new ChildMemberMapperData(targetMember, mappingData.MapperData); var targetMemberMappingData = mappingData.GetChildMappingData(targetMemberMapperData); - var bestMatchingSourceMember = SourceMemberMatcher.GetMatchFor(targetMemberMappingData, out _); + var bestSourceMemberMatch = SourceMemberMatcher.GetMatchFor(targetMemberMappingData); - if (bestMatchingSourceMember == null) + if (!bestSourceMemberMatch.IsUseable) { return; } var sourceMember = sourceMemberLambda.ToSourceMember(MapperContext); - if (!bestMatchingSourceMember.Matches(sourceMember)) + if (!bestSourceMemberMatch.SourceMember.Matches(sourceMember)) { return; } diff --git a/AgileMapper/DataSources/AdHocDataSource.cs b/AgileMapper/DataSources/AdHocDataSource.cs index ecdcd95ef..65089290f 100644 --- a/AgileMapper/DataSources/AdHocDataSource.cs +++ b/AgileMapper/DataSources/AdHocDataSource.cs @@ -1,12 +1,12 @@ namespace AgileObjects.AgileMapper.DataSources { using System.Collections.Generic; - using Members; #if NET35 using Microsoft.Scripting.Ast; #else using System.Linq.Expressions; #endif + using Members; internal class AdHocDataSource : DataSourceBase { diff --git a/AgileMapper/DataSources/Finders/MetaMemberDataSourceFinder.cs b/AgileMapper/DataSources/Finders/MetaMemberDataSourceFinder.cs index e581cf8d3..8d0421445 100644 --- a/AgileMapper/DataSources/Finders/MetaMemberDataSourceFinder.cs +++ b/AgileMapper/DataSources/Finders/MetaMemberDataSourceFinder.cs @@ -214,16 +214,17 @@ private static bool TryGetMetaMember( var memberMappingData = currentMappingData.GetChildMappingData(childMemberMapperData); - currentSourceMember = SourceMemberMatcher.GetMatchFor( + var currentSourceMemberMatch = SourceMemberMatcher.GetMatchFor( memberMappingData, - out _, searchParentContexts: false); - if (currentSourceMember == null) + if (!currentSourceMemberMatch.IsUseable) { return false; } + currentSourceMember = currentSourceMemberMatch.SourceMember; + currentMemberPart = new SourceMemberMetaMemberPart( currentSourceMember, currentMapperData, diff --git a/AgileMapper/DataSources/Finders/SourceMemberDataSourceFinder.cs b/AgileMapper/DataSources/Finders/SourceMemberDataSourceFinder.cs index 18aec53a3..fb2091e0d 100644 --- a/AgileMapper/DataSources/Finders/SourceMemberDataSourceFinder.cs +++ b/AgileMapper/DataSources/Finders/SourceMemberDataSourceFinder.cs @@ -1,8 +1,8 @@ namespace AgileObjects.AgileMapper.DataSources.Finders { + using System.Collections.Generic; using Extensions.Internal; using Members; - using System.Collections.Generic; internal struct SourceMemberDataSourceFinder : IDataSourceFinder { @@ -13,11 +13,11 @@ public IEnumerable FindFor(DataSourceFindContext context) yield break; } - var matchingSourceMemberDataSource = GetSourceMemberDataSourceOrNull(context); + var matchingSourceMemberDataSource = GetSourceMemberDataSource(context); var configuredDataSources = context.ConfiguredDataSources; var targetMember = context.MapperData.TargetMember; - if ((matchingSourceMemberDataSource == null) || + if (!matchingSourceMemberDataSource.IsValid || configuredDataSources.Any(cds => cds.IsSameAs(matchingSourceMemberDataSource))) { if (context.DataSourceIndex == 0) @@ -32,7 +32,10 @@ public IEnumerable FindFor(DataSourceFindContext context) yield return context.GetFallbackDataSource(); } - yield break; + if (matchingSourceMemberDataSource.SourceMember == null) + { + yield break; + } } if (matchingSourceMemberDataSource.SourceMember.IsSimple && @@ -55,6 +58,11 @@ public IEnumerable FindFor(DataSourceFindContext context) } } + if (!matchingSourceMemberDataSource.IsValid) + { + yield break; + } + yield return matchingSourceMemberDataSource; if (!targetMember.IsReadOnly && @@ -65,21 +73,20 @@ public IEnumerable FindFor(DataSourceFindContext context) } } - private static IDataSource GetSourceMemberDataSourceOrNull(DataSourceFindContext context) + private static IDataSource GetSourceMemberDataSource(DataSourceFindContext context) { - var bestMatchingSourceMember = SourceMemberMatcher.GetMatchFor( - context.ChildMappingData, - out var contextMappingData); + var bestSourceMemberMatch = SourceMemberMatcher.GetMatchFor(context.ChildMappingData); - if (bestMatchingSourceMember == null) + if (bestSourceMemberMatch.IsUseable) { - return null; + return context.GetFinalDataSource( + SourceMemberDataSource.For(bestSourceMemberMatch), + bestSourceMemberMatch.ContextMappingData); } - var sourceMemberDataSource = SourceMemberDataSource - .For(bestMatchingSourceMember, contextMappingData.MapperData); - - return context.GetFinalDataSource(sourceMemberDataSource, contextMappingData); + return new AdHocDataSource( + bestSourceMemberMatch.SourceMember, + Constants.EmptyExpression); } private static bool UseFallbackComplexTypeMappingDataSource(QualifiedMember targetMember) diff --git a/AgileMapper/DataSources/SourceMemberDataSource.cs b/AgileMapper/DataSources/SourceMemberDataSource.cs index 823e734ad..61b2e62fb 100644 --- a/AgileMapper/DataSources/SourceMemberDataSource.cs +++ b/AgileMapper/DataSources/SourceMemberDataSource.cs @@ -64,9 +64,10 @@ private static Expression GetRuntimeTypeCheck(UnaryExpression cast, IMemberMappe return memberHasRuntimeType; } - public static SourceMemberDataSource For(IQualifiedMember sourceMember, IMemberMapperData mapperData) + public static SourceMemberDataSource For(SourceMemberMatch sourceMemberMatch) { - sourceMember = sourceMember.RelativeTo(mapperData.SourceMember); + var mapperData = sourceMemberMatch.ContextMappingData.MapperData; + var sourceMember = sourceMemberMatch.SourceMember.RelativeTo(mapperData.SourceMember); var sourceMemberValue = sourceMember .GetQualifiedAccess(mapperData) diff --git a/AgileMapper/Members/SourceMemberMatch.cs b/AgileMapper/Members/SourceMemberMatch.cs new file mode 100644 index 000000000..12ca94da0 --- /dev/null +++ b/AgileMapper/Members/SourceMemberMatch.cs @@ -0,0 +1,38 @@ +namespace AgileObjects.AgileMapper.Members +{ + internal class SourceMemberMatch + { + public static readonly SourceMemberMatch Null = new SourceMemberMatch(); + + private SourceMemberMatch() + { + } + + public SourceMemberMatch( + IQualifiedMember sourceMember, + IChildMemberMappingData contextMappingData, + bool isUseable = true) + { + SourceMember = GetFinalSourceMember(sourceMember, contextMappingData); + ContextMappingData = contextMappingData; + IsUseable = isUseable; + } + + private static IQualifiedMember GetFinalSourceMember( + IQualifiedMember sourceMember, + IChildMemberMappingData targetData) + { + return targetData + .MapperData + .MapperContext + .QualifiedMemberFactory + .GetFinalSourceMember(sourceMember, targetData.MapperData.TargetMember); + } + + public IQualifiedMember SourceMember { get; } + + public IChildMemberMappingData ContextMappingData { get; } + + public bool IsUseable { get; } + } +} \ No newline at end of file diff --git a/AgileMapper/Members/SourceMemberMatcher.cs b/AgileMapper/Members/SourceMemberMatcher.cs index 991bf7191..617953892 100644 --- a/AgileMapper/Members/SourceMemberMatcher.cs +++ b/AgileMapper/Members/SourceMemberMatcher.cs @@ -8,76 +8,44 @@ internal static class SourceMemberMatcher { - public static IQualifiedMember GetMatchFor( - IChildMemberMappingData targetData, - out IChildMemberMappingData contextMappingData, + public static SourceMemberMatch GetMatchFor( + IChildMemberMappingData targetMappingData, bool searchParentContexts = true) { - var parentSourceMember = targetData.MapperData.SourceMember; + var parentSourceMember = targetMappingData.MapperData.SourceMember; if (parentSourceMember.IsSimple) { - contextMappingData = null; - return null; + return SourceMemberMatch.Null; } - if (ExactMatchingSourceMemberExists(parentSourceMember, targetData, out var matchingMember)) + if (ExactMatchingMemberExists(parentSourceMember, targetMappingData, out var matchingMember) && + TypesAreCompatible(matchingMember.Type, targetMappingData.MapperData)) { - contextMappingData = targetData; - return GetFinalSourceMember(matchingMember, targetData); + return new SourceMemberMatch(matchingMember, targetMappingData); } - matchingMember = EnumerateSourceMembers(parentSourceMember, targetData) - .FirstOrDefault(sm => IsMatchingMember(sm, targetData.MapperData)); - - if (matchingMember != null) + if (TryFindSourceMemberMatch( + targetMappingData, + parentSourceMember, + ref matchingMember, + out var sourceMemberMatch)) { - contextMappingData = targetData; - return GetFinalSourceMember(matchingMember, targetData); + return sourceMemberMatch; } - if (searchParentContexts) + if (searchParentContexts && + TryFindParentContextSourceMemberMatch(targetMappingData, ref matchingMember, out sourceMemberMatch)) { - return GetParentContextMatchOrNull(targetData, out contextMappingData); + return sourceMemberMatch; } - contextMappingData = null; - return null; + return (matchingMember != null) + ? new SourceMemberMatch(matchingMember, targetMappingData, isUseable: false) + : SourceMemberMatch.Null; } - private static IQualifiedMember GetParentContextMatchOrNull( - IChildMemberMappingData targetData, - out IChildMemberMappingData contextMappingData) - { - var mappingData = targetData.Parent; - - while (mappingData.Parent != null) - { - if (mappingData.MapperData.IsEntryPoint || - mappingData.MapperData.TargetMemberIsEnumerableElement()) - { - break; - } - - mappingData = mappingData.Parent; - - var childMapperData = new ChildMemberMapperData(targetData.MapperData.TargetMember, mappingData.MapperData); - contextMappingData = mappingData.GetChildMappingData(childMapperData); - - var matchingMember = EnumerateSourceMembers(mappingData.MapperData.SourceMember, contextMappingData) - .FirstOrDefault(sm => IsMatchingMember(sm, targetData.MapperData)); - - if (matchingMember != null) - { - return GetFinalSourceMember(matchingMember, targetData); - } - } - - contextMappingData = null; - return null; - } - - private static bool ExactMatchingSourceMemberExists( + private static bool ExactMatchingMemberExists( IQualifiedMember parentSourceMember, IChildMemberMappingData targetData, out IQualifiedMember matchingMember) @@ -94,7 +62,7 @@ private static bool ExactMatchingSourceMemberExists( matcher) .FirstOrDefault(); - if ((sourceMember == null) || !TypesAreCompatible(sourceMember.Type, mapperData)) + if (sourceMember == null) { matchingMember = null; return false; @@ -104,6 +72,9 @@ private static bool ExactMatchingSourceMemberExists( return true; } + private static bool MembersAreTheSame(IChildMemberMappingData mappingData, Member sourceMember) + => mappingData.MapperData.TargetMember.LeafMember.Equals(sourceMember); + private static bool MembersMatch(IChildMemberMappingData mappingData, Member sourceMember) { if (MembersAreTheSame(mappingData, sourceMember)) @@ -118,9 +89,6 @@ private static bool MembersMatch(IChildMemberMappingData mappingData, Member sou .Matches(mappingData.MapperData.TargetMember); } - private static bool MembersAreTheSame(IChildMemberMappingData mappingData, Member sourceMember) - => mappingData.MapperData.TargetMember.LeafMember.Equals(sourceMember); - private static IEnumerable QuerySourceMembers( IQualifiedMember parentMember, IChildMemberMappingData mappingData, @@ -137,15 +105,82 @@ private static IEnumerable QuerySourceMembers( : members.Filter(m => m.MemberType != MemberType.GetMethod); } - private static IQualifiedMember GetFinalSourceMember( - IQualifiedMember sourceMember, - IChildMemberMappingData targetData) + private static bool TryFindParentContextSourceMemberMatch( + IChildMemberMappingData targetMappingData, + ref IQualifiedMember matchingMember, + out SourceMemberMatch sourceMemberMatch) { - return targetData - .MapperData - .MapperContext - .QualifiedMemberFactory - .GetFinalSourceMember(sourceMember, targetData.MapperData.TargetMember); + var targetMember = targetMappingData.MapperData.TargetMember; + var mappingData = targetMappingData.Parent; + + while (mappingData.Parent != null) + { + if (mappingData.MapperData.IsEntryPoint || + mappingData.MapperData.TargetMemberIsEnumerableElement()) + { + break; + } + + mappingData = mappingData.Parent; + + var childMapperData = new ChildMemberMapperData(targetMember, mappingData.MapperData); + var contextMappingData = mappingData.GetChildMappingData(childMapperData); + + if (TryFindSourceMemberMatch( + targetMappingData, + mappingData.MapperData.SourceMember, + contextMappingData, + ref matchingMember, + out sourceMemberMatch)) + { + return true; + } + } + + sourceMemberMatch = null; + return false; + } + + private static bool TryFindSourceMemberMatch( + IChildMemberMappingData targetMappingData, + IQualifiedMember parentSourceMember, + ref IQualifiedMember matchingMember, + out SourceMemberMatch sourceMemberMatch) + { + return TryFindSourceMemberMatch( + targetMappingData, + parentSourceMember, + targetMappingData, + ref matchingMember, + out sourceMemberMatch); + } + + private static bool TryFindSourceMemberMatch( + IChildMemberMappingData targetMappingData, + IQualifiedMember parentSourceMember, + IChildMemberMappingData contextMappingData, + ref IQualifiedMember matchingMember, + out SourceMemberMatch sourceMemberMatch) + { + var candidateSourceMembers = EnumerateSourceMembers(parentSourceMember, contextMappingData) + .Where(targetMappingData.MapperData.TargetMember.Matches); + + foreach (var sourceMember in candidateSourceMembers) + { + if (TypesAreCompatible(sourceMember.Type, targetMappingData.MapperData)) + { + sourceMemberMatch = new SourceMemberMatch(sourceMember, contextMappingData); + return true; + } + + if (matchingMember == null) + { + matchingMember = sourceMember; + } + } + + sourceMemberMatch = null; + return false; } private static IEnumerable EnumerateSourceMembers( @@ -211,9 +246,6 @@ private static bool MembersHaveCompatibleTypes(IChildMemberMappingData rootData, return targetMember.Type == typeof(object); } - private static bool IsMatchingMember(IQualifiedMember sourceMember, IMemberMapperData mapperData) - => mapperData.TargetMember.Matches(sourceMember) && TypesAreCompatible(sourceMember.Type, mapperData); - private static bool TypesAreCompatible(Type sourceType, IMemberMapperData mapperData) => mapperData.CanConvert(sourceType, mapperData.TargetMember.Type); } From b41d31b3db98dc68c36095f87066f000b47bd680 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Tue, 2 Jul 2019 09:15:46 +0100 Subject: [PATCH 6/7] Tidying --- AgileMapper/ObjectPopulation/ObjectMapperData.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/AgileMapper/ObjectPopulation/ObjectMapperData.cs b/AgileMapper/ObjectPopulation/ObjectMapperData.cs index b046f0954..fc755e267 100644 --- a/AgileMapper/ObjectPopulation/ObjectMapperData.cs +++ b/AgileMapper/ObjectPopulation/ObjectMapperData.cs @@ -5,6 +5,11 @@ namespace AgileObjects.AgileMapper.ObjectPopulation using System.Globalization; using System.Linq; using System.Reflection; +#if NET35 + using Microsoft.Scripting.Ast; +#else + using System.Linq.Expressions; +#endif using DataSources; using Enumerables; using Extensions; @@ -14,11 +19,6 @@ namespace AgileObjects.AgileMapper.ObjectPopulation using Members.Sources; using NetStandardPolyfills; using ReadableExpressions.Extensions; -#if NET35 - using Microsoft.Scripting.Ast; -#else - using System.Linq.Expressions; -#endif using static Members.Member; internal class ObjectMapperData : BasicMapperData, IMemberMapperData From 9edbab750251a30c466631e1557eb2359310bab1 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Tue, 2 Jul 2019 12:36:49 +0100 Subject: [PATCH 7/7] Fixing tests --- .../Finders/SourceMemberDataSourceFinder.cs | 18 +++++----- .../DataSources/SourceMemberDataSource.cs | 16 +-------- AgileMapper/Members/SourceMemberMatch.cs | 34 ++++++++++++++++--- 3 files changed, 38 insertions(+), 30 deletions(-) diff --git a/AgileMapper/DataSources/Finders/SourceMemberDataSourceFinder.cs b/AgileMapper/DataSources/Finders/SourceMemberDataSourceFinder.cs index fb2091e0d..0ec0b02da 100644 --- a/AgileMapper/DataSources/Finders/SourceMemberDataSourceFinder.cs +++ b/AgileMapper/DataSources/Finders/SourceMemberDataSourceFinder.cs @@ -13,11 +13,11 @@ public IEnumerable FindFor(DataSourceFindContext context) yield break; } - var matchingSourceMemberDataSource = GetSourceMemberDataSource(context); + var matchingSourceMemberDataSource = GetSourceMemberDataSource(context, out var hasUseableSourceMember); var configuredDataSources = context.ConfiguredDataSources; var targetMember = context.MapperData.TargetMember; - if (!matchingSourceMemberDataSource.IsValid || + if (!hasUseableSourceMember || configuredDataSources.Any(cds => cds.IsSameAs(matchingSourceMemberDataSource))) { if (context.DataSourceIndex == 0) @@ -58,11 +58,6 @@ public IEnumerable FindFor(DataSourceFindContext context) } } - if (!matchingSourceMemberDataSource.IsValid) - { - yield break; - } - yield return matchingSourceMemberDataSource; if (!targetMember.IsReadOnly && @@ -73,14 +68,17 @@ public IEnumerable FindFor(DataSourceFindContext context) } } - private static IDataSource GetSourceMemberDataSource(DataSourceFindContext context) + private static IDataSource GetSourceMemberDataSource( + DataSourceFindContext context, + out bool hasUseableSourceMember) { var bestSourceMemberMatch = SourceMemberMatcher.GetMatchFor(context.ChildMappingData); + hasUseableSourceMember = bestSourceMemberMatch.IsUseable; - if (bestSourceMemberMatch.IsUseable) + if (hasUseableSourceMember) { return context.GetFinalDataSource( - SourceMemberDataSource.For(bestSourceMemberMatch), + bestSourceMemberMatch.CreateDataSource(), bestSourceMemberMatch.ContextMappingData); } diff --git a/AgileMapper/DataSources/SourceMemberDataSource.cs b/AgileMapper/DataSources/SourceMemberDataSource.cs index 61b2e62fb..7d0cefc3d 100644 --- a/AgileMapper/DataSources/SourceMemberDataSource.cs +++ b/AgileMapper/DataSources/SourceMemberDataSource.cs @@ -13,7 +13,7 @@ internal class SourceMemberDataSource : DataSourceBase { - private SourceMemberDataSource( + public SourceMemberDataSource( IQualifiedMember sourceMember, Expression sourceMemberValue, IMemberMapperData mapperData) @@ -63,19 +63,5 @@ private static Expression GetRuntimeTypeCheck(UnaryExpression cast, IMemberMappe return memberHasRuntimeType; } - - public static SourceMemberDataSource For(SourceMemberMatch sourceMemberMatch) - { - var mapperData = sourceMemberMatch.ContextMappingData.MapperData; - var sourceMember = sourceMemberMatch.SourceMember.RelativeTo(mapperData.SourceMember); - - var sourceMemberValue = sourceMember - .GetQualifiedAccess(mapperData) - .GetConversionTo(sourceMember.Type); - - var sourceMemberDataSource = new SourceMemberDataSource(sourceMember, sourceMemberValue, mapperData); - - return sourceMemberDataSource; - } } } \ No newline at end of file diff --git a/AgileMapper/Members/SourceMemberMatch.cs b/AgileMapper/Members/SourceMemberMatch.cs index 12ca94da0..29d387ff9 100644 --- a/AgileMapper/Members/SourceMemberMatch.cs +++ b/AgileMapper/Members/SourceMemberMatch.cs @@ -1,5 +1,13 @@ namespace AgileObjects.AgileMapper.Members { +#if NET35 + using Microsoft.Scripting.Ast; +#else + using System.Linq.Expressions; +#endif + using DataSources; + using Extensions.Internal; + internal class SourceMemberMatch { public static readonly SourceMemberMatch Null = new SourceMemberMatch(); @@ -13,20 +21,19 @@ public SourceMemberMatch( IChildMemberMappingData contextMappingData, bool isUseable = true) { - SourceMember = GetFinalSourceMember(sourceMember, contextMappingData); + SourceMember = GetFinalSourceMember(sourceMember, contextMappingData.MapperData); ContextMappingData = contextMappingData; IsUseable = isUseable; } private static IQualifiedMember GetFinalSourceMember( IQualifiedMember sourceMember, - IChildMemberMappingData targetData) + IMemberMapperData targetMapperData) { - return targetData - .MapperData + return targetMapperData .MapperContext .QualifiedMemberFactory - .GetFinalSourceMember(sourceMember, targetData.MapperData.TargetMember); + .GetFinalSourceMember(sourceMember, targetMapperData.TargetMember); } public IQualifiedMember SourceMember { get; } @@ -34,5 +41,22 @@ private static IQualifiedMember GetFinalSourceMember( public IChildMemberMappingData ContextMappingData { get; } public bool IsUseable { get; } + + public IDataSource CreateDataSource() + { + var mapperData = ContextMappingData.MapperData; + var sourceMember = SourceMember.RelativeTo(mapperData.SourceMember); + + var sourceMemberValue = sourceMember + .GetQualifiedAccess(mapperData) + .GetConversionTo(sourceMember.Type); + + var sourceMemberDataSource = new SourceMemberDataSource( + sourceMember, + sourceMemberValue, + mapperData); + + return sourceMemberDataSource; + } } } \ No newline at end of file