Skip to content

Commit 48b02af

Browse files
authored
IReadOnlyCollection support (#39)
* Support for mapping to a new root IReadOnlyCollection * Short-circuiting target collection variable creation for root new enumerable mapping * Support for mapping over an IReadOnlyCollection list / Short-circuiting target enumerable variable population for root Over and OnTo mappings * Handling read-only target IReadOnlyCollections when mapping Over or OnTo * Removing unnecessary value type checking * Test coverage for mapping on to a nested IReadOnlyCollection * Support for merging on to a nested IReadOnlyCollection complex type list * Using Expression.ArrayLength * Finding object ids by convention for types ending in 'Dto' and 'ViewModel' * Moving identifier finding into NamingSettings and caching on a per-mapper basis, taking into account custom naming patterns / Renaming MemberFinder to MemberCache / Removing NamingSettings singleton * Erroring if an identifier configuration is redundant * Support for configuring WhenMapping.InstanceOf() with anonymous types / Test coverage for erroring when configuring a redundant identifier in a mapper with a custom naming convention * Test coverage for mapping over an IReadOnlyCollection Collection<T>
1 parent e68c1f8 commit 48b02af

33 files changed

+472
-135
lines changed

AgileMapper.UnitTests/Configuration/WhenConfiguringNameMatching.cs

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
namespace AgileObjects.AgileMapper.UnitTests.Configuration
22
{
33
using System;
4+
using System.Collections.Generic;
45
using AgileMapper.Configuration;
6+
using AgileMapper.Extensions;
57
using Shouldly;
68
using TestClasses;
79
using Xunit;
@@ -77,9 +79,7 @@ public void ShouldHandleACustomNamingPattern()
7779
{
7880
using (var mapper = Mapper.CreateNew())
7981
{
80-
mapper
81-
.WhenMapping
82-
.UseNamePattern("^_abc(.+)xyz_$");
82+
mapper.WhenMapping.UseNamePattern("^_abc(.+)xyz_$");
8383

8484
var source = new { _abcValuexyz_ = 999 };
8585
var result = mapper.Map(source).ToANew<PublicField<string>>();
@@ -88,6 +88,27 @@ public void ShouldHandleACustomNamingPattern()
8888
}
8989
}
9090

91+
[Fact]
92+
public void ShouldUseACustomNamingPatternInIdentifierMatching()
93+
{
94+
using (var mapper = Mapper.CreateNew())
95+
{
96+
mapper.WhenMapping.UseNamePattern("^_(.+)_$");
97+
98+
var source = new[] { new { _Id_ = 123, _Price_ = 1.99 } };
99+
var target = new List<ProductDto>
100+
{
101+
new ProductDto { ProductId = "123", Price = 0.99m }
102+
};
103+
var productDto = target.First();
104+
var result = mapper.Map(source).Over(target);
105+
106+
result.ShouldHaveSingleItem();
107+
result.First().ShouldBeSameAs(productDto);
108+
result.First().Price.ShouldBe(1.99m);
109+
}
110+
}
111+
91112
[Fact]
92113
public void ShouldHandleACustomNamingPrefixPattern()
93114
{

AgileMapper.UnitTests/Configuration/WhenConfiguringTypeIdentifiers.cs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,38 @@ public void ShouldUseAConfiguredIdentifierExpression()
8787
}
8888
}
8989

90+
[Fact]
91+
public void ShouldErrorIfRedundantIdentifierSpecified()
92+
{
93+
using (var mapper = Mapper.CreateNew())
94+
{
95+
var idEx = Should.Throw<MappingConfigurationException>(() =>
96+
mapper.WhenMapping
97+
.InstancesOf<Person>()
98+
.IdentifyUsing(p => p.Id));
99+
100+
idEx.Message.ShouldContain("Id is automatically used as the identifier");
101+
idEx.Message.ShouldContain("does not need to be configured");
102+
}
103+
}
104+
105+
[Fact]
106+
public void ShouldErrorIfRedundantCustomNamingIdentifierSpecified()
107+
{
108+
using (var mapper = Mapper.CreateNew())
109+
{
110+
mapper.WhenMapping.UseNamePattern("^_(.+)_$");
111+
112+
var idEx = Should.Throw<MappingConfigurationException>(() =>
113+
mapper.WhenMapping
114+
.InstancesOf(new { _Id_ = default(int) })
115+
.IdentifyUsing(d => d._Id_));
116+
117+
idEx.Message.ShouldContain("_Id_ is automatically used as the identifier");
118+
idEx.Message.ShouldContain("does not need to be configured");
119+
}
120+
}
121+
90122
[Fact]
91123
public void ShouldErrorIfMultipleIdentifiersSpecifiedForSameType()
92124
{

AgileMapper.UnitTests/Members/MemberTestsBase.cs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@
99

1010
public abstract class MemberTestsBase
1111
{
12-
internal static readonly MapperContext DefaultMapperContext = new MapperContext();
13-
internal static readonly MemberFinder MemberFinder = GlobalContext.Instance.MemberFinder;
12+
internal static readonly MapperContext DefaultMapperContext = MapperContext.Default;
13+
internal static readonly MemberCache MemberCache = GlobalContext.Instance.MemberCache;
1414

1515
internal IQualifiedMember SourceMemberFor<T>(T sourceObject)
1616
{
@@ -29,22 +29,22 @@ internal IQualifiedMember SourceMemberFor<T>(T sourceObject, Expression<Func<T,
2929
internal IQualifiedMember SourceMemberFor<T>(Expression<Func<T, object>> childMemberExpression = null)
3030
=> SourceMemberFor(Member.RootSource<T>(), childMemberExpression);
3131

32-
private static IQualifiedMember SourceMemberFor(Member rootSourceMember, LambdaExpression childMemberExpression)
32+
private static IQualifiedMember SourceMemberFor(Member rootSourceMember, Expression childMemberExpression)
3333
{
3434
return (childMemberExpression == null)
35-
? QualifiedMember.From(rootSourceMember, MapperContext.Default)
35+
? QualifiedMember.From(rootSourceMember, DefaultMapperContext)
3636
: MemberExtensions.CreateMember(
3737
childMemberExpression,
3838
Member.RootSource,
39-
MemberFinder.GetSourceMembers,
40-
MapperContext.Default);
39+
MemberCache.GetSourceMembers,
40+
DefaultMapperContext);
4141
}
4242

4343
internal QualifiedMember TargetMemberFor<T>(Expression<Func<T, object>> childMemberExpression = null)
4444
{
4545
return (childMemberExpression == null)
46-
? QualifiedMember.From(Member.RootTarget(typeof(T)), MapperContext.Default)
47-
: childMemberExpression.ToTargetMember(MapperContext.Default);
46+
? QualifiedMember.From(Member.RootTarget(typeof(T)), DefaultMapperContext)
47+
: childMemberExpression.ToTargetMember(DefaultMapperContext);
4848
}
4949
}
5050
}

AgileMapper.UnitTests/Members/WhenDeterminingATypeIdentifier.cs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,31 +10,35 @@ public class WhenDeterminingATypeIdentifier : MemberTestsBase
1010
[Fact]
1111
public void ShouldUseAnIdProperty()
1212
{
13-
MemberFinder
13+
DefaultMapperContext
14+
.Naming
1415
.GetIdentifierOrNull(TypeKey.ForTypeId(new { Id = "blahblahblah" }.GetType()))
1516
.ShouldNotBeNull();
1617
}
1718

1819
[Fact]
1920
public void ShouldUseAnIdentifierProperty()
2021
{
21-
MemberFinder
22+
DefaultMapperContext
23+
.Naming
2224
.GetIdentifierOrNull(TypeKey.ForTypeId(new { Identifier = "lalalala" }.GetType()))
2325
.ShouldNotBeNull();
2426
}
2527

2628
[Fact]
2729
public void ShouldUseATypeIdProperty()
2830
{
29-
MemberFinder
31+
DefaultMapperContext
32+
.Naming
3033
.GetIdentifierOrNull(TypeKey.ForTypeId(typeof(Person)))
3134
.ShouldNotBeNull();
3235
}
3336

3437
[Fact]
3538
public void ShouldReturnNullIfNoIdentifier()
3639
{
37-
MemberFinder
40+
DefaultMapperContext
41+
.Naming
3842
.GetIdentifierOrNull(TypeKey.ForTypeId(new { NoIdHere = true }.GetType()))
3943
.ShouldBeNull();
4044
}

AgileMapper.UnitTests/Members/WhenFindingSourceMembers.cs

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ public class WhenFindingSourceMembers : MemberTestsBase
1212
[Fact]
1313
public void ShouldFindAPublicProperty()
1414
{
15-
var member = MemberFinder
15+
var member = MemberCache
1616
.GetSourceMembers(typeof(PublicProperty<string>))
1717
.FirstOrDefault(m => m.Name == "Value");
1818

@@ -23,7 +23,7 @@ public void ShouldFindAPublicProperty()
2323
[Fact]
2424
public void ShouldFindAPublicField()
2525
{
26-
var member = MemberFinder
26+
var member = MemberCache
2727
.GetSourceMembers(typeof(PublicField<int>))
2828
.FirstOrDefault(m => m.Name == "Value");
2929

@@ -34,7 +34,7 @@ public void ShouldFindAPublicField()
3434
[Fact]
3535
public void ShouldFindAPublicGetMethod()
3636
{
37-
var member = MemberFinder
37+
var member = MemberCache
3838
.GetSourceMembers(typeof(PublicGetMethod<DateTime>))
3939
.FirstOrDefault(m => m.Name == "GetValue");
4040

@@ -45,7 +45,7 @@ public void ShouldFindAPublicGetMethod()
4545
[Fact]
4646
public void ShouldFindARootArrayElement()
4747
{
48-
var member = MemberFinder
48+
var member = MemberCache
4949
.GetSourceMembers(typeof(int[]))
5050
.FirstOrDefault();
5151

@@ -55,7 +55,7 @@ public void ShouldFindARootArrayElement()
5555
[Fact]
5656
public void ShouldIgnoreAWriteOnlyPublicProperty()
5757
{
58-
var member = MemberFinder
58+
var member = MemberCache
5959
.GetSourceMembers(typeof(PublicWriteOnlyProperty<long>))
6060
.FirstOrDefault(m => m.Name == "Value");
6161

@@ -65,7 +65,7 @@ public void ShouldIgnoreAWriteOnlyPublicProperty()
6565
[Fact]
6666
public void ShouldIgnoreANonPublicField()
6767
{
68-
var member = MemberFinder
68+
var member = MemberCache
6969
.GetSourceMembers(typeof(InternalField<byte>))
7070
.FirstOrDefault(m => m.Name == "Value");
7171

@@ -75,7 +75,7 @@ public void ShouldIgnoreANonPublicField()
7575
[Fact]
7676
public void ShouldIgnoreASetMethod()
7777
{
78-
var member = MemberFinder
78+
var member = MemberCache
7979
.GetSourceMembers(typeof(PublicSetMethod<short>))
8080
.FirstOrDefault(m => m.Name == "Value");
8181

@@ -85,7 +85,7 @@ public void ShouldIgnoreASetMethod()
8585
[Fact]
8686
public void ShouldIgnoreGetType()
8787
{
88-
var member = MemberFinder
88+
var member = MemberCache
8989
.GetSourceMembers(typeof(PublicProperty<int?>))
9090
.FirstOrDefault(m => m.Name == "GetType");
9191

@@ -95,7 +95,7 @@ public void ShouldIgnoreGetType()
9595
[Fact]
9696
public void ShouldIgnoreGetHashCode()
9797
{
98-
var member = MemberFinder
98+
var member = MemberCache
9999
.GetSourceMembers(typeof(PublicProperty<DateTime?>))
100100
.FirstOrDefault(m => m.Name == "GetHashCode");
101101

@@ -105,7 +105,7 @@ public void ShouldIgnoreGetHashCode()
105105
[Fact]
106106
public void ShouldIgnoreAPropertyGetter()
107107
{
108-
var member = MemberFinder
108+
var member = MemberCache
109109
.GetSourceMembers(typeof(PublicProperty<string>))
110110
.FirstOrDefault(m => m.Name.StartsWith("get_"));
111111

AgileMapper.UnitTests/Members/WhenFindingTargetMembers.cs

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ public class WhenFindingTargetMembers : MemberTestsBase
1313
[Fact]
1414
public void ShouldFindAPublicProperty()
1515
{
16-
var member = MemberFinder
16+
var member = MemberCache
1717
.GetTargetMembers(typeof(PublicProperty<byte>))
1818
.FirstOrDefault(m => m.Name == "Value");
1919

@@ -24,7 +24,7 @@ public void ShouldFindAPublicProperty()
2424
[Fact]
2525
public void ShouldFindAPublicField()
2626
{
27-
var member = MemberFinder
27+
var member = MemberCache
2828
.GetTargetMembers(typeof(PublicField<int>))
2929
.FirstOrDefault(m => m.Name == "Value");
3030

@@ -36,7 +36,7 @@ public void ShouldFindAPublicField()
3636
[Fact]
3737
public void ShouldFindAPublicReadOnlyField()
3838
{
39-
var member = MemberFinder
39+
var member = MemberCache
4040
.GetTargetMembers(typeof(PublicReadOnlyField<IEnumerable<byte>>))
4141
.FirstOrDefault(m => m.Name == "Value");
4242

@@ -48,7 +48,7 @@ public void ShouldFindAPublicReadOnlyField()
4848
[Fact]
4949
public void ShouldFindAPublicSetMethod()
5050
{
51-
var member = MemberFinder
51+
var member = MemberCache
5252
.GetTargetMembers(typeof(PublicSetMethod<DateTime>))
5353
.FirstOrDefault(m => m.Name == "SetValue");
5454

@@ -60,7 +60,7 @@ public void ShouldFindAPublicSetMethod()
6060
[Fact]
6161
public void ShouldFindAPublicReadOnlyComplexTypeProperty()
6262
{
63-
var member = MemberFinder
63+
var member = MemberCache
6464
.GetTargetMembers(typeof(PublicReadOnlyProperty<object>))
6565
.FirstOrDefault(m => m.Name == "Value");
6666

@@ -72,7 +72,7 @@ public void ShouldFindAPublicReadOnlyComplexTypeProperty()
7272
[Fact]
7373
public void ShouldFindAPublicReadOnlyArrayField()
7474
{
75-
var member = MemberFinder
75+
var member = MemberCache
7676
.GetTargetMembers(typeof(PublicReadOnlyField<byte[]>))
7777
.FirstOrDefault(m => m.Name == "Value");
7878

@@ -85,7 +85,7 @@ public void ShouldFindAPublicReadOnlyArrayField()
8585
[Fact]
8686
public void ShouldFindAPublicReadOnlySimpleTypeProperty()
8787
{
88-
var member = MemberFinder
88+
var member = MemberCache
8989
.GetTargetMembers(typeof(PublicReadOnlyProperty<long>))
9090
.FirstOrDefault(m => m.Name == "Value");
9191

@@ -97,7 +97,7 @@ public void ShouldFindAPublicReadOnlySimpleTypeProperty()
9797
[Fact]
9898
public void ShouldFindAReadOnlyArrayProperty()
9999
{
100-
var member = MemberFinder
100+
var member = MemberCache
101101
.GetTargetMembers(typeof(PublicReadOnlyProperty<long[]>))
102102
.FirstOrDefault(m => m.Name.StartsWith("Value"));
103103

@@ -110,7 +110,7 @@ public void ShouldFindAReadOnlyArrayProperty()
110110
[Fact]
111111
public void ShouldIgnoreANonPublicField()
112112
{
113-
var member = MemberFinder
113+
var member = MemberCache
114114
.GetTargetMembers(typeof(InternalField<List<byte>>))
115115
.FirstOrDefault(m => m.Name == "Value");
116116

@@ -120,7 +120,7 @@ public void ShouldIgnoreANonPublicField()
120120
[Fact]
121121
public void ShouldIgnoreAGetMethod()
122122
{
123-
var member = MemberFinder
123+
var member = MemberCache
124124
.GetTargetMembers(typeof(PublicGetMethod<string[]>))
125125
.FirstOrDefault(m => m.Name == "Value");
126126

@@ -130,7 +130,7 @@ public void ShouldIgnoreAGetMethod()
130130
[Fact]
131131
public void ShouldIgnoreAPropertySetter()
132132
{
133-
var member = MemberFinder
133+
var member = MemberCache
134134
.GetTargetMembers(typeof(PublicProperty<long>))
135135
.FirstOrDefault(m => m.Name.StartsWith("set_"));
136136

0 commit comments

Comments
 (0)