diff --git a/AgileMapper.UnitTests/WhenMappingOnToEnumerableMembers.cs b/AgileMapper.UnitTests/WhenMappingOnToEnumerableMembers.cs index 2579f3b11..ba8825696 100644 --- a/AgileMapper.UnitTests/WhenMappingOnToEnumerableMembers.cs +++ b/AgileMapper.UnitTests/WhenMappingOnToEnumerableMembers.cs @@ -201,6 +201,35 @@ public void ShouldMergeAnIdentifiableComplexTypeIReadOnlyCollectionArray() result.Value.Second().Price.ShouldBe(1000.00); result.Value.ShouldBe(r => r.ProductId, "Magic", "Science"); } + + [Fact] + public void ShouldMergeAnIdentifiableComplexTypeSet() + { + var source = new PublicField + { + Value = new Product[] + { + new MegaProduct { ProductId = "Science", Price = 1000.00 } + } + }; + + var target = new PublicField> + { + Value = new HashSet + { + new MegaProduct { ProductId = "Magic", Price = 1.00 }, + new MegaProduct { ProductId = "Science" } + } + }; + + var existingProduct = target.Value.Second(); + var result = Mapper.Map(source).OnTo(target); + + result.Value.ShouldBeSameAs(target.Value); + result.Value.Second().ShouldBeSameAs(existingProduct); + result.Value.Second().Price.ShouldBe(1000.00); + result.Value.ShouldBe(r => r.ProductId, "Magic", "Science"); + } #endif [Fact] public void ShouldHandleANullSourceMember() diff --git a/AgileMapper.UnitTests/WhenMappingToNewEnumerables.cs b/AgileMapper.UnitTests/WhenMappingToNewEnumerables.cs index c359e9042..7b825f128 100644 --- a/AgileMapper.UnitTests/WhenMappingToNewEnumerables.cs +++ b/AgileMapper.UnitTests/WhenMappingToNewEnumerables.cs @@ -127,7 +127,6 @@ public void ShouldCreateAnIReadOnlyCollection() result.ShouldBe((short)1, (short)2, (short)3); } #endif - [Fact] public void ShouldMapFromAHashset() { @@ -142,6 +141,17 @@ public void ShouldMapFromAHashset() result.ShouldBe(yesterday, today, tomorrow); } +#if !NET35 + [Fact] + public void ShouldMapToAnISet() + { + var source = new[] { "1", "2", "3" }; + var result = Mapper.Map(source).ToANew>(); + + result.ShouldNotBeNull(); + result.ShouldBe(1L, 2L, 3L); + } +#endif [Fact] public void ShouldHandleANullComplexTypeElement() { diff --git a/AgileMapper/AgileMapper.csproj b/AgileMapper/AgileMapper.csproj index a888c42bf..f74b3b4ac 100644 --- a/AgileMapper/AgileMapper.csproj +++ b/AgileMapper/AgileMapper.csproj @@ -44,7 +44,7 @@ - $(DefineConstants);FEATURE_SERIALIZATION;FEATURE_DYNAMIC;FEATURE_COLLECTION_CAPACITY;NET_40; + $(DefineConstants);FEATURE_SERIALIZATION;FEATURE_DYNAMIC;FEATURE_COLLECTION_CAPACITY;FEATURE_ISET;NET_40; @@ -55,7 +55,7 @@ - $(DefineConstants);NET_STANDARD;FEATURE_DYNAMIC;NET_STANDARD_1; + $(DefineConstants);NET_STANDARD;FEATURE_DYNAMIC;FEATURE_ISET;NET_STANDARD_1; @@ -67,7 +67,7 @@ - $(DefineConstants);NET_STANDARD;FEATURE_SERIALIZATION;FEATURE_DYNAMIC;FEATURE_COLLECTION_CAPACITY;NET_STANDARD_2; + $(DefineConstants);NET_STANDARD;FEATURE_SERIALIZATION;FEATURE_DYNAMIC;FEATURE_COLLECTION_CAPACITY;FEATURE_ISET;NET_STANDARD_2; diff --git a/AgileMapper/ObjectPopulation/Enumerables/EnumerablePopulationBuilder.cs b/AgileMapper/ObjectPopulation/Enumerables/EnumerablePopulationBuilder.cs index 2268433a9..6abea2430 100644 --- a/AgileMapper/ObjectPopulation/Enumerables/EnumerablePopulationBuilder.cs +++ b/AgileMapper/ObjectPopulation/Enumerables/EnumerablePopulationBuilder.cs @@ -379,7 +379,7 @@ private Expression GetTargetVariableValue() if (MapperData.TargetIsDefinitelyUnpopulated()) { - return GetNullTargetListConstruction(); + return GetNullTargetConstruction(); } if (_sourceAdapter.UseReadOnlyTargetWrapper) @@ -415,7 +415,7 @@ private Expression GetTargetVariableValue() return nonNullTargetVariableValue; } - var nullTargetVariableValue = GetNullTargetListConstruction(); + var nullTargetVariableValue = GetNullTargetConstruction(); var targetVariableValue = Expression.Condition( MapperData.TargetObject.GetIsNotDefaultComparison(), @@ -429,21 +429,21 @@ private Expression GetTargetVariableValue() private Expression GetCopyIntoWrapperConstruction() => TargetTypeHelper.GetWrapperConstruction(MapperData.TargetObject, GetSourceCountAccess()); - private Expression GetNullTargetListConstruction() + private Expression GetNullTargetConstruction() { var nonNullTargetVariableType = (TargetTypeHelper.IsDeclaredReadOnly ? TargetTypeHelper.ListType : MapperData.TargetType); var nullTargetVariableType = GetNullTargetVariableType(nonNullTargetVariableType); +#if FEATURE_COLLECTION_CAPACITY return SourceTypeHelper.IsEnumerableInterface || TargetTypeHelper.IsCollection ? Expression.New(nullTargetVariableType) -#if FEATURE_COLLECTION_CAPACITY : Expression.New( nullTargetVariableType.GetPublicInstanceConstructor(typeof(int)), GetSourceCountAccess()); #else - : Expression.New(nullTargetVariableType.GetPublicInstanceConstructor()); + return Expression.New(nullTargetVariableType); #endif } @@ -493,7 +493,13 @@ private Expression GetCopyIntoObjectConstruction() private Type GetNullTargetVariableType(Type nonNullTargetVariableType) { return nonNullTargetVariableType.IsInterface() +#if FEATURE_ISET + ? nonNullTargetVariableType.IsClosedTypeOf(typeof(ISet<>)) + ? TargetTypeHelper.HashSetType + : TargetTypeHelper.ListType +#else ? TargetTypeHelper.ListType +#endif : nonNullTargetVariableType; } diff --git a/AgileMapper/ObjectPopulation/Enumerables/EnumerableTypeHelper.cs b/AgileMapper/ObjectPopulation/Enumerables/EnumerableTypeHelper.cs index 86dd88b1a..ff6dd4480 100644 --- a/AgileMapper/ObjectPopulation/Enumerables/EnumerableTypeHelper.cs +++ b/AgileMapper/ObjectPopulation/Enumerables/EnumerableTypeHelper.cs @@ -22,7 +22,9 @@ internal class EnumerableTypeHelper private Type _readOnlyCollectionType; private Type _collectionInterfaceType; private Type _enumerableInterfaceType; - +#if FEATURE_ISET + private Type _setInterfaceType; +#endif public EnumerableTypeHelper(IQualifiedMember member) : this(member.Type, member.ElementType) { @@ -45,7 +47,7 @@ public bool IsDictionary public bool IsCollection => EnumerableType.IsAssignableTo(CollectionType); - public bool IsHashSet => EnumerableType == HashSetType; + private bool IsHashSet => EnumerableType == HashSetType; public bool IsReadOnlyCollection => EnumerableType == ReadOnlyCollectionType; @@ -53,6 +55,11 @@ public bool IsDictionary public bool HasCollectionInterface => EnumerableType.IsAssignableTo(CollectionInterfaceType); +#if FEATURE_ISET + private bool HasSetInterface => EnumerableType.IsAssignableTo(SetInterfaceType); +#else + private bool HasSetInterface => IsHashSet; +#endif public bool IsReadOnly => IsArray || IsReadOnlyCollection; public bool IsDeclaredReadOnly @@ -89,6 +96,9 @@ private bool IsReadOnlyCollectionInterface() public Type EnumerableInterfaceType => GetEnumerableType(ref _enumerableInterfaceType, typeof(IEnumerable<>)); +#if FEATURE_ISET + private Type SetInterfaceType => GetEnumerableType(ref _setInterfaceType, typeof(ISet<>)); +#endif private Type GetEnumerableType(ref Type typeField, Type openGenericEnumerableType) => typeField ?? (typeField = openGenericEnumerableType.MakeGenericType(ElementType)); @@ -113,7 +123,7 @@ public Expression GetEmptyInstanceCreation(Type enumerableType = null) public Expression GetCopyIntoObjectConstruction(Expression targetObject) { - var objectType = IsHashSet ? HashSetType : ListType; + var objectType = HasSetInterface ? HashSetType : ListType; return Expression.New( objectType.GetPublicInstanceConstructor(EnumerableInterfaceType), @@ -151,7 +161,7 @@ public Expression GetEnumerableConversion(Expression instance, bool allowEnumera return instance.WithToCollectionCall(ElementType); } - return instance.WithToListLinqCall(ElementType); + return GetCopyIntoObjectConstruction(instance); } private static bool ValueIsNotEnumerableInterface(Expression instance) @@ -166,7 +176,7 @@ public Type GetEmptyInstanceCreationFallbackType() ? EnumerableType.GetDictionaryConcreteType() : IsCollection ? CollectionType - : IsHashSet + : HasSetInterface ? HashSetType : ListType; }