diff --git a/AgileMapper.PerformanceTester.Net461/AgileMapper.PerformanceTester.Net461.csproj b/AgileMapper.PerformanceTester.Net461/AgileMapper.PerformanceTester.Net461.csproj index 1b2c38431..587322d49 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.0\lib\net40\AgileObjects.ReadableExpressions.dll + + ..\packages\AgileObjects.ReadableExpressions.2.5.1\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 e8b2821c2..6c40706b4 100644 --- a/AgileMapper.PerformanceTester.Net461/packages.config +++ b/AgileMapper.PerformanceTester.Net461/packages.config @@ -1,7 +1,7 @@  - + diff --git a/AgileMapper.UnitTests.Net35/AgileMapper.UnitTests.Net35.csproj b/AgileMapper.UnitTests.Net35/AgileMapper.UnitTests.Net35.csproj index 8c0e81871..b7e78114d 100644 --- a/AgileMapper.UnitTests.Net35/AgileMapper.UnitTests.Net35.csproj +++ b/AgileMapper.UnitTests.Net35/AgileMapper.UnitTests.Net35.csproj @@ -39,7 +39,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/AgileMapper.UnitTests.NetCore/AgileMapper.UnitTests.NetCore.csproj b/AgileMapper.UnitTests.NetCore/AgileMapper.UnitTests.NetCore.csproj index 97252c0a1..999b8acf1 100644 --- a/AgileMapper.UnitTests.NetCore/AgileMapper.UnitTests.NetCore.csproj +++ b/AgileMapper.UnitTests.NetCore/AgileMapper.UnitTests.NetCore.csproj @@ -36,7 +36,7 @@ - + all runtime; build; native; contentfiles; analyzers diff --git a/AgileMapper.UnitTests.NetCore2.1/AgileMapper.UnitTests.NetCore2.1.csproj b/AgileMapper.UnitTests.NetCore2.1/AgileMapper.UnitTests.NetCore2.1.csproj index 67e922d6b..3e40f1f61 100644 --- a/AgileMapper.UnitTests.NetCore2.1/AgileMapper.UnitTests.NetCore2.1.csproj +++ b/AgileMapper.UnitTests.NetCore2.1/AgileMapper.UnitTests.NetCore2.1.csproj @@ -37,7 +37,7 @@ - + all runtime; build; native; contentfiles; analyzers diff --git a/AgileMapper.UnitTests.NetCore2.2/AgileMapper.UnitTests.NetCore2.2.csproj b/AgileMapper.UnitTests.NetCore2.2/AgileMapper.UnitTests.NetCore2.2.csproj index 663495ce2..5e0d1efe0 100644 --- a/AgileMapper.UnitTests.NetCore2.2/AgileMapper.UnitTests.NetCore2.2.csproj +++ b/AgileMapper.UnitTests.NetCore2.2/AgileMapper.UnitTests.NetCore2.2.csproj @@ -37,7 +37,7 @@ - + all runtime; build; native; contentfiles; analyzers diff --git a/AgileMapper.UnitTests.NetCore3.1/AgileMapper.UnitTests.NetCore3.1.csproj b/AgileMapper.UnitTests.NetCore3.1/AgileMapper.UnitTests.NetCore3.1.csproj index 38f8dc622..81cfab425 100644 --- a/AgileMapper.UnitTests.NetCore3.1/AgileMapper.UnitTests.NetCore3.1.csproj +++ b/AgileMapper.UnitTests.NetCore3.1/AgileMapper.UnitTests.NetCore3.1.csproj @@ -37,7 +37,7 @@ - + all runtime; build; native; contentfiles; analyzers diff --git a/AgileMapper.UnitTests.NetCore3/AgileMapper.UnitTests.NetCore3.csproj b/AgileMapper.UnitTests.NetCore3/AgileMapper.UnitTests.NetCore3.csproj index 0534875b8..44543edab 100644 --- a/AgileMapper.UnitTests.NetCore3/AgileMapper.UnitTests.NetCore3.csproj +++ b/AgileMapper.UnitTests.NetCore3/AgileMapper.UnitTests.NetCore3.csproj @@ -37,7 +37,7 @@ - + all runtime; build; native; contentfiles; analyzers diff --git a/AgileMapper.UnitTests.NonParallel/AgileMapper.UnitTests.NonParallel.csproj b/AgileMapper.UnitTests.NonParallel/AgileMapper.UnitTests.NonParallel.csproj index c9f7ca6e8..b6a183796 100644 --- a/AgileMapper.UnitTests.NonParallel/AgileMapper.UnitTests.NonParallel.csproj +++ b/AgileMapper.UnitTests.NonParallel/AgileMapper.UnitTests.NonParallel.csproj @@ -26,7 +26,7 @@ - + all runtime; build; native; contentfiles; analyzers 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 014d2c0c4..63d168c2a 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 @@ -26,7 +26,7 @@ - + all runtime; build; native; contentfiles; analyzers diff --git a/AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj b/AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj index 4c6712872..0688b7074 100644 --- a/AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj +++ b/AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj @@ -27,7 +27,7 @@ - + all runtime; build; native; contentfiles; analyzers 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 b8b54c380..f4090b86f 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 @@ -26,7 +26,7 @@ - + all runtime; build; native; contentfiles; analyzers diff --git a/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj b/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj index 86f60cd8b..08a5ec14f 100644 --- a/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj +++ b/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj @@ -27,7 +27,7 @@ - + all runtime; build; native; contentfiles; analyzers diff --git a/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj b/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj index d1dc60466..eac0b8a9f 100644 --- a/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj +++ b/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj @@ -26,7 +26,7 @@ - + all runtime; build; native; contentfiles; analyzers diff --git a/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj b/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj index 870a221cf..ed7fbd7d8 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj +++ b/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj @@ -28,7 +28,7 @@ - + all runtime; build; native; contentfiles; analyzers diff --git a/AgileMapper.UnitTests/AgileMapper.UnitTests.csproj b/AgileMapper.UnitTests/AgileMapper.UnitTests.csproj index 36d350f11..98765bf2c 100644 --- a/AgileMapper.UnitTests/AgileMapper.UnitTests.csproj +++ b/AgileMapper.UnitTests/AgileMapper.UnitTests.csproj @@ -31,7 +31,7 @@ - + all runtime; build; native; contentfiles; analyzers diff --git a/AgileMapper.UnitTests/Dictionaries/WhenMappingToNewDictionaryMembers.cs b/AgileMapper.UnitTests/Dictionaries/WhenMappingToNewDictionaryMembers.cs index 9d7062577..57cbe0dad 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,64 @@ 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>>>(); + + 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 private static class Issue97 diff --git a/AgileMapper/AgileMapper.csproj b/AgileMapper/AgileMapper.csproj index cb1ec3784..1e143265b 100644 --- a/AgileMapper/AgileMapper.csproj +++ b/AgileMapper/AgileMapper.csproj @@ -13,24 +13,15 @@ AgileObjects.AgileMapper 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+ + 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 - - Support for configuring complete sub-mappings, re: #176 -- Support for configuring sequential data sources, re: #184 -- Support for configuring alternate data sources, re: #133, #173 -- Including ToTarget data sources in mapping plan validation, re: #184 -- Fixing enumerable target ToTarget data sources, re: #173 -- Fixing caching of custom method struct creation factory -- Erroring if configured data sources conflict with ignored source members -- Improving configuration error messages -- Surfacing mapping plan Expressions -- Updating to ReadableExpressions v2.5 -- Performance improvements + - Fixing Dictionary-of-collections mapping, re: #200 +- Updating to ReadableExpressions v2.5.1 @@ -44,7 +35,7 @@ - + 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/Members/Dictionaries/DictionaryEntrySourceMember.cs b/AgileMapper/Members/Dictionaries/DictionaryEntrySourceMember.cs index 66ad8f65e..dd2e92923 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; 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 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)); }); } diff --git a/Directory.Build.props b/Directory.Build.props index 5a56b9539..f2e031791 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -11,9 +11,9 @@ true git https://github.com/AgileObjects/AgileMapper - 1.7.0 - 1.7.0.0 - 1.7.0.0 + 1.7.1 + 1.7.1.0 + 1.7.1.0 \ No newline at end of file diff --git a/NuGet/AgileObjects.AgileMapper.1.7.1.nupkg b/NuGet/AgileObjects.AgileMapper.1.7.1.nupkg new file mode 100644 index 000000000..33b920e0a Binary files /dev/null and b/NuGet/AgileObjects.AgileMapper.1.7.1.nupkg differ 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