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
31 changes: 31 additions & 0 deletions src/Mapster.Tests/WhenIncludeDerivedClasses.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,37 @@ public void Map_Including_Derived_Class_With_List()
((BikeDto)dto[1]).Brand.ShouldBe("BMX");
}

/// <summary>
/// https://github.com/MapsterMapper/Mapster/issues/801
/// </summary>
[TestMethod]
public void CompileProjection_Including_Derived_Class()
{
TypeAdapterConfig<PocoA801, DtoA801>.NewConfig()
.Include<PocoDerived801, DtoDerived801>()
.CompileProjection();
}

public abstract class PocoA801
{
public int Id { get; set; }
}

public class PocoDerived801 : PocoA801
{
public int DerivedVal { get; set; }
}

public abstract class DtoA801
{
public int Id { get; set; }
}

public class DtoDerived801 : DtoA801
{
public int DerivedVal { get; set; }
}

#region test classes
public abstract class Vehicle
{
Expand Down
43 changes: 43 additions & 0 deletions src/Mapster.Tests/WhenMappingRecordRegression.cs
Original file line number Diff line number Diff line change
Expand Up @@ -539,6 +539,49 @@ public void NotSelfCreationTypeMappingToSelfWithOutError()
resultJ.RootElement.GetProperty("key").ToString().ShouldBe("value");
}

/// <summary>
/// https://github.com/MapsterMapper/Mapster/issues/911
/// </summary>
[TestMethod]
public void NotSelfCreationTypeMappingInContainingClassWithoutError()
{
var jsonSource = new SourceClassWithJsonDocument911
{
Json = JsonDocument.Parse("{\"key\": \"value\"}")
};

var uriSource = new SourceClassWithUri911
{
Uri = new Uri("https://www.google.com/")
};

var jsonDest = jsonSource.Adapt<DestinationClassWithJsonDocument911>();
var uriDest = uriSource.Adapt<DestinationClassWithUri911>();

jsonDest.Json.RootElement.GetProperty("key").GetString().ShouldBe("value");
uriDest.Uri.ToString().ShouldBe("https://www.google.com/");
}

class SourceClassWithJsonDocument911
{
public required JsonDocument Json { get; init; }
}

class DestinationClassWithJsonDocument911
{
public required JsonDocument Json { get; init; }
}

class SourceClassWithUri911
{
public required Uri Uri { get; init; }
}

class DestinationClassWithUri911
{
public required Uri Uri { get; init; }
}

/// <summary>
/// https://github.com/MapsterMapper/Mapster/issues/927
/// </summary>
Expand Down
40 changes: 39 additions & 1 deletion src/Mapster/Adapters/ClassAdapter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -219,9 +219,17 @@ private static Expression SetValueByReflection(MemberMapping member, MemberExpre
// Prop2 = convert(src.Prop2),
//}

if (arg.MapType == MapType.Projection && arg.DestinationType.IsAbstract && arg.Settings.Includes.Count > 0)
return CreateIncludeProjectionExpression(source, arg);

var exp = CreateInstantiationExpression(source, arg);
if (exp.NodeType == ExpressionType.Throw)
return null;

var memberInit = exp as MemberInitExpression;
var newInstance = memberInit?.NewExpression ?? (NewExpression)exp;
var newInstance = memberInit?.NewExpression ?? exp as NewExpression;
if (newInstance == null)
return null;
var contructorMembers = newInstance.GetAllMemberExpressionsMemberInfo().ToArray();
ClassModel? classModel;
ClassMapping? classConverter;
Expand Down Expand Up @@ -272,6 +280,36 @@ private static Expression SetValueByReflection(MemberMapping member, MemberExpre
return Expression.MemberInit(newInstance, lines);
}

static Expression CreateIncludeProjectionExpression(Expression source, CompileArgument arg)
{
Expression body = Expression.Default(arg.DestinationType);
foreach (var tuple in arg.Settings.Includes)
{
var itemTuple = tuple;
if (tuple.Source.IsOpenGenericType() && tuple.Destination.IsOpenGenericType())
{
var genericArg = source.Type.GetGenericArguments();
itemTuple = new TypeTuple(tuple.Source.MakeGenericType(genericArg), tuple.Destination.MakeGenericType(genericArg));
}

if (itemTuple.Source == arg.SourceType)
continue;

if (!arg.SourceType.GetTypeInfo().IsAssignableFrom(itemTuple.Source.GetTypeInfo()))
continue;

if (!arg.DestinationType.GetTypeInfo().IsAssignableFrom(itemTuple.Destination.GetTypeInfo()))
continue;

var test = Expression.TypeIs(source, itemTuple.Source);
var cast = Expression.TypeAs(source, itemTuple.Source);
var mapped = CreateAdaptExpressionCore(cast!, itemTuple.Destination, arg);
body = Expression.Condition(test, mapped.To(arg.DestinationType, true), body);
}

return body;
}

protected override Expression CreateExpressionBody(Expression source, Expression? destination, CompileArgument arg)
{
TypeAdapterRule? rule;
Expand Down