diff --git a/src/Mapster.Tests/WhenIncludeDerivedClasses.cs b/src/Mapster.Tests/WhenIncludeDerivedClasses.cs index 3002766a..3af19256 100644 --- a/src/Mapster.Tests/WhenIncludeDerivedClasses.cs +++ b/src/Mapster.Tests/WhenIncludeDerivedClasses.cs @@ -40,6 +40,37 @@ public void Map_Including_Derived_Class_With_List() ((BikeDto)dto[1]).Brand.ShouldBe("BMX"); } + /// + /// https://github.com/MapsterMapper/Mapster/issues/801 + /// + [TestMethod] + public void CompileProjection_Including_Derived_Class() + { + TypeAdapterConfig.NewConfig() + .Include() + .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 { diff --git a/src/Mapster.Tests/WhenMappingRecordRegression.cs b/src/Mapster.Tests/WhenMappingRecordRegression.cs index 13ff3ea3..bcf0c96e 100644 --- a/src/Mapster.Tests/WhenMappingRecordRegression.cs +++ b/src/Mapster.Tests/WhenMappingRecordRegression.cs @@ -539,6 +539,49 @@ public void NotSelfCreationTypeMappingToSelfWithOutError() resultJ.RootElement.GetProperty("key").ToString().ShouldBe("value"); } + /// + /// https://github.com/MapsterMapper/Mapster/issues/911 + /// + [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(); + var uriDest = uriSource.Adapt(); + + 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; } + } + /// /// https://github.com/MapsterMapper/Mapster/issues/927 /// diff --git a/src/Mapster/Adapters/ClassAdapter.cs b/src/Mapster/Adapters/ClassAdapter.cs index 27d09d9b..d71067b0 100644 --- a/src/Mapster/Adapters/ClassAdapter.cs +++ b/src/Mapster/Adapters/ClassAdapter.cs @@ -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; @@ -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;