diff --git a/AgileMapper.PerformanceTester.Net45/AgileMapper.PerformanceTester.Net45.csproj b/AgileMapper.PerformanceTester.Net45/AgileMapper.PerformanceTester.Net45.csproj
index f0c622748..cf7abc181 100644
--- a/AgileMapper.PerformanceTester.Net45/AgileMapper.PerformanceTester.Net45.csproj
+++ b/AgileMapper.PerformanceTester.Net45/AgileMapper.PerformanceTester.Net45.csproj
@@ -37,8 +37,8 @@
..\packages\AgileObjects.NetStandardPolyfills.1.4.0\lib\net40\AgileObjects.NetStandardPolyfills.dll
-
- ..\packages\AgileObjects.ReadableExpressions.2.0.0\lib\net40\AgileObjects.ReadableExpressions.dll
+
+ ..\packages\AgileObjects.ReadableExpressions.2.1.0\lib\net40\AgileObjects.ReadableExpressions.dll
..\packages\AutoMapper.7.0.1\lib\net45\AutoMapper.dll
diff --git a/AgileMapper.PerformanceTester.Net45/packages.config b/AgileMapper.PerformanceTester.Net45/packages.config
index da651426a..92d57fe9a 100644
--- a/AgileMapper.PerformanceTester.Net45/packages.config
+++ b/AgileMapper.PerformanceTester.Net45/packages.config
@@ -1,7 +1,7 @@
-
+
diff --git a/AgileMapper.UnitTests.Common/ShouldExtensions.cs b/AgileMapper.UnitTests.Common/ShouldExtensions.cs
index 59b3843fc..4fd39e6de 100644
--- a/AgileMapper.UnitTests.Common/ShouldExtensions.cs
+++ b/AgileMapper.UnitTests.Common/ShouldExtensions.cs
@@ -400,7 +400,7 @@ public static IDictionary ShouldContainKeyAndValue(
Asplode("Dictionary with key " + expectedKey, "No contained key");
}
- value.ShouldBeSameAs(expectedValue);
+ value.ShouldBe(expectedValue);
return dictionary;
}
diff --git a/AgileMapper.UnitTests.Net35/AgileMapper.UnitTests.Net35.csproj b/AgileMapper.UnitTests.Net35/AgileMapper.UnitTests.Net35.csproj
index fde472b1b..0e0c4f28a 100644
--- a/AgileMapper.UnitTests.Net35/AgileMapper.UnitTests.Net35.csproj
+++ b/AgileMapper.UnitTests.Net35/AgileMapper.UnitTests.Net35.csproj
@@ -37,8 +37,8 @@
..\packages\AgileObjects.NetStandardPolyfills.1.4.0\lib\net35\AgileObjects.NetStandardPolyfills.dll
-
- ..\packages\AgileObjects.ReadableExpressions.2.0.0\lib\net35\AgileObjects.ReadableExpressions.dll
+
+ ..\packages\AgileObjects.ReadableExpressions.2.1.0\lib\net35\AgileObjects.ReadableExpressions.dll
..\packages\DynamicLanguageRuntime.1.1.2\lib\Net35\Microsoft.Dynamic.dll
diff --git a/AgileMapper.UnitTests.Net35/packages.config b/AgileMapper.UnitTests.Net35/packages.config
index bd262d035..e4f4503b6 100644
--- a/AgileMapper.UnitTests.Net35/packages.config
+++ b/AgileMapper.UnitTests.Net35/packages.config
@@ -1,7 +1,7 @@
-
+
diff --git a/AgileMapper.UnitTests.NonParallel/AgileMapper.UnitTests.NonParallel.csproj b/AgileMapper.UnitTests.NonParallel/AgileMapper.UnitTests.NonParallel.csproj
index 85845179b..1fc3c9e92 100644
--- a/AgileMapper.UnitTests.NonParallel/AgileMapper.UnitTests.NonParallel.csproj
+++ b/AgileMapper.UnitTests.NonParallel/AgileMapper.UnitTests.NonParallel.csproj
@@ -42,8 +42,8 @@
..\packages\AgileObjects.NetStandardPolyfills.1.4.0\lib\net40\AgileObjects.NetStandardPolyfills.dll
-
- ..\packages\AgileObjects.ReadableExpressions.2.0.0\lib\net40\AgileObjects.ReadableExpressions.dll
+
+ ..\packages\AgileObjects.ReadableExpressions.2.1.0\lib\net40\AgileObjects.ReadableExpressions.dll
@@ -75,6 +75,7 @@
VersionInfo.cs
+
diff --git a/AgileMapper.UnitTests.NonParallel/Configuration/Inline/WhenConfiguringDerivedTypesInline.cs b/AgileMapper.UnitTests.NonParallel/Configuration/Inline/WhenConfiguringDerivedTypesInline.cs
new file mode 100644
index 000000000..ab9d772d0
--- /dev/null
+++ b/AgileMapper.UnitTests.NonParallel/Configuration/Inline/WhenConfiguringDerivedTypesInline.cs
@@ -0,0 +1,26 @@
+namespace AgileObjects.AgileMapper.UnitTests.NonParallel.Configuration.Inline
+{
+ using Common;
+ using MoreTestClasses;
+ using NetStandardPolyfills;
+ using TestClasses;
+ using Xunit;
+
+ public class WhenConfiguringDerivedTypesInline : NonParallelTestsBase
+ {
+ [Fact]
+ public void ShouldScanConfiguredAssembliesInline()
+ {
+ TestThenReset(() =>
+ {
+ var result = Mapper
+ .Map(new { NumberOfLegs = 100, SlitherNoise = "ththtth" })
+ .Over(new Earthworm() as AnimalBase, cgf => cgf
+ .LookForDerivedTypesIn(typeof(Dog).GetAssembly(), typeof(Earthworm).GetAssembly()));
+
+ result.NumberOfLegs.ShouldBe(100);
+ ((Earthworm)result).SlitherNoise.ShouldBe("ththtth");
+ });
+ }
+ }
+}
diff --git a/AgileMapper.UnitTests.NonParallel/Configuration/WhenConfiguringDerivedTypes.cs b/AgileMapper.UnitTests.NonParallel/Configuration/WhenConfiguringDerivedTypes.cs
index e44c49977..0f40f4428 100644
--- a/AgileMapper.UnitTests.NonParallel/Configuration/WhenConfiguringDerivedTypes.cs
+++ b/AgileMapper.UnitTests.NonParallel/Configuration/WhenConfiguringDerivedTypes.cs
@@ -2,13 +2,14 @@
{
using Common;
using MoreTestClasses;
+ using NetStandardPolyfills;
using TestClasses;
using Xunit;
public class WhenConfiguringDerivedTypes : NonParallelTestsBase
{
[Fact]
- public void ShouldScanConfiguredAssemblies()
+ public void ShouldScanConfiguredAssembliesViaTheStaticApi()
{
TestThenReset(() =>
{
@@ -30,5 +31,57 @@ public void ShouldScanConfiguredAssemblies()
((Earthworm)wormResult).SlitherNoise.ShouldBe("sssSSS");
});
}
+
+ [Fact]
+ public void ShouldScanConfiguredAssembliesViaTheInstanceApi()
+ {
+ TestThenReset(() =>
+ {
+ using (var mapper = Mapper.CreateNew())
+ {
+ mapper.WhenMapping
+ .LookForDerivedTypesIn(typeof(Dog).GetAssembly(), typeof(Earthworm).GetAssembly());
+
+ var result = mapper
+ .Map(new { NumberOfLegs = 1000, SlitherNoise = "thththtth" })
+ .Over(new Earthworm() as AnimalBase);
+
+ result.NumberOfLegs.ShouldBe(1000);
+ ((Earthworm)result).SlitherNoise.ShouldBe("thththtth");
+ }
+ });
+ }
+
+ [Fact]
+ public void ShouldSetAssembliesToScanGlobally()
+ {
+ TestThenReset(() =>
+ {
+ using (var mapper1 = Mapper.CreateNew())
+ using (var mapper2 = Mapper.CreateNew())
+ {
+ // Set assembly scanning on mapper1...
+ mapper1.WhenMapping
+ .LookForDerivedTypesIn(typeof(Dog).GetAssembly(), typeof(Earthworm).GetAssembly());
+
+ // ...use mapper2 to cache the assembly scan results...
+ var result1 = mapper2
+ .Map(new { NumberOfLegs = 4, WoofSound = "AWESOME" })
+ .OnTo(new Dog() as AnimalBase);
+
+ result1.NumberOfLegs.ShouldBe(4);
+ ((Dog)result1).WoofSound.ShouldBe("AWESOME");
+
+ // ...use mapper1 with a type outside the base type's assembly;
+ // assemblies are cached globally, so the scan settings should be too:
+ var result2 = mapper1
+ .Map(new { NumberOfLegs = 100, SlitherNoise = "SLITHERRR" })
+ .OnTo(new Earthworm() as AnimalBase);
+
+ result2.NumberOfLegs.ShouldBe(100);
+ ((Earthworm)result2).SlitherNoise.ShouldBe("SLITHERRR");
+ }
+ });
+ }
}
}
diff --git a/AgileMapper.UnitTests.NonParallel/packages.config b/AgileMapper.UnitTests.NonParallel/packages.config
index 68f01a437..574dc374e 100644
--- a/AgileMapper.UnitTests.NonParallel/packages.config
+++ b/AgileMapper.UnitTests.NonParallel/packages.config
@@ -1,7 +1,7 @@
-
+
diff --git a/AgileMapper.UnitTests/AgileMapper.UnitTests.csproj b/AgileMapper.UnitTests/AgileMapper.UnitTests.csproj
index ef2f96729..9a22ca05a 100644
--- a/AgileMapper.UnitTests/AgileMapper.UnitTests.csproj
+++ b/AgileMapper.UnitTests/AgileMapper.UnitTests.csproj
@@ -44,8 +44,8 @@
..\packages\AgileObjects.NetStandardPolyfills.1.4.0\lib\net40\AgileObjects.NetStandardPolyfills.dll
-
- ..\packages\AgileObjects.ReadableExpressions.2.0.0\lib\net40\AgileObjects.ReadableExpressions.dll
+
+ ..\packages\AgileObjects.ReadableExpressions.2.1.0\lib\net40\AgileObjects.ReadableExpressions.dll
..\packages\Microsoft.Extensions.Primitives.2.0.0\lib\netstandard2.0\Microsoft.Extensions.Primitives.dll
diff --git a/AgileMapper.UnitTests/Configuration/Inline/WhenConfiguringDataSourcesInlineIncorrectly.cs b/AgileMapper.UnitTests/Configuration/Inline/WhenConfiguringDataSourcesInlineIncorrectly.cs
index 18d12b785..b19c35e01 100644
--- a/AgileMapper.UnitTests/Configuration/Inline/WhenConfiguringDataSourcesInlineIncorrectly.cs
+++ b/AgileMapper.UnitTests/Configuration/Inline/WhenConfiguringDataSourcesInlineIncorrectly.cs
@@ -28,7 +28,7 @@ public void ShouldErrorIfUnconvertibleConstantSpecifiedInline()
}
});
- inlineConfigEx.Message.ShouldContain("Unable to convert configured decimal? ");
+ inlineConfigEx.Message.ShouldContain("Unable to convert configured 'decimal?' ");
}
[Fact]
diff --git a/AgileMapper.UnitTests/Configuration/Inline/WhenConfiguringDerivedTypesInline.cs b/AgileMapper.UnitTests/Configuration/Inline/WhenConfiguringDerivedTypesInline.cs
index 7dcd33e42..405697830 100644
--- a/AgileMapper.UnitTests/Configuration/Inline/WhenConfiguringDerivedTypesInline.cs
+++ b/AgileMapper.UnitTests/Configuration/Inline/WhenConfiguringDerivedTypesInline.cs
@@ -2,8 +2,6 @@
{
using System.Linq;
using Common;
- using MoreTestClasses;
- using NetStandardPolyfills;
using TestClasses;
#if !NET35
using Xunit;
@@ -112,20 +110,5 @@ public void ShouldMapACustomTypePairInACollectionInline()
mapper.InlineContexts().Count.ShouldBe(2);
}
}
-
- [Fact]
- public void ShouldScanConfiguredAssembliesInline()
- {
- using (var mapper = Mapper.CreateNew())
- {
- var result = mapper
- .Map(new { NumberOfLegs = 100, SlitherNoise = "ththtth" })
- .Over(new Earthworm() as AnimalBase, cgf => cgf
- .LookForDerivedTypesIn(typeof(Dog).GetAssembly(), typeof(Earthworm).GetAssembly()));
-
- result.NumberOfLegs.ShouldBe(100);
- ((Earthworm)result).SlitherNoise.ShouldBe("ththtth");
- }
- }
}
}
diff --git a/AgileMapper.UnitTests/Configuration/WhenConfiguringDataSources.cs b/AgileMapper.UnitTests/Configuration/WhenConfiguringDataSources.cs
index 14df549cb..574987103 100644
--- a/AgileMapper.UnitTests/Configuration/WhenConfiguringDataSources.cs
+++ b/AgileMapper.UnitTests/Configuration/WhenConfiguringDataSources.cs
@@ -148,6 +148,92 @@ public void ShouldApplyMultipleConfiguredMembersBySourceType()
}
}
+ // See https://github.com/agileobjects/AgileMapper/issues/111
+ [Fact]
+ public void ShouldConditionallyApplyAToTargetConfiguredSimpleTypeConstant()
+ {
+ using (var mapper = Mapper.CreateNew())
+ {
+ mapper.WhenMapping
+ .From().ToANew()
+ .If(ctx => string.IsNullOrEmpty(ctx.Source))
+ .Map(default(string)).ToTarget();
+
+ var source = new Address { Line1 = "Here", Line2 = string.Empty };
+ var result = mapper.Map(source).ToANew();
+
+ result.Line1.ShouldBe("Here");
+ result.Line2.ShouldBeNull();
+ }
+ }
+
+ [Fact]
+ public void ShouldApplyAToTargetConfiguredSimpleTypeConstant()
+ {
+ using (var mapper = Mapper.CreateNew())
+ {
+ mapper.WhenMapping
+ .From().ToANew()
+ .Map((s, t) => string.IsNullOrEmpty(s) ? null : s).ToTarget();
+
+ var source = new Address { Line1 = "There", Line2 = string.Empty };
+ var result = mapper.Map(source).ToANew();
+
+ result.Line1.ShouldBe("There");
+ result.Line2.ShouldBeNull();
+ }
+ }
+
+ [Fact]
+ public void ShouldConditionallyApplyAToTargetConfiguredNestedSimpleTypeExpression()
+ {
+ using (var mapper = Mapper.CreateNew())
+ {
+ mapper.WhenMapping
+ .From().ToANew()
+ .If(ctx => ctx.Source % 2 == 0)
+ .Map(ctx => ctx.Source * 2).ToTarget();
+
+ var nonMatchingSource = new { ValueValue = 3 };
+ var nonMatchingResult = mapper.Map(nonMatchingSource).ToANew>>();
+
+ nonMatchingResult.Value.ShouldNotBeNull();
+ nonMatchingResult.Value.Value.ShouldBe(3);
+
+ var matchingSource = new { ValueValue = 4 };
+ var matchingResult = mapper.Map(matchingSource).ToANew>>();
+
+ matchingResult.Value.ShouldNotBeNull();
+ matchingResult.Value.Value.ShouldBe(8);
+ }
+ }
+
+ [Fact]
+ public void ShouldConditionallyApplyAToTargetConfiguredSimpleTypeExpressionInAComplexTypeList()
+ {
+ using (var mapper = Mapper.CreateNew())
+ {
+ mapper.WhenMapping
+ .From().ToANew()
+ .If((s, t) => s % 2 == 0)
+ .Map(ctx => ctx.Source * 2).ToTarget();
+
+ var source = new PublicField>>
+ {
+ Value = new List>
+ {
+ new PublicField { Value = 1 },
+ new PublicField { Value = 2 },
+ new PublicField { Value = 3 }
+ }
+ };
+ var result = mapper.Map(source).ToANew>>>();
+
+ result.Value.ShouldNotBeNull();
+ result.Value.ShouldBe(pf => pf.Value, 1, 4, 3);
+ }
+ }
+
[Fact]
public void ShouldConditionallyApplyAConfiguredMember()
{
@@ -740,6 +826,62 @@ public void ShouldApplyAConfiguredComplexTypeEnumerableConditionally()
}
}
+ // See https://github.com/agileobjects/AgileMapper/issues/113
+ [Fact]
+ public void ShouldApplyAConfiguredComplexToSimpleTypeEnumerableProjection()
+ {
+ using (var mapper = Mapper.CreateNew())
+ {
+ mapper.WhenMapping
+ .From[]>>()
+ .To>()
+ .Map(
+ pfpfi => pfpfi.Value.Select(v => v.Value),
+ pfi => pfi.Value);
+
+ var source = new PublicField[]>
+ {
+ Value = new[]
+ {
+ new PublicField { Value = 1 },
+ new PublicField { Value = 2 },
+ new PublicField { Value = 3 }
+ }
+ };
+
+ var result = mapper.Map(source).ToANew>();
+
+ result.Value.ShouldNotBeEmpty();
+ result.Value.ShouldBe(1, 2, 3);
+ }
+ }
+
+ // See https://github.com/agileobjects/AgileMapper/issues/113
+ [Fact]
+ public void ShouldApplyAConfiguredComplexToSimpleTypeEnumerableProjectionToTheRootTarget()
+ {
+ using (var mapper = Mapper.CreateNew())
+ {
+ mapper.WhenMapping
+ .From[]>()
+ .To()
+ .Map(ctx => ctx.Source.Select(v => v.Value))
+ .ToTarget();
+
+ var source = new[]
+ {
+ new PublicField { Value = 1 },
+ new PublicField { Value = 2 },
+ new PublicField { Value = 3 }
+ };
+
+ var result = mapper.Map(source).ToANew();
+
+ result.ShouldNotBeEmpty();
+ result.ShouldBe(1, 2, 3);
+ }
+ }
+
[Fact]
public void ShouldApplyAConfiguredSourceAndTargetFunction()
{
diff --git a/AgileMapper.UnitTests/Configuration/WhenConfiguringDataSourcesIncorrectly.cs b/AgileMapper.UnitTests/Configuration/WhenConfiguringDataSourcesIncorrectly.cs
index 2eddf7b99..4cb2ce33a 100644
--- a/AgileMapper.UnitTests/Configuration/WhenConfiguringDataSourcesIncorrectly.cs
+++ b/AgileMapper.UnitTests/Configuration/WhenConfiguringDataSourcesIncorrectly.cs
@@ -300,6 +300,24 @@ public void ShouldErrorIfSimpleTypeConfiguredForEnumerableTarget()
"PublicField.Value of type 'int' cannot be mapped to target type 'int[]'");
}
+ [Fact]
+ public void ShouldErrorIfUnconvertibleEnumerableElementTypeConfigured()
+ {
+ var configurationException = Should.Throw(() =>
+ {
+ using (var mapper = Mapper.CreateNew())
+ {
+ mapper.WhenMapping
+ .From[]>>()
+ .To>()
+ .Map(s => s.Value, t => t.Value);
+ }
+ });
+
+ configurationException.Message.ShouldContain(
+ "Unable to convert configured 'PublicField' to target type 'int'");
+ }
+
[Fact]
public void ShouldErrorIfTargetParameterConfiguredAsTarget()
{
@@ -319,7 +337,7 @@ public void ShouldErrorIfTargetParameterConfiguredAsTarget()
}
[Fact]
- public void ShouldErrorIfRootTargetSimpleTypeConstantDataSourceConfigured()
+ public void ShouldErrorIfSimpleTypeConstantConfiguredForRootTarget()
{
var configurationException = Should.Throw(() =>
{
@@ -338,7 +356,7 @@ public void ShouldErrorIfRootTargetSimpleTypeConstantDataSourceConfigured()
}
[Fact]
- public void ShouldErrorIfRootTargetSimpleTypeMemberDataSourceConfigured()
+ public void ShouldErrorIfSimpleTypeMemberConfiguredForRootTarget()
{
var configurationException = Should.Throw(() =>
{
@@ -357,7 +375,7 @@ public void ShouldErrorIfRootTargetSimpleTypeMemberDataSourceConfigured()
}
[Fact]
- public void ShouldErrorIfRootEnumerableTargetNonEnumerableTypeMemberDataSourceConfigured()
+ public void ShouldErrorIfNonEnumerableTypeMemberConfiguredForRootEnumerableTarget()
{
var configurationException = Should.Throw(() =>
{
@@ -376,7 +394,7 @@ public void ShouldErrorIfRootEnumerableTargetNonEnumerableTypeMemberDataSourceCo
}
[Fact]
- public void ShouldErrorIfRootNonEnumerableTargetEnumerableTypeMemberDataSourceConfigured()
+ public void ShouldErrorIfEnumerableTypeMemberConfiguredForRootNonEnumerableTarget()
{
var configurationException = Should.Throw(() =>
{
@@ -394,6 +412,25 @@ public void ShouldErrorIfRootNonEnumerableTargetEnumerableTypeMemberDataSourceCo
configurationException.Message.ShouldContain("cannot be mapped to non-enumerable target type 'PublicProperty'");
}
+ [Fact]
+ public void ShouldErrorIfUnconvertibleEnumerableElementTypeConfiguredForRootTarget()
+ {
+ var configurationException = Should.Throw(() =>
+ {
+ using (var mapper = Mapper.CreateNew())
+ {
+ mapper.WhenMapping
+ .From[]>>()
+ .To()
+ .Map(ctx => ctx.Source.Value)
+ .ToTarget();
+ }
+ });
+
+ configurationException.Message.ShouldContain(
+ "Unable to convert configured 'PublicField' to target type 'decimal'");
+ }
+
[Fact]
public void ShouldErrorIfConstantSpecifiedForTargetMember()
{
diff --git a/AgileMapper.UnitTests/Configuration/WhenConfiguringDerivedTypes.cs b/AgileMapper.UnitTests/Configuration/WhenConfiguringDerivedTypes.cs
index 16bf2ebee..134745b89 100644
--- a/AgileMapper.UnitTests/Configuration/WhenConfiguringDerivedTypes.cs
+++ b/AgileMapper.UnitTests/Configuration/WhenConfiguringDerivedTypes.cs
@@ -2,8 +2,6 @@
{
using AgileMapper.Extensions.Internal;
using Common;
- using MoreTestClasses;
- using NetStandardPolyfills;
using TestClasses;
#if !NET35
using Xunit;
@@ -14,52 +12,6 @@
#endif
public class WhenConfiguringDerivedTypes
{
- [Fact]
- public void ShouldScanConfiguredAssemblies()
- {
- using (var mapper = Mapper.CreateNew())
- {
- mapper.WhenMapping
- .LookForDerivedTypesIn(typeof(Dog).GetAssembly(), typeof(Earthworm).GetAssembly());
-
- var result = mapper
- .Map(new { NumberOfLegs = 1000, SlitherNoise = "thththtth" })
- .Over(new Earthworm() as AnimalBase);
-
- result.NumberOfLegs.ShouldBe(1000);
- ((Earthworm)result).SlitherNoise.ShouldBe("thththtth");
- }
- }
-
- [Fact]
- public void ShouldSetAssembliesToScanGlobally()
- {
- using (var mapper1 = Mapper.CreateNew())
- using (var mapper2 = Mapper.CreateNew())
- {
- // Set assembly scanning on mapper1...
- mapper1.WhenMapping
- .LookForDerivedTypesIn(typeof(Dog).GetAssembly(), typeof(Earthworm).GetAssembly());
-
- // ...use mapper2 to cache the assembly scan results...
- var result1 = mapper2
- .Map(new { NumberOfLegs = 4, WoofSound = "AWESOME" })
- .OnTo(new Dog() as AnimalBase);
-
- result1.NumberOfLegs.ShouldBe(4);
- ((Dog)result1).WoofSound.ShouldBe("AWESOME");
-
- // ...use mapper1 with a type outside the base type's assembly;
- // assemblies are cached globally, so the scan settings should be too:
- var result2 = mapper1
- .Map(new { NumberOfLegs = 100, SlitherNoise = "SLITHERRR" })
- .OnTo(new Earthworm() as AnimalBase);
-
- result2.NumberOfLegs.ShouldBe(100);
- ((Earthworm)result2).SlitherNoise.ShouldBe("SLITHERRR");
- }
- }
-
[Fact]
public void ShouldMapACustomTypePair()
{
diff --git a/AgileMapper.UnitTests/Configuration/WhenConfiguringObjectCreation.cs b/AgileMapper.UnitTests/Configuration/WhenConfiguringObjectCreation.cs
index a6a1aefe7..8b88d5ee6 100644
--- a/AgileMapper.UnitTests/Configuration/WhenConfiguringObjectCreation.cs
+++ b/AgileMapper.UnitTests/Configuration/WhenConfiguringObjectCreation.cs
@@ -526,6 +526,24 @@ public void ShouldErrorIfConflictingFactoryConfigured()
factoryEx.Message.ShouldContain("has already been configured");
}
+ // See https://github.com/agileobjects/AgileMapper/issues/114
+ [Fact]
+ public void ShouldErrorIfPrimitiveTargetTypeSpecified()
+ {
+ var factoryEx = Should.Throw(() =>
+ {
+ using (var mapper = Mapper.CreateNew())
+ {
+ mapper.WhenMapping
+ .From()
+ .To()
+ .CreateInstancesUsing(ctx => 123);
+ }
+ });
+
+ factoryEx.Message.ShouldContain("primitive type 'int'");
+ }
+
#region Helper Classes
private class CustomerCtor : Person
diff --git a/AgileMapper.UnitTests/Configuration/WhenMappingToNull.cs b/AgileMapper.UnitTests/Configuration/WhenMappingToNull.cs
index 2a8864e33..87087d4ff 100644
--- a/AgileMapper.UnitTests/Configuration/WhenMappingToNull.cs
+++ b/AgileMapper.UnitTests/Configuration/WhenMappingToNull.cs
@@ -2,6 +2,7 @@
{
using System;
using System.Collections.Generic;
+ using System.Collections.ObjectModel;
using System.Linq;
using AgileMapper.Configuration;
using AgileMapper.Extensions.Internal;
@@ -186,6 +187,43 @@ public void ShouldMapCollectionElementNestedPropertiesToNull()
}
}
+ [Fact]
+ public void ShouldMapCollectionsToNullIfConfiguredGlobally()
+ {
+ using (var mapper = Mapper.CreateNew())
+ {
+ mapper.WhenMapping
+ .MapNullCollectionsToNull();
+
+ var source = new PublicField> { Value = null };
+ var result = mapper.Map(source).ToANew>>();
+
+ result.Value.ShouldBeNull();
+ }
+ }
+
+ [Fact]
+ public void ShouldMapCollectionsToNullIfConfiguredByType()
+ {
+ using (var mapper = Mapper.CreateNew())
+ {
+ mapper.WhenMapping
+ .From>>()
+ .To>>()
+ .MapNullCollectionsToNull();
+
+ var matchingSource = new PublicProperty> { Value = null };
+ var matchingResult = mapper.Map(matchingSource).ToANew>>();
+
+ matchingResult.Value.ShouldBeNull();
+
+ var nonMatchingSource = new PublicProperty> { Value = null };
+ var nonMatchingResult = mapper.Map(nonMatchingSource).ToANew>>();
+
+ nonMatchingResult.Value.ShouldBeEmpty();
+ }
+ }
+
[Fact]
public void ShouldErrorIfConditionsAreConfiguredForTheSameType()
{
diff --git a/AgileMapper.UnitTests/Dictionaries/WhenMappingToNewDictionaryMembers.cs b/AgileMapper.UnitTests/Dictionaries/WhenMappingToNewDictionaryMembers.cs
index 78e8b9664..591d2ce36 100644
--- a/AgileMapper.UnitTests/Dictionaries/WhenMappingToNewDictionaryMembers.cs
+++ b/AgileMapper.UnitTests/Dictionaries/WhenMappingToNewDictionaryMembers.cs
@@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
+ using AgileMapper.Extensions;
using Common;
using TestClasses;
#if !NET35
@@ -105,6 +106,7 @@ public void ShouldMapANestedEnumerableOfArraysToANestedEnumerableTypedDictionary
Value = new[]
{
new [] { 1, 2, 3 },
+ null,
new [] { 4, 5, 6 }
}
};
@@ -113,7 +115,8 @@ public void ShouldMapANestedEnumerableOfArraysToANestedEnumerableTypedDictionary
result.Value.ShouldNotContainKey("[0][0]");
result.Value["[0]"].ShouldBe("1", "2", "3");
- result.Value["[1]"].ShouldBe("4", "5", "6");
+ result.Value["[1]"].ShouldBeDefault();
+ result.Value["[2]"].ShouldBe("4", "5", "6");
}
// See https://github.com/agileobjects/AgileMapper/issues/8
@@ -160,6 +163,34 @@ public void ShouldMapADictionaryMemberToANewDictionaryMember()
((CustomerViewModel)result.Value["Object"]).Name.ShouldBe("Mr Yo Yo");
}
+ // See https://github.com/agileobjects/AgileMapper/issues/110
+ [Fact]
+ public void ShouldMapSimpleTypeObjectValuesToSimpleTypeObjectValues()
+ {
+ var source = new PublicField>
+ {
+ Value = new Dictionary
+ {
+ { "int", 1 },
+ { "double", 1.0 },
+ { "decimal", 1m },
+ { "string", "hello" },
+ { "bool", true }
+ }
+ };
+
+ var result = Mapper.Map(source).ToANew>>();
+
+ result.Value.ShouldNotBeNull();
+ result.Value.ShouldNotBeSameAs(source.Value);
+ result.Value.Count.ShouldBe(source.Value.Count);
+ result.Value.ShouldContainKeyAndValue("int", 1);
+ result.Value.ShouldContainKeyAndValue("double", 1.0);
+ result.Value.ShouldContainKeyAndValue("decimal", 1m);
+ result.Value.ShouldContainKeyAndValue("string", "hello");
+ result.Value.ShouldContainKeyAndValue("bool", true);
+ }
+
// See https://github.com/agileobjects/AgileMapper/issues/10
[Fact]
public void ShouldMapADictionaryObjectValuesToNewDictionaryObjectValues()
@@ -210,6 +241,40 @@ public void ShouldUseACloneConstructorToPopulateADictionaryConstructorParameter(
result.Value["Test"].ShouldBe("Hello!");
}
+ // See https://github.com/agileobjects/AgileMapper/issues/110
+ [Fact]
+ public void ShouldCloneSimpleTypeValuesInAnObjectDictionary()
+ {
+ var source = new PublicTwoFields>
+ {
+ Value1 = 6372,
+ Value2 = new Dictionary
+ {
+ ["QueryName"] = "References",
+ ["IsDefault"] = false,
+ ["QueryId"] = 155,
+ ["WorkspaceTypeId"] = 1,
+ ["IsUserDefined"] = true,
+ ["QueryTypeId"] = 2,
+ ["Test"] = default(int?)
+ }
+ };
+
+ var result = source.DeepClone();
+
+ result.Value1.ShouldBe(6372);
+ result.Value2.ShouldNotBeNull();
+ result.Value2.ShouldNotBeSameAs(source.Value2);
+ result.Value2.Count.ShouldBe(source.Value2.Count);
+ result.Value2.ShouldContainKeyAndValue("QueryName", "References");
+ result.Value2.ShouldContainKeyAndValue("IsDefault", false);
+ result.Value2.ShouldContainKeyAndValue("QueryId", 155);
+ result.Value2.ShouldContainKeyAndValue("WorkspaceTypeId", 1);
+ result.Value2.ShouldContainKeyAndValue("IsUserDefined", true);
+ result.Value2.ShouldContainKeyAndValue("QueryTypeId", 2);
+ result.Value2.ShouldContainKeyAndValue("Test", null);
+ }
+
[Fact]
public void ShouldNotCreateDictionaryAsFallbackComplexType()
{
@@ -239,6 +304,7 @@ public void ShouldFlattenAComplexTypeCollectionToANestedObjectDictionaryImplemen
Line2 = "That place",
}
},
+ default(Customer),
new MysteryCustomer
{
Id = Guid.NewGuid(),
@@ -261,14 +327,14 @@ public void ShouldFlattenAComplexTypeCollectionToANestedObjectDictionaryImplemen
result.Value["[0].Address.Line1"].ShouldBe("This place");
result.Value["[0].Address.Line2"].ShouldBe("That place");
- result.Value["[1].Id"].ShouldBe(source.Value.Second().Id);
- result.Value["[1].Title"].ShouldBe(Title.Dr);
- result.Value["[1].Name"].ShouldBe("Customer 2");
- result.Value["[1].Discount"].ShouldBe(0.3m);
- result.Value["[1].Report"].ShouldBe("It was all a mystery :o");
- result.Value.ShouldNotContainKey("[1].Address");
- result.Value.ShouldNotContainKey("[1].Address.Line1");
- result.Value.ShouldNotContainKey("[1].Address.Line2");
+ result.Value["[2].Id"].ShouldBe(source.Value.Third().Id);
+ result.Value["[2].Title"].ShouldBe(Title.Dr);
+ result.Value["[2].Name"].ShouldBe("Customer 2");
+ result.Value["[2].Discount"].ShouldBe(0.3m);
+ result.Value["[2].Report"].ShouldBe("It was all a mystery :o");
+ result.Value.ShouldNotContainKey("[2].Address");
+ result.Value.ShouldNotContainKey("[2].Address.Line1");
+ result.Value.ShouldNotContainKey("[2].Address.Line2");
}
diff --git a/AgileMapper.UnitTests/SimpleTypeConversion/WhenConvertingToInts.cs b/AgileMapper.UnitTests/SimpleTypeConversion/WhenConvertingToInts.cs
index 0db0937bc..c287fc2e2 100644
--- a/AgileMapper.UnitTests/SimpleTypeConversion/WhenConvertingToInts.cs
+++ b/AgileMapper.UnitTests/SimpleTypeConversion/WhenConvertingToInts.cs
@@ -324,5 +324,43 @@ public void ShouldMapAStringEnumerableToAnIntEnumerable()
result.ShouldBe(1, 2, 3);
}
+
+ // See https://github.com/agileobjects/AgileMapper/issues/114
+ [Fact]
+ public void ShouldUseUserDefinedToIntOperators()
+ {
+ var source = new PublicField { Value = new UserId(123) };
+ var result = Mapper.Map(source).ToANew>();
+
+ result.Value.ShouldBe(123);
+ }
+
+ [Fact]
+ public void ShouldUseUserDefinedFromIntOperators()
+ {
+ var source = new PublicField { Value = 456 };
+ var result = Mapper.Map(source).ToANew>();
+
+ result.Value.Equals(new UserId(456)).ShouldBeTrue();
+ }
+
+ #region Helper Members
+
+ private struct UserId
+ {
+ private readonly int _value;
+
+ public UserId(int value) => _value = value;
+
+ public static implicit operator UserId(int value) => new UserId(value);
+ public static explicit operator int(UserId userId) => userId._value;
+
+ public override bool Equals(object obj)
+ => obj is UserId other && _value.Equals(other._value);
+
+ public override int GetHashCode() => _value.GetHashCode();
+ }
+
+ #endregion
}
}
diff --git a/AgileMapper.UnitTests/WhenMappingCircularReferences.cs b/AgileMapper.UnitTests/WhenMappingCircularReferences.cs
index eca776380..530162687 100644
--- a/AgileMapper.UnitTests/WhenMappingCircularReferences.cs
+++ b/AgileMapper.UnitTests/WhenMappingCircularReferences.cs
@@ -772,6 +772,51 @@ public void ShouldNotMapAnEmptyNestedRecursiveParentMember()
result.Value.Parent.ShouldBeNull();
}
+ [Fact]
+ public void ShouldPerformRepeatedComplexTypeMemberAndElementMappings()
+ {
+ var c = new Issue115.C { Id = 222 };
+
+ var source = new Issue115.A
+ {
+ Id = 1,
+ B = new Issue115.B
+ {
+ Id = 11,
+ Cs = new[]
+ {
+ new Issue115.C
+ {
+ Id = 111,
+ Parent = c
+ }
+ }
+ },
+ C = new Issue115.C
+ {
+ Id = 12,
+ Parent = c
+ }
+ };
+
+ var result = Mapper.Map(source).ToANew();
+
+ result.Id.ShouldBe(1);
+
+ result.B.ShouldNotBeNull();
+ result.B.Id.ShouldBe(11);
+ result.B.Cs.ShouldHaveSingleItem();
+ result.B.Cs[0].Parent.ShouldNotBeNull();
+ result.B.Cs[0].Parent.Id.ShouldBe(222);
+ result.B.Cs[0].Parent.Parent.ShouldBeNull();
+
+ result.C.ShouldNotBeNull();
+ result.C.Id.ShouldBe(12);
+ result.C.Parent.ShouldNotBeNull();
+ result.C.Parent.Id.ShouldBe(222);
+ result.C.Parent.Parent.ShouldBeNull();
+ }
+
[Fact]
public void ShouldGenerateAMappingPlanForLinkRelationships()
{
@@ -1131,6 +1176,57 @@ internal class WarehouseProduct : EntityBase
}
}
+ public static class Issue115
+ {
+ public class A
+ {
+ public int Id { get; set; }
+
+ public B B { get; set; }
+
+ public C C { get; set; }
+ }
+
+ public class B
+ {
+ public int Id { get; set; }
+
+ public C[] Cs { get; set; }
+ }
+
+ public class C
+ {
+ public int Id { get; set; }
+
+ public C Parent { get; set; }
+ }
+
+ public class ADto
+ {
+ public int Id { get; set; }
+
+ // ReSharper disable MemberHidesStaticFromOuterClass
+ public BDto B { get; set; }
+
+ public CDto C { get; set; }
+ // ReSharper restore MemberHidesStaticFromOuterClass
+ }
+
+ public class BDto
+ {
+ public int Id { get; set; }
+
+ public CDto[] Cs { get; set; }
+ }
+
+ public class CDto
+ {
+ public int Id { get; set; }
+
+ public CDto Parent { get; set; }
+ }
+ }
+
#endregion
}
}
diff --git a/AgileMapper.UnitTests/WhenMappingConcurrently.cs b/AgileMapper.UnitTests/WhenMappingConcurrently.cs
index 99102c5d4..0851f5683 100644
--- a/AgileMapper.UnitTests/WhenMappingConcurrently.cs
+++ b/AgileMapper.UnitTests/WhenMappingConcurrently.cs
@@ -1,6 +1,7 @@
namespace AgileObjects.AgileMapper.UnitTests
{
using System;
+ using System.Linq;
using System.Threading.Tasks;
using Common;
using static WhenMappingCircularReferences;
@@ -54,6 +55,20 @@ public void ShouldConcurrentlyMapLargeObjectsUsingTheStaticApi()
// ReSharper disable once PossibleNullReferenceException
product.Warehouses.Count.ShouldBe(counts.Warehouses * counts.WarehouseProducts);
});
+
+ var branchMapper = Mapper
+ .Default
+ .Context
+ .ObjectMapperFactory
+ .RootMappers
+ .FirstOrDefault(mapper =>
+ (mapper.MapperData.SourceType == typeof(Issue77.Branch)) &&
+ (mapper.MapperData.TargetType == typeof(Issue77.Branch)));
+
+ branchMapper.ShouldNotBeNull();
+
+ // ReSharper disable once PossibleNullReferenceException
+ branchMapper.RepeatedMappingFuncs.Count().ShouldBe(11);
}
#region Helper Members
diff --git a/AgileMapper.UnitTests/WhenMappingToNewEnumerableMembers.cs b/AgileMapper.UnitTests/WhenMappingToNewEnumerableMembers.cs
index f2f5b6d8b..f0ebd4534 100644
--- a/AgileMapper.UnitTests/WhenMappingToNewEnumerableMembers.cs
+++ b/AgileMapper.UnitTests/WhenMappingToNewEnumerableMembers.cs
@@ -497,41 +497,117 @@ public void ShouldCreateAnEmptyCollectionByDefault()
result.Value.ShouldBeEmpty();
}
+ // See https://github.com/agileobjects/AgileMapper/issues/115
[Fact]
- public void ShouldMapCollectionsToNullIfConfiguredGlobally()
+ public void ShouldMapNestedLists()
{
- using (var mapper = Mapper.CreateNew())
+ var source = new Issue115.A2
{
- mapper.WhenMapping
- .MapNullCollectionsToNull();
+ Id = 2,
+ BB = new Issue115.B
+ {
+ Id = 11,
+ CC = new List
+ {
+ new Issue115.C
+ {
+ Id = 111,
+ DD = new Issue115.D { Id = 111 }
+ }
+ }
+ },
+ CC = new Issue115.C
+ {
+ Id = 111,
+ DD = new Issue115.D { Id = 112 }
+ }
+ };
- var source = new PublicField> { Value = null };
- var result = mapper.Map(source).ToANew>>();
+ var result = Mapper.Map(source).ToANew();
+
+ result.Id.ShouldBe(2);
+
+ result.BB.ShouldNotBeNull();
+ result.BB.Id.ShouldBe(11);
+ result.BB.CC.ShouldHaveSingleItem();
+ result.BB.CC[0].Id.ShouldBe(111);
+ result.BB.CC[0].DD.ShouldNotBeNull().Id.ShouldBe(111);
+
+ result.CC.ShouldNotBeNull();
+ result.CC.Id.ShouldBe(111);
+ result.CC.DD.ShouldNotBeNull().Id.ShouldBe(112);
- result.Value.ShouldBeNull();
- }
}
- [Fact]
- public void ShouldMapCollectionsToNullIfConfiguredByType()
+ #region Helper Members
+
+ private static class Issue115
{
- using (var mapper = Mapper.CreateNew())
+ // ReSharper disable ClassNeverInstantiated.Local
+ // ReSharper disable InconsistentNaming
+ // ReSharper disable UnusedAutoPropertyAccessor.Local
+ // ReSharper disable CollectionNeverUpdated.Local
+ public class A2
{
- mapper.WhenMapping
- .From>>()
- .To>>()
- .MapNullCollectionsToNull();
+ public int Id { get; set; }
+
+ public B BB { get; set; }
+
+ public C CC { get; set; }
+ }
+
+ public class B
+ {
+ public int Id { get; set; }
+
+ public IList CC { get; set; }
+ }
+
+ public class C
+ {
+ public int Id { get; set; }
+
+ public D DD { get; set; }
+ }
+
+ public class D
+ {
+ public int Id { get; set; }
+ }
+
+ public class A2Dto
+ {
+ public int Id { get; set; }
- var matchingSource = new PublicProperty> { Value = null };
- var matchingResult = mapper.Map(matchingSource).ToANew>>();
+ public BDto BB { get; set; }
- matchingResult.Value.ShouldBeNull();
+ public CDto CC { get; set; }
+ }
- var nonMatchingSource = new PublicProperty> { Value = null };
- var nonMatchingResult = mapper.Map(nonMatchingSource).ToANew>>();
+ public class BDto
+ {
+ public int Id { get; set; }
- nonMatchingResult.Value.ShouldBeEmpty();
+ public IList CC { get; set; }
}
+
+ public class CDto
+ {
+ public int Id { get; set; }
+
+ public DDto DD { get; set; }
+ }
+
+ public class DDto
+ {
+ public int Id { get; set; }
+ }
+ // ReSharper restore CollectionNeverUpdated.Local
+ // ReSharper restore UnusedAutoPropertyAccessor.Local
+ // ReSharper restore InconsistentNaming
+ // ReSharper restore ClassNeverInstantiated.Local
}
+
+ #endregion
}
}
diff --git a/AgileMapper.UnitTests/WhenMappingToNewEnumerables.cs b/AgileMapper.UnitTests/WhenMappingToNewEnumerables.cs
index 7b825f128..5ed6f0afb 100644
--- a/AgileMapper.UnitTests/WhenMappingToNewEnumerables.cs
+++ b/AgileMapper.UnitTests/WhenMappingToNewEnumerables.cs
@@ -152,6 +152,20 @@ public void ShouldMapToAnISet()
result.ShouldBe(1L, 2L, 3L);
}
#endif
+ [Fact]
+ public void ShouldHandleAnUnconvertibleElementType()
+ {
+ var source = new[]
+ {
+ new PublicField { Value = 1 },
+ new PublicField { Value = 2 }
+ };
+
+ var result = Mapper.Map(source).ToANew();
+
+ result.ShouldBeEmpty();
+ }
+
[Fact]
public void ShouldHandleANullComplexTypeElement()
{
@@ -169,6 +183,25 @@ public void ShouldHandleANullComplexTypeElement()
result.Second().ShouldBeNull();
}
+ [Fact]
+ public void ShouldHandleANullObjectElement()
+ {
+ var source = new List