Skip to content

Commit 37e4eb8

Browse files
committed
Support for mapping between runtime-typed simple types in object enumerables
1 parent a84b2ca commit 37e4eb8

File tree

6 files changed

+80
-11
lines changed

6 files changed

+80
-11
lines changed

AgileMapper.Net40/AgileMapper.Net40.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -780,6 +780,7 @@
780780
<Compile Include="..\VersionInfo.cs">
781781
<Link>Properties\VersionInfo.cs</Link>
782782
</Compile>
783+
<Compile Include="ObjectPopulation\SimpleTypeMappingExpressionFactory.cs" />
783784
<Compile Include="Properties\AssemblyInfo.Net40.cs" />
784785
</ItemGroup>
785786
<ItemGroup>
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
namespace AgileObjects.AgileMapper.ObjectPopulation
2+
{
3+
using System.Collections.Generic;
4+
using System.Linq.Expressions;
5+
using Extensions;
6+
using Members;
7+
8+
internal class SimpleTypeMappingExpressionFactory : MappingExpressionFactoryBase
9+
{
10+
public override bool IsFor(IObjectMappingData mappingData)
11+
{
12+
return mappingData.MapperKey.MappingTypes.TargetType.IsSimple();
13+
}
14+
15+
protected override bool TargetCannotBeMapped(IObjectMappingData mappingData, out Expression nullMappingBlock)
16+
{
17+
nullMappingBlock = null;
18+
return false;
19+
}
20+
21+
protected override IEnumerable<Expression> GetShortCircuitReturns(GotoExpression returnNull, IObjectMappingData mappingData)
22+
=> Enumerable<Expression>.Empty;
23+
24+
protected override Expression GetDerivedTypeMappings(IObjectMappingData mappingData)
25+
=> Constants.EmptyExpression;
26+
27+
protected override IEnumerable<Expression> GetObjectPopulation(IObjectMappingData mappingData)
28+
{
29+
var mapperData = mappingData.MapperData;
30+
31+
if (mapperData.MapperContext.ValueConverters.CanConvert(mapperData.SourceType, mapperData.TargetType))
32+
{
33+
yield return mapperData.GetValueConversion(mapperData.SourceObject, mapperData.TargetType);
34+
yield break;
35+
}
36+
37+
yield return mapperData.TargetObject;
38+
}
39+
40+
protected override Expression GetReturnValue(ObjectMapperData mapperData) => mapperData.InstanceVariable;
41+
}
42+
}

AgileMapper.UnitTests/WhenMappingToNewEnumerableMembers.cs

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,22 @@ public void ShouldCreateANewIntEnumerable()
4242
result.Value.SequenceEqual(source.Value).ShouldBeTrue();
4343
}
4444

45+
[Fact]
46+
public void ShouldCreateANewObjectCollection()
47+
{
48+
var source = new PublicField<object[]>
49+
{
50+
Value = new object[] { 9, new Person { Name = "Mags" }, string.Empty }
51+
};
52+
var result = Mapper.Map(source).ToANew<PublicField<Collection<object>>>();
53+
54+
result.Value.ShouldNotBeNull();
55+
result.Value.First().ShouldBe(9);
56+
result.Value.Second().ShouldBeOfType<Person>();
57+
((Person)result.Value.Second()).Name.ShouldBe("Mags");
58+
result.Value.Third().ShouldBe(string.Empty);
59+
}
60+
4561
[Fact]
4662
public void ShouldCreateANewComplexTypeEnumerable()
4763
{
@@ -221,16 +237,6 @@ public void ShouldHandleANullSourceMemberForAWriteOnlyTarget()
221237
result.Value.ShouldBeEmpty();
222238
}
223239

224-
[Fact]
225-
public void ShouldCreateAnEmptyCollectionByDefault()
226-
{
227-
var source = new PublicProperty<Collection<int>> { Value = null };
228-
var result = Mapper.Map(source).ToANew<PublicProperty<Collection<int>>>();
229-
230-
result.Value.ShouldNotBeNull();
231-
result.Value.ShouldBeEmpty();
232-
}
233-
234240
[Fact]
235241
public void ShouldPopulateANonNullReadOnlyNestedICollection()
236242
{
@@ -337,6 +343,16 @@ public void ShouldPopulateANonNullReadOnlyNestedIList()
337343
}
338344
}
339345

346+
[Fact]
347+
public void ShouldCreateAnEmptyCollectionByDefault()
348+
{
349+
var source = new PublicProperty<Collection<int>> { Value = null };
350+
var result = Mapper.Map(source).ToANew<PublicProperty<Collection<int>>>();
351+
352+
result.Value.ShouldNotBeNull();
353+
result.Value.ShouldBeEmpty();
354+
}
355+
340356
[Fact]
341357
public void ShouldMapCollectionsToNullIfConfiguredGlobally()
342358
{

AgileMapper/ObjectPopulation/MappingExpressionFactoryBase.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,11 @@ private Expression GetMappingBlock(IList<Expression> mappingExpressions, ObjectM
8888
{
8989
if (mappingExpressions[0].NodeType != ExpressionType.Block)
9090
{
91+
if (mappingExpressions[0].NodeType == ExpressionType.MemberAccess)
92+
{
93+
return mappingExpressions[0];
94+
}
95+
9196
var objectAssignment = mappingExpressions.First(exp => exp.NodeType == ExpressionType.Assign);
9297

9398
if (mappingExpressions.Last() == objectAssignment)

AgileMapper/ObjectPopulation/ObjectMapperFactory.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ public ObjectMapperFactory(MapperContext mapperContext)
1717
_mappingExpressionFactories = new MappingExpressionFactoryBase[]
1818
{
1919
new DictionaryMappingExpressionFactory(),
20+
new SimpleTypeMappingExpressionFactory(),
2021
new EnumerableMappingExpressionFactory(),
2122
new ComplexTypeMappingExpressionFactory(mapperContext)
2223
};

AgileMapper/ObjectPopulation/ObjectMappingDataFactory.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -355,10 +355,14 @@ private static MappingDataCreator<TSource, TTarget> GetMappingDataCreator<TSourc
355355
var sourceParameter = Parameters.Create(k.DeclaredSourceType, "source");
356356
var targetParameter = Parameters.Create(k.DeclaredTargetType, "target");
357357

358+
var targetParameterValue = k.RuntimeTargetType.IsPrimitive()
359+
? Expression.Coalesce(targetParameter, typeof(int).ToDefaultExpression())
360+
: (Expression)targetParameter;
361+
358362
var constructorCall = Expression.New(
359363
dataType.GetPublicInstanceConstructors().First(),
360364
sourceParameter.GetConversionTo(k.RuntimeSourceType),
361-
targetParameter.GetConversionTo(k.RuntimeTargetType),
365+
targetParameterValue.GetConversionTo(k.RuntimeTargetType),
362366
enumerableIndexParameter,
363367
mapperKeyParameter,
364368
Parameters.MappingContext,

0 commit comments

Comments
 (0)