Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Shouldly;

namespace Mapster.Tests
{
/// <summary>
/// https://github.com/MapsterMapper/Mapster/issues/947
/// </summary>
[TestClass]
public class WhenImplicitInheritanceMapWithDerivedDestination
{
[TestCleanup]
public void Cleanup()
{
TypeAdapterConfig.GlobalSettings.Clear();
TypeAdapterConfig.GlobalSettings.AllowImplicitDestinationInheritance = false;
}

[TestMethod]
public void Inherited_MapWith_On_Base_Destination_Casts_To_Derived_Destination()
{
var config = new TypeAdapterConfig();
config.AllowImplicitDestinationInheritance = true;
config.NewConfig<AnimalDto947, Animal947>()
.MapWith(src => src.Type == "Bird"
? (Animal947)new Bird947 { AnimalValue = src.AnimalValueDto }
: new Dog947 { AnimalValue = src.AnimalValueDto });

var source = new AnimalDto947 { AnimalValueDto = "Hello", Type = "Dog" };

var dog = source.Adapt<Dog947>(config);

dog.ShouldBeOfType<Dog947>();
dog.AnimalValue.ShouldBe("Hello");
}

[TestMethod]
public void Inherited_MapWith_Works_For_Explicit_Source_Destination_Pair()
{
var config = new TypeAdapterConfig();
config.AllowImplicitDestinationInheritance = true;
config.NewConfig<AnimalDto947, Animal947>()
.MapWith(src => src.Type == "Bird"
? (Animal947)new Bird947 { AnimalValue = src.AnimalValueDto }
: new Dog947 { AnimalValue = src.AnimalValueDto });

var source = new AnimalDto947 { AnimalValueDto = "Hello", Type = "Dog" };

var dog = source.Adapt<AnimalDto947, Dog947>(config);

dog.ShouldBeOfType<Dog947>();
dog.AnimalValue.ShouldBe("Hello");
}

public abstract class Animal947
{
public string AnimalValue { get; set; } = null!;
}

public class Dog947 : Animal947
{
}

public class Bird947 : Animal947
{
}

public class AnimalDto947
{
public string AnimalValueDto { get; set; } = null!;

public string Type { get; set; } = null!;
}
}
}
18 changes: 17 additions & 1 deletion src/Mapster/TypeAdapterConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -431,14 +431,30 @@ private static LambdaExpression CreateMapExpression(CompileArgument arg)
throw new CompileException(arg, new InvalidOperationException("ConverterFactory is not found"));
try
{
return fn(arg);
return AdjustInheritedConverterReturnType(fn(arg), arg);
}
catch (Exception ex)
{
throw new CompileException(arg, ex);
}
}

private static LambdaExpression AdjustInheritedConverterReturnType(LambdaExpression lambda, CompileArgument arg)
{
var destinationType = arg.DestinationType;
var returnType = lambda.ReturnType;
if (returnType == destinationType)
return lambda;

// MapWith configured on a base destination type returns the base type, but implicit
// destination inheritance can compile the converter for a derived destination.
if (!returnType.IsAssignableFrom(destinationType))
return lambda;

var body = lambda.Body.To(destinationType, force: true);
return Expression.Lambda(body, lambda.Parameters);
}

private LambdaExpression CreateDynamicMapExpression(TypeTuple tuple)
{
var lambda = CreateMapExpression(tuple, MapType.Map);
Expand Down