From 804b92f2357036ce489e84ee35877694f37ae4a2 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Thu, 18 Jun 2020 19:25:44 +0100 Subject: [PATCH 1/4] Updating documentation example --- docs/src/configuration/Object-Mapping.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/src/configuration/Object-Mapping.md b/docs/src/configuration/Object-Mapping.md index dc63bae31..05c638f9b 100644 --- a/docs/src/configuration/Object-Mapping.md +++ b/docs/src/configuration/Object-Mapping.md @@ -9,10 +9,10 @@ Configure a custom factory for the mapping of a particular type using: Mapper.WhenMapping .From() // Apply to CustomerViewModel mappings .ToANew() // Apply to Customer creations - .MapInstancesUsing((cvm, c) => new Customer + .MapInstancesUsing(ctx => new Customer { - Name = cvm.Forename + " " + cvm.Surname, - Number = cvm.CustomerNo + Name = ctx.Source.Forename + " " + ctx.Source.Surname, + Number = ctx.Source.CustomerNo }); ``` @@ -21,10 +21,10 @@ Configure a conditional custom factory using ([inline](/configuration/Inline) ex ```cs Mapper.Map(customerViewModel).ToANew(cfg => cfg .If((cvm, c) => cvm.Discount > 0) // Apply if view model Discount > 0 - .MapInstancesUsing((cvm, c, i) => new Customer + .MapInstancesUsing(ctx => new Customer { - Name = cvm.Forename + " " + cvm.Surname, - Number = i, + Name = ctx.Source.Forename + " " + ctx.Source.Surname, + Number = ctx.ElementIndex, HasDiscount = true })); ``` \ No newline at end of file From 15b4ba6eb8466eccb261e8a4fe035f9b027511c4 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Sat, 8 Aug 2020 21:47:25 +0100 Subject: [PATCH 2/4] Adding failing test, re: #200 --- .../WhenMappingToNewDictionaryMembers.cs | 20 ++++++++++++++++++- AgileMapper/Caching/ArrayCache.cs | 3 ++- .../Looping/PopulationLoopDataExtensions.cs | 2 +- .../MappingExpressionFactoryBase.cs | 6 +++--- 4 files changed, 25 insertions(+), 6 deletions(-) diff --git a/AgileMapper.UnitTests/Dictionaries/WhenMappingToNewDictionaryMembers.cs b/AgileMapper.UnitTests/Dictionaries/WhenMappingToNewDictionaryMembers.cs index 9d7062577..61c4ea66f 100644 --- a/AgileMapper.UnitTests/Dictionaries/WhenMappingToNewDictionaryMembers.cs +++ b/AgileMapper.UnitTests/Dictionaries/WhenMappingToNewDictionaryMembers.cs @@ -290,7 +290,7 @@ public void ShouldNotCreateDictionaryAsFallbackComplexType() [Fact] public void ShouldFlattenAComplexTypeCollectionToANestedObjectDictionaryImplementation() { - var source = new PublicField>() + var source = new PublicField> { Value = new[] { @@ -339,6 +339,24 @@ public void ShouldFlattenAComplexTypeCollectionToANestedObjectDictionaryImplemen } + // See https://github.com/agileobjects/AgileMapper/issues/200 + [Fact] + public void ShouldMapListDictionaries() + { + var source = new PublicField>> + { + Value = new Dictionary> + { + ["a"] = new List { "b" } + } + }; + + var result = Mapper.Map(source) + .ToANew>>>(); + + result.ShouldNotBeNull(); + } + #region Helper Members private static class Issue97 diff --git a/AgileMapper/Caching/ArrayCache.cs b/AgileMapper/Caching/ArrayCache.cs index 8fdb577e4..e68f66d4d 100644 --- a/AgileMapper/Caching/ArrayCache.cs +++ b/AgileMapper/Caching/ArrayCache.cs @@ -79,7 +79,8 @@ public TValue GetOrAdd(TKey key, Func valueFactory) _keys[_length] = key; } - _values[_length++] = value; + _values[_length] = value; + _length++; } return value; diff --git a/AgileMapper/ObjectPopulation/Enumerables/Looping/PopulationLoopDataExtensions.cs b/AgileMapper/ObjectPopulation/Enumerables/Looping/PopulationLoopDataExtensions.cs index 0629e7386..55ed264a1 100644 --- a/AgileMapper/ObjectPopulation/Enumerables/Looping/PopulationLoopDataExtensions.cs +++ b/AgileMapper/ObjectPopulation/Enumerables/Looping/PopulationLoopDataExtensions.cs @@ -7,7 +7,7 @@ using System.Linq.Expressions; #endif using DataSources; - using AgileObjects.AgileMapper.Extensions.Internal; + using Extensions.Internal; using TypeConversion; using Members; diff --git a/AgileMapper/ObjectPopulation/MappingExpressionFactoryBase.cs b/AgileMapper/ObjectPopulation/MappingExpressionFactoryBase.cs index 9fb0b8983..eee848757 100644 --- a/AgileMapper/ObjectPopulation/MappingExpressionFactoryBase.cs +++ b/AgileMapper/ObjectPopulation/MappingExpressionFactoryBase.cs @@ -170,10 +170,10 @@ private void AddPopulationsAndCallbacks(MappingCreationContext context) { AddPopulationsAndCallbacks(this, context, (factory, ctx) => { - factory.AddObjectPopulation(context); + factory.AddObjectPopulation(ctx); - context.MappingExpressions.AddRange( - GetConfiguredToTargetDataSourceMappings(context, sequential: true)); + ctx.MappingExpressions.AddRange( + GetConfiguredToTargetDataSourceMappings(ctx, sequential: true)); }); } From 72975a35b5b6585fbee426316e40a36915444b28 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Sat, 8 Aug 2020 22:58:05 +0100 Subject: [PATCH 3/4] Support for mapping between Dictionaries of Lists --- .../DictionaryEntrySourceMember.cs | 48 ++++++++++++------- .../Dictionaries/DictionarySourceMember.cs | 7 ++- .../Members/Sources/ElementMembersSource.cs | 2 +- 3 files changed, 36 insertions(+), 21 deletions(-) diff --git a/AgileMapper/Members/Dictionaries/DictionaryEntrySourceMember.cs b/AgileMapper/Members/Dictionaries/DictionaryEntrySourceMember.cs index 66ad8f65e..135b7fe5d 100644 --- a/AgileMapper/Members/Dictionaries/DictionaryEntrySourceMember.cs +++ b/AgileMapper/Members/Dictionaries/DictionaryEntrySourceMember.cs @@ -25,6 +25,11 @@ public DictionaryEntrySourceMember( matchedTargetMember, parent) { + var entryMember = Member.RootSource(entryType); + _childMembers = new[] { entryMember }; + + IsEnumerable = entryMember.IsEnumerable; + IsSimple = !IsEnumerable && entryMember.IsSimple; } private DictionaryEntrySourceMember(DictionaryEntrySourceMember parent, Member childMember) @@ -42,26 +47,28 @@ private DictionaryEntrySourceMember( Func pathFactory, QualifiedMember matchedTargetMember, DictionarySourceMember parent, - Member[] childMembers = null) + Member[] childMembers) + : this(type, pathFactory, matchedTargetMember, parent) { - Type = type; - _pathFactory = pathFactory; - _matchedTargetMember = matchedTargetMember; - Parent = parent; - _childMembers = childMembers ?? new[] { Member.RootSource(type) }; - - if (childMembers == null) - { - IsEnumerable = _childMembers.First().IsEnumerable; - IsSimple = !IsEnumerable && _childMembers.First().IsSimple; - return; - } + _childMembers = childMembers; var leafMember = childMembers.Last(); IsEnumerable = leafMember.IsEnumerable; IsSimple = leafMember.IsSimple; } + private DictionaryEntrySourceMember( + Type type, + Func pathFactory, + QualifiedMember matchedTargetMember, + DictionarySourceMember parent) + { + Type = type; + _pathFactory = pathFactory; + _matchedTargetMember = matchedTargetMember; + Parent = parent; + } + public DictionarySourceMember Parent { get; } public bool IsRoot => false; @@ -86,7 +93,8 @@ private DictionaryEntrySourceMember( public IQualifiedMember GetInstanceElementMember() => Append(Member.EnumerableElement(Type, Type)); - public IQualifiedMember Append(Member childMember) => new DictionaryEntrySourceMember(this, childMember); + public IQualifiedMember Append(Member childMember) + => new DictionaryEntrySourceMember(this, childMember); public IQualifiedMember RelativeTo(IQualifiedMember otherMember) { @@ -129,13 +137,17 @@ public IQualifiedMember WithType(Type runtimeType) public bool HasCompatibleType(Type type) => Parent.HasCompatibleType(type); - public bool CouldMatch(QualifiedMember otherMember) => _matchedTargetMember.CouldMatch(otherMember); + public bool CouldMatch(QualifiedMember otherMember) + => _matchedTargetMember.CouldMatch(otherMember); public bool Matches(IQualifiedMember otherMember) { - return (otherMember == Parent) - ? Type.IsDictionary() - : _matchedTargetMember.Matches(otherMember); + if (otherMember == Parent) + { + return Type.IsDictionary(); + } + + return _matchedTargetMember.Matches(otherMember); } public Expression GetQualifiedAccess(Expression parentInstance) diff --git a/AgileMapper/Members/Dictionaries/DictionarySourceMember.cs b/AgileMapper/Members/Dictionaries/DictionarySourceMember.cs index 34eed4ae1..fade28fa2 100644 --- a/AgileMapper/Members/Dictionaries/DictionarySourceMember.cs +++ b/AgileMapper/Members/Dictionaries/DictionarySourceMember.cs @@ -12,6 +12,7 @@ internal class DictionarySourceMember : IQualifiedMember { private readonly IQualifiedMember _wrappedSourceMember; private readonly QualifiedMember _matchedTargetMember; + private IQualifiedMember _elementMember; public DictionarySourceMember(IQualifiedMemberContext context) : this(context.SourceMember, context.TargetMember) @@ -94,8 +95,10 @@ private DictionarySourceMember( public IQualifiedMember GetElementMember() { - return EntryMember.IsEnumerable - ? EntryMember.GetElementMember() + return _elementMember ??= EntryMember.IsEnumerable + ? IsEntireDictionaryMatch + ? EntryMember + : EntryMember.GetElementMember() : EntryMember.GetInstanceElementMember(); } diff --git a/AgileMapper/Members/Sources/ElementMembersSource.cs b/AgileMapper/Members/Sources/ElementMembersSource.cs index 9fa19efd1..bcd687a6a 100644 --- a/AgileMapper/Members/Sources/ElementMembersSource.cs +++ b/AgileMapper/Members/Sources/ElementMembersSource.cs @@ -34,6 +34,6 @@ QualifiedMember IMembersSource.GetTargetMember() => GetTargetMember(); public QualifiedMember GetTargetMember() - => _targetMember ?? (_targetMember = _enumerableMapperData.TargetMember.GetElementMember()); + => _targetMember ??= _enumerableMapperData.TargetMember.GetElementMember(); } } \ No newline at end of file From e043e9eb2f683e5e1c873ff39d2ccb8ddde0bff1 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Sat, 8 Aug 2020 23:14:48 +0100 Subject: [PATCH 4/4] Extending test coverage --- .../WhenMappingToNewDictionaryMembers.cs | 42 ++++++++++++++++++- .../DictionaryEntrySourceMember.cs | 15 +++---- 2 files changed, 46 insertions(+), 11 deletions(-) diff --git a/AgileMapper.UnitTests/Dictionaries/WhenMappingToNewDictionaryMembers.cs b/AgileMapper.UnitTests/Dictionaries/WhenMappingToNewDictionaryMembers.cs index 61c4ea66f..57cbe0dad 100644 --- a/AgileMapper.UnitTests/Dictionaries/WhenMappingToNewDictionaryMembers.cs +++ b/AgileMapper.UnitTests/Dictionaries/WhenMappingToNewDictionaryMembers.cs @@ -354,7 +354,47 @@ public void ShouldMapListDictionaries() var result = Mapper.Map(source) .ToANew>>>(); - result.ShouldNotBeNull(); + var resultDictionary = result + .ShouldNotBeNull() + .Value + .ShouldNotBeNull(); + + resultDictionary.ShouldHaveSingleItem(); + resultDictionary["a"].ShouldHaveSingleItem().ShouldBe("b"); + } + + [Fact] + public void ShouldMapArrayDictionaries() + { + var source = new PublicField> + { + Value = new Dictionary + { + ["1"] = new[] { 1 }, + ["2"] = new[] { 1, 2 }, + ["3"] = new[] { 1, 2, 3 }, + } + }; + + var result = Mapper.Map(source) + .ToANew>>(); + + var resultDictionary = result + .ShouldNotBeNull() + .Value + .ShouldNotBeNull(); + + resultDictionary.Count.ShouldBe(3); ; + resultDictionary["1"].ShouldHaveSingleItem().ShouldBe(1L); + + resultDictionary["2"].Length.ShouldBe(2); + resultDictionary["2"][0].ShouldBe(1L); + resultDictionary["2"][1].ShouldBe(2L); + + resultDictionary["3"].Length.ShouldBe(3); + resultDictionary["3"][0].ShouldBe(1L); + resultDictionary["3"][1].ShouldBe(2L); + resultDictionary["3"][2].ShouldBe(3L); } #region Helper Members diff --git a/AgileMapper/Members/Dictionaries/DictionaryEntrySourceMember.cs b/AgileMapper/Members/Dictionaries/DictionaryEntrySourceMember.cs index 135b7fe5d..dd2e92923 100644 --- a/AgileMapper/Members/Dictionaries/DictionaryEntrySourceMember.cs +++ b/AgileMapper/Members/Dictionaries/DictionaryEntrySourceMember.cs @@ -93,8 +93,7 @@ private DictionaryEntrySourceMember( public IQualifiedMember GetInstanceElementMember() => Append(Member.EnumerableElement(Type, Type)); - public IQualifiedMember Append(Member childMember) - => new DictionaryEntrySourceMember(this, childMember); + public IQualifiedMember Append(Member childMember) => new DictionaryEntrySourceMember(this, childMember); public IQualifiedMember RelativeTo(IQualifiedMember otherMember) { @@ -137,17 +136,13 @@ public IQualifiedMember WithType(Type runtimeType) public bool HasCompatibleType(Type type) => Parent.HasCompatibleType(type); - public bool CouldMatch(QualifiedMember otherMember) - => _matchedTargetMember.CouldMatch(otherMember); + public bool CouldMatch(QualifiedMember otherMember) => _matchedTargetMember.CouldMatch(otherMember); public bool Matches(IQualifiedMember otherMember) { - if (otherMember == Parent) - { - return Type.IsDictionary(); - } - - return _matchedTargetMember.Matches(otherMember); + return (otherMember == Parent) + ? Type.IsDictionary() + : _matchedTargetMember.Matches(otherMember); } public Expression GetQualifiedAccess(Expression parentInstance)