Skip to content

Commit f089763

Browse files
authored
chore: Merge pull request #977 from leno23/fix/projection-include-abstract-801-dev
fix: support CompileProjection with Include on abstract types (#801)
2 parents b0dda70 + 98f1afa commit f089763

3 files changed

Lines changed: 113 additions & 1 deletion

File tree

src/Mapster.Tests/WhenIncludeDerivedClasses.cs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,37 @@ public void Map_Including_Derived_Class_With_List()
4040
((BikeDto)dto[1]).Brand.ShouldBe("BMX");
4141
}
4242

43+
/// <summary>
44+
/// https://github.com/MapsterMapper/Mapster/issues/801
45+
/// </summary>
46+
[TestMethod]
47+
public void CompileProjection_Including_Derived_Class()
48+
{
49+
TypeAdapterConfig<PocoA801, DtoA801>.NewConfig()
50+
.Include<PocoDerived801, DtoDerived801>()
51+
.CompileProjection();
52+
}
53+
54+
public abstract class PocoA801
55+
{
56+
public int Id { get; set; }
57+
}
58+
59+
public class PocoDerived801 : PocoA801
60+
{
61+
public int DerivedVal { get; set; }
62+
}
63+
64+
public abstract class DtoA801
65+
{
66+
public int Id { get; set; }
67+
}
68+
69+
public class DtoDerived801 : DtoA801
70+
{
71+
public int DerivedVal { get; set; }
72+
}
73+
4374
#region test classes
4475
public abstract class Vehicle
4576
{

src/Mapster.Tests/WhenMappingRecordRegression.cs

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -539,6 +539,49 @@ public void NotSelfCreationTypeMappingToSelfWithOutError()
539539
resultJ.RootElement.GetProperty("key").ToString().ShouldBe("value");
540540
}
541541

542+
/// <summary>
543+
/// https://github.com/MapsterMapper/Mapster/issues/911
544+
/// </summary>
545+
[TestMethod]
546+
public void NotSelfCreationTypeMappingInContainingClassWithoutError()
547+
{
548+
var jsonSource = new SourceClassWithJsonDocument911
549+
{
550+
Json = JsonDocument.Parse("{\"key\": \"value\"}")
551+
};
552+
553+
var uriSource = new SourceClassWithUri911
554+
{
555+
Uri = new Uri("https://www.google.com/")
556+
};
557+
558+
var jsonDest = jsonSource.Adapt<DestinationClassWithJsonDocument911>();
559+
var uriDest = uriSource.Adapt<DestinationClassWithUri911>();
560+
561+
jsonDest.Json.RootElement.GetProperty("key").GetString().ShouldBe("value");
562+
uriDest.Uri.ToString().ShouldBe("https://www.google.com/");
563+
}
564+
565+
class SourceClassWithJsonDocument911
566+
{
567+
public required JsonDocument Json { get; init; }
568+
}
569+
570+
class DestinationClassWithJsonDocument911
571+
{
572+
public required JsonDocument Json { get; init; }
573+
}
574+
575+
class SourceClassWithUri911
576+
{
577+
public required Uri Uri { get; init; }
578+
}
579+
580+
class DestinationClassWithUri911
581+
{
582+
public required Uri Uri { get; init; }
583+
}
584+
542585
/// <summary>
543586
/// https://github.com/MapsterMapper/Mapster/issues/927
544587
/// </summary>

src/Mapster/Adapters/ClassAdapter.cs

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -219,9 +219,17 @@ private static Expression SetValueByReflection(MemberMapping member, MemberExpre
219219
// Prop2 = convert(src.Prop2),
220220
//}
221221

222+
if (arg.MapType == MapType.Projection && arg.DestinationType.IsAbstract && arg.Settings.Includes.Count > 0)
223+
return CreateIncludeProjectionExpression(source, arg);
224+
222225
var exp = CreateInstantiationExpression(source, arg);
226+
if (exp.NodeType == ExpressionType.Throw)
227+
return null;
228+
223229
var memberInit = exp as MemberInitExpression;
224-
var newInstance = memberInit?.NewExpression ?? (NewExpression)exp;
230+
var newInstance = memberInit?.NewExpression ?? exp as NewExpression;
231+
if (newInstance == null)
232+
return null;
225233
var contructorMembers = newInstance.GetAllMemberExpressionsMemberInfo().ToArray();
226234
ClassModel? classModel;
227235
ClassMapping? classConverter;
@@ -272,6 +280,36 @@ private static Expression SetValueByReflection(MemberMapping member, MemberExpre
272280
return Expression.MemberInit(newInstance, lines);
273281
}
274282

283+
static Expression CreateIncludeProjectionExpression(Expression source, CompileArgument arg)
284+
{
285+
Expression body = Expression.Default(arg.DestinationType);
286+
foreach (var tuple in arg.Settings.Includes)
287+
{
288+
var itemTuple = tuple;
289+
if (tuple.Source.IsOpenGenericType() && tuple.Destination.IsOpenGenericType())
290+
{
291+
var genericArg = source.Type.GetGenericArguments();
292+
itemTuple = new TypeTuple(tuple.Source.MakeGenericType(genericArg), tuple.Destination.MakeGenericType(genericArg));
293+
}
294+
295+
if (itemTuple.Source == arg.SourceType)
296+
continue;
297+
298+
if (!arg.SourceType.GetTypeInfo().IsAssignableFrom(itemTuple.Source.GetTypeInfo()))
299+
continue;
300+
301+
if (!arg.DestinationType.GetTypeInfo().IsAssignableFrom(itemTuple.Destination.GetTypeInfo()))
302+
continue;
303+
304+
var test = Expression.TypeIs(source, itemTuple.Source);
305+
var cast = Expression.TypeAs(source, itemTuple.Source);
306+
var mapped = CreateAdaptExpressionCore(cast!, itemTuple.Destination, arg);
307+
body = Expression.Condition(test, mapped.To(arg.DestinationType, true), body);
308+
}
309+
310+
return body;
311+
}
312+
275313
protected override Expression CreateExpressionBody(Expression source, Expression? destination, CompileArgument arg)
276314
{
277315
TypeAdapterRule? rule;

0 commit comments

Comments
 (0)