diff --git a/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj b/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj
index 6852fcf27..363325913 100644
--- a/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj
+++ b/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj
@@ -60,9 +60,6 @@
..\packages\Microsoft.Extensions.Caching.Memory.2.0.0\lib\netstandard2.0\Microsoft.Extensions.Caching.Memory.dll
-
- ..\packages\Microsoft.Extensions.Configuration.Abstractions.2.0.0\lib\netstandard2.0\Microsoft.Extensions.Configuration.Abstractions.dll
-
..\packages\Microsoft.Extensions.DependencyInjection.2.0.0\lib\netstandard2.0\Microsoft.Extensions.DependencyInjection.dll
diff --git a/AgileMapper.UnitTests/Configuration/WhenConfiguringDerivedTypes.cs b/AgileMapper.UnitTests/Configuration/WhenConfiguringDerivedTypes.cs
index 62936a911..140bebc19 100644
--- a/AgileMapper.UnitTests/Configuration/WhenConfiguringDerivedTypes.cs
+++ b/AgileMapper.UnitTests/Configuration/WhenConfiguringDerivedTypes.cs
@@ -1,5 +1,6 @@
namespace AgileObjects.AgileMapper.UnitTests.Configuration
{
+ using System;
using System.Collections.Generic;
using AgileMapper.Extensions.Internal;
using Common;
@@ -214,8 +215,8 @@ public void ShouldUseATypedToTarget()
.Map(ctx => ctx.Source.Leaf)
.ToTarget()
.AndWhenMapping
- .From().To()
- .Map((dto, l) => dto.Leaf.Description + "!")
+ .From().To()
+ .Map((dto, l) => dto.Description + "!")
.To(l => l.Description);
var leafDto = new Issue123.CompositeDto
@@ -233,6 +234,66 @@ public void ShouldUseATypedToTarget()
}
}
+ [Fact]
+ public void ShouldUseACtorParameterWithATypedToTarget()
+ {
+ using (var mapper = Mapper.CreateNew())
+ {
+ mapper.WhenMapping
+ .From()
+ .To()
+ .If(d => d.Source.ConcreteValue == Issue129.Source.Wrapper.ConcreteValueType.Delay)
+ .Map(d => d.Source.DelayValue)
+ .ToTarget();
+
+ var delaySource = new Issue129.Source.Wrapper
+ {
+ ConcreteValue = Issue129.Source.Wrapper.ConcreteValueType.Delay,
+ DelayValue = new Issue129.Source.DelayObject
+ {
+ Name = "Situation Object",
+ Duration = TimeSpan.FromHours(2).ToString()
+ }
+ };
+
+ var delayResult = mapper.Map(delaySource).ToANew();
+
+ delayResult.ShouldNotBeNull();
+ delayResult.ShouldBeOfType();
+
+ var delayObject = (Issue129.Target.DelayObject)delayResult;
+ delayObject.CurrentClass.ShouldNotBeNull();
+ delayObject.CurrentClass.ShouldBeOfType();
+
+ var delayClass = (Issue129.Target.DelayClass)delayObject.CurrentClass;
+ delayClass.Name.ShouldBe("Delay");
+ delayClass.Duration.ShouldBe(TimeSpan.FromHours(2));
+ }
+ }
+
+ [Fact]
+ public void ShouldHandleANullTypedToTargetSource()
+ {
+ using (var mapper = Mapper.CreateNew())
+ {
+ mapper.WhenMapping
+ .From()
+ .To()
+ .If(d => d.Source.ConcreteValue == Issue129.Source.Wrapper.ConcreteValueType.Delay)
+ .Map(d => d.Source.DelayValue)
+ .ToTarget();
+
+ var nullDelaySource = new Issue129.Source.Wrapper
+ {
+ ConcreteValue = Issue129.Source.Wrapper.ConcreteValueType.Delay
+ };
+
+ var delayResult = mapper.Map(nullDelaySource).ToANew();
+
+ delayResult.ShouldBeNull();
+ }
+ }
+
// See https://github.com/agileobjects/AgileMapper/issues/129
[Fact]
public void ShouldUseAConfiguredCtorParameterWithATypedToTarget()
@@ -248,7 +309,7 @@ public void ShouldUseAConfiguredCtorParameterWithATypedToTarget()
mapper.WhenMapping
.From()
.To()
- .Map(new Issue129.Target.ActionClass())
+ .Map(ctx => new Issue129.Target.ActionClass())
.ToCtor();
mapper.WhenMapping
@@ -261,6 +322,31 @@ public void ShouldUseAConfiguredCtorParameterWithATypedToTarget()
.If(d => d.Source.ConcreteValue == Issue129.Source.Wrapper.ConcreteValueType.Situation)
.Map(d => d.Source.SituationValue)
.ToTarget();
+
+ var situationSource = new Issue129.Source.Wrapper
+ {
+ ConcreteValue = Issue129.Source.Wrapper.ConcreteValueType.Situation,
+ SituationValue = new Issue129.Source.SituationObject
+ {
+ Name = "Situation Object",
+ CurrentClass = new Issue129.Source.SituationClass
+ {
+ Name = "Situation Class"
+ }
+ }
+ };
+
+ var situationResult = mapper.Map(situationSource).ToANew();
+
+ situationResult.ShouldNotBeNull();
+ situationResult.ShouldBeOfType();
+
+ var situationObject = (Issue129.Target.SituationObject)situationResult;
+ situationObject.CurrentClass.ShouldNotBeNull();
+ situationObject.CurrentClass.ShouldBeOfType();
+
+ var situationClass = (Issue129.Target.SituationClass)situationObject.CurrentClass;
+ situationClass.Name.ShouldBe("Situation Class");
}
}
@@ -334,7 +420,7 @@ public class Leaf : ILeaf
}
}
- internal class Issue129
+ internal static class Issue129
{
public static class Source
{
@@ -348,22 +434,31 @@ public class SituationObject
public string Name { get; set; }
public SituationClass CurrentClass { get; set; }
-
}
+
public class ActionObject
{
public string Name { get; set; }
}
+ public class DelayObject
+ {
+ public string Name { get; set; }
+
+ public string Duration { get; set; }
+ }
+
public class Wrapper
{
- public enum ConcreteValueType { Situation, Action }
+ public enum ConcreteValueType { Situation, Action, Delay }
public ConcreteValueType ConcreteValue { get; set; }
public SituationObject SituationValue { get; set; }
public ActionObject ActionValue { get; set; }
+
+ public DelayObject DelayValue { get; set; }
}
}
@@ -373,22 +468,49 @@ public interface ITrafficObj { ITrafficClass CurrentClass { get; } }
public interface ITrafficClass { string Name { get; } }
- public class SituationClass : ITrafficClass { public string Name { get; set; } }
+ public class SituationClass : ITrafficClass
+ {
+ public string Name { get; set; }
+ }
+
+ public class ActionClass : ITrafficClass
+ {
+ public string Name { get; set; }
+ }
+
+ public class DelayClass : ITrafficClass
+ {
+ public string Name { get; set; }
- public class ActionClass : ITrafficClass { public string Name { get; set; } }
+ public TimeSpan Duration { get; set; }
+ }
public class SituationObject : ITrafficObj
{
public SituationObject(SituationClass clazz) { CurrentClass = clazz; }
- public ITrafficClass CurrentClass { get; private set; }
+ public ITrafficClass CurrentClass { get; }
}
public class ActionObject : ITrafficObj
{
public ActionObject(ActionClass clazz) { CurrentClass = clazz; }
- public ITrafficClass CurrentClass { get; private set; }
+ public ITrafficClass CurrentClass { get; }
+ }
+
+ public class DelayObject : ITrafficObj
+ {
+ public DelayObject(TimeSpan duration)
+ {
+ CurrentClass = new DelayClass
+ {
+ Name = "Delay",
+ Duration = duration
+ };
+ }
+
+ public ITrafficClass CurrentClass { get; }
}
}
}
diff --git a/AgileMapper.UnitTests/Configuration/WhenConfiguringMappingCallbacks.cs b/AgileMapper.UnitTests/Configuration/WhenConfiguringMappingCallbacks.cs
index 56dbe9f2d..9d1aad259 100644
--- a/AgileMapper.UnitTests/Configuration/WhenConfiguringMappingCallbacks.cs
+++ b/AgileMapper.UnitTests/Configuration/WhenConfiguringMappingCallbacks.cs
@@ -2,6 +2,7 @@
{
using System;
using System.Collections.Generic;
+ using AgileMapper.Extensions.Internal;
using Common;
using TestClasses;
#if !NET35
@@ -355,5 +356,116 @@ public void ShouldPopulateAChildTargetObjectInAPostMappingCallback()
result.Value.Value.ShouldBe("Hello!");
}
}
+
+ [Fact]
+ public void ShouldExecuteAPreMappingCallbackInARootToTargetMapping()
+ {
+ using (var mapper = Mapper.CreateNew())
+ {
+ var callbackCalled = false;
+
+ mapper.WhenMapping
+ .From, int>>()
+ .To>()
+ .Map(ctx => ctx.Source.Value1)
+ .ToTarget();
+
+ mapper.WhenMapping
+ .From>()
+ .To>()
+ .Before.MappingBegins
+ .Call(md => callbackCalled = true);
+
+ var source = new PublicTwoFields, int>
+ {
+ Value1 = new PublicField { Value = 123 },
+ Value2 = 456
+ };
+
+ var result = mapper.Map(source).ToANew>();
+
+ result.Value.ShouldBe(123);
+ callbackCalled.ShouldBeTrue();
+ }
+ }
+
+ [Fact]
+ public void ShouldExecuteAPreMappingCallbackInAChildToTargetMapping()
+ {
+ using (var mapper = Mapper.CreateNew())
+ {
+ var callbackCalled = false;
+
+ mapper.WhenMapping
+ .From, int>>()
+ .To>()
+ .Map(ctx => ctx.Source.Value1)
+ .ToTarget();
+
+ mapper.WhenMapping
+ .From>()
+ .To>()
+ .After.MappingEnds
+ .Call(md => callbackCalled = true);
+
+ var source = new PublicProperty, int>>
+ {
+ Value = new PublicTwoFields, int>
+ {
+ Value1 = new PublicField { Value = 456 },
+ Value2 = 123
+ }
+ };
+
+ var result = mapper.Map(source).ToANew>>();
+
+ result.Value.ShouldNotBeNull();
+ result.Value.Value.ShouldBe(456);
+ callbackCalled.ShouldBeTrue();
+ }
+ }
+
+ [Fact]
+ public void ShouldExecuteAPreMappingCallbackInAnElementToTargetMapping()
+ {
+ using (var mapper = Mapper.CreateNew())
+ {
+ var callbackCount = 0;
+
+ mapper.WhenMapping
+ .From>>()
+ .To>()
+ .Map(ctx => ctx.Source.Value2)
+ .ToTarget();
+
+ mapper.WhenMapping
+ .From>()
+ .To>()
+ .After.MappingEnds
+ .Call(md => ++callbackCount);
+
+ var source = new[]
+ {
+ new PublicTwoFields>
+ {
+ Value1 = 111,
+ Value2 = new PublicField { Value = 222 },
+ },
+ new PublicTwoFields>
+ {
+ Value1 = 333,
+ Value2 = new PublicField { Value = 444 },
+ }
+ };
+
+ var result = mapper.Map(source).ToANew[]>();
+
+ result.ShouldNotBeNull();
+ result.Length.ShouldBe(2);
+ result.First().Value.ShouldBe(222);
+ result.Second().Value.ShouldBe(444);
+ callbackCount.ShouldBe(2);
+ }
+ }
}
}
diff --git a/AgileMapper/Api/Configuration/DerivedPairTargetTypeSpecifier.cs b/AgileMapper/Api/Configuration/DerivedPairTargetTypeSpecifier.cs
index c72771948..b68318d80 100644
--- a/AgileMapper/Api/Configuration/DerivedPairTargetTypeSpecifier.cs
+++ b/AgileMapper/Api/Configuration/DerivedPairTargetTypeSpecifier.cs
@@ -1,10 +1,8 @@
namespace AgileObjects.AgileMapper.Api.Configuration
{
- using System;
using AgileMapper.Configuration;
using Extensions.Internal;
using NetStandardPolyfills;
- using ObjectPopulation;
using Projection;
using ReadableExpressions.Extensions;
@@ -46,12 +44,8 @@ private void ThrowIfUnconstructable()
{
var mappingData = _configInfo.ToMappingData();
- if (IsConstructableUsing(mappingData))
- {
- return;
- }
-
- if (IsConstructableFromToTargetDataSources(mappingData, typeof(TDerivedTarget)))
+ if (mappingData.IsTargetConstructable() ||
+ mappingData.IsConstructableFromToTargetDataSource())
{
return;
}
@@ -63,9 +57,8 @@ private void ThrowIfUnconstructable()
var configuredImplementationPairings = MapperContext
.UserConfigurations
- .DerivedTypes.GetImplementationTypePairsFor(
- _configInfo.ToMapperData(),
- MapperContext);
+ .DerivedTypes
+ .GetImplementationTypePairsFor(_configInfo.ToMapperData(), MapperContext);
if (configuredImplementationPairings.None())
{
@@ -73,45 +66,6 @@ private void ThrowIfUnconstructable()
}
}
- private bool IsConstructableUsing(IObjectMappingData mappingData)
- => MapperContext.ConstructionFactory.GetNewObjectCreation(mappingData) != null;
-
- private bool IsConstructableFromToTargetDataSources(IObjectMappingData mappingData, Type derivedTargetType)
- {
- var toTargetDataSources = MapperContext
- .UserConfigurations
- .GetDataSourcesForToTarget(mappingData.MapperData);
-
- if (toTargetDataSources.None())
- {
- return false;
- }
-
- var constructionCheckMethod = typeof(DerivedPairTargetTypeSpecifier)
- .GetNonPublicInstanceMethod(nameof(IsConstructableFromDataSource));
-
- foreach (var dataSource in toTargetDataSources)
- {
- var isConstructable = (bool)constructionCheckMethod
- .MakeGenericMethod(dataSource.SourceMember.Type, derivedTargetType)
- .Invoke(this, Enumerable