diff --git a/src/Mapster.Tests/WhenMappingWithDictionary.cs b/src/Mapster.Tests/WhenMappingWithDictionary.cs index e76212da..df38030b 100644 --- a/src/Mapster.Tests/WhenMappingWithDictionary.cs +++ b/src/Mapster.Tests/WhenMappingWithDictionary.cs @@ -278,6 +278,22 @@ public void AdaptClassWithObjectKeyDictionary() result.Dict.Keys.Any(k => k.Name == "one hundred").ShouldBeTrue(); } + [TestMethod] + public void MapNestedDictionariesToClasses() + { + var pet = new Dictionary {{"Name", "Fluffy"}, {"Type", "Cat"}}; + var dictionary = new Dictionary + { + {"Name", "Alice"}, + {"Pet", pet} + }; + + var person = dictionary.Adapt(); + person.Name.ShouldBe(dictionary["Name"]); + person.Pet.Name.ShouldBe(pet["Name"]); + person.Pet.Type.ShouldBe(pet["Type"]); + } + public class SimplePoco { public Guid Id { get; set; } @@ -313,5 +329,18 @@ public class OtherClassWithPocoKeyDictionary { public Dictionary Dict { get; set; } } + + public class Person + { + public string Name { get; set; } + public Pet Pet { get; set; } + } + + public class Pet + { + public string Name { get; set; } + public string Type { get; set; } + } + } } diff --git a/src/Mapster.Tests/WhenMappingWithPath.cs b/src/Mapster.Tests/WhenMappingWithPath.cs index fe774654..1e295812 100644 --- a/src/Mapster.Tests/WhenMappingWithPath.cs +++ b/src/Mapster.Tests/WhenMappingWithPath.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using ExpressionDebugger; using Microsoft.VisualStudio.TestTools.UnitTesting; using Shouldly; diff --git a/src/Mapster/Adapters/BaseAdapter.cs b/src/Mapster/Adapters/BaseAdapter.cs index 25a68e6a..0dd371c0 100644 --- a/src/Mapster/Adapters/BaseAdapter.cs +++ b/src/Mapster/Adapters/BaseAdapter.cs @@ -108,9 +108,16 @@ protected virtual Expression CreateExpressionBody(Expression source, Expression? if (exp != null) return exp.To(arg.DestinationType, true); } - - if (arg.Context.Running.Count > 1 && !arg.Context.Config.SelfContainedCodeGeneration && !arg.Context.IsSubFunction()) - return arg.Context.Config.CreateMapInvokeExpressionBody(source.Type, arg.DestinationType, source, destination); + + if (arg.Context.Running.Count > 1 && + !arg.Context.Config.SelfContainedCodeGeneration && + !arg.Context.IsSubFunction()) + { + if (destination == null) + return arg.Context.Config.CreateMapInvokeExpressionBody(source.Type, arg.DestinationType, source); + else + return arg.Context.Config.CreateMapToTargetInvokeExpressionBody(source.Type, arg.DestinationType, source, destination); + } else return CreateBlockExpressionBody(source, destination, arg); } diff --git a/src/Mapster/Adapters/ObjectAdapter.cs b/src/Mapster/Adapters/ObjectAdapter.cs index a0e1b73e..71817886 100644 --- a/src/Mapster/Adapters/ObjectAdapter.cs +++ b/src/Mapster/Adapters/ObjectAdapter.cs @@ -17,11 +17,9 @@ protected override Expression CreateInstantiationExpression(Expression source, E var destType = arg.DestinationType; if (srcType == destType) return source; - else if (destType == typeof(object)) + if (destType == typeof(object)) return Expression.Convert(source, destType); - else //if (srcType == typeof(object)) - return ReflectionUtils.CreateConvertMethod(srcType, destType, source) - ?? Expression.Convert(source, destType); + return arg.Context.Config.CreateDynamicMapInvokeExpressionBody(arg.DestinationType, source); } protected override Expression CreateBlockExpression(Expression source, Expression destination, CompileArgument arg) diff --git a/src/Mapster/Compile/PreCompileArgument.cs b/src/Mapster/Compile/PreCompileArgument.cs index fe43fcef..d7c879b0 100644 --- a/src/Mapster/Compile/PreCompileArgument.cs +++ b/src/Mapster/Compile/PreCompileArgument.cs @@ -4,9 +4,9 @@ namespace Mapster { public class PreCompileArgument { - public Type SourceType; - public Type DestinationType; - public MapType MapType; - public bool ExplicitMapping; + public Type SourceType { get; set; } + public Type DestinationType { get; set; } + public MapType MapType { get; set; } + public bool ExplicitMapping { get; set; } } } diff --git a/src/Mapster/TypeAdapterConfig.cs b/src/Mapster/TypeAdapterConfig.cs index f0fd556c..fdfd8482 100644 --- a/src/Mapster/TypeAdapterConfig.cs +++ b/src/Mapster/TypeAdapterConfig.cs @@ -292,7 +292,7 @@ internal MethodCallExpression GetProjectionCallExpression(Type sourceType, Type } private readonly ConcurrentDictionary _dynamicMapDict = new ConcurrentDictionary(); - internal Func GetDynamicMapFunction(Type sourceType) + public Func GetDynamicMapFunction(Type sourceType) { var key = new TypeTuple(sourceType, typeof(TDestination)); if (!_dynamicMapDict.TryGetValue(key, out var del)) @@ -300,6 +300,24 @@ internal MethodCallExpression GetProjectionCallExpression(Type sourceType, Type return (Func)del; } + private Expression CreateSelfExpression() + { + if (this == GlobalSettings) + return Expression.Property(null, typeof(TypeAdapterConfig).GetProperty(nameof(GlobalSettings))); + else + return Expression.Constant(this); + } + + internal Expression CreateDynamicMapInvokeExpressionBody(Type destinationType, Expression p1) + { + var method = (from m in typeof(TypeAdapterConfig).GetMethods(BindingFlags.Instance | BindingFlags.Public) + where m.Name == nameof(GetDynamicMapFunction) + select m).First().MakeGenericMethod(destinationType); + var getType = typeof(object).GetMethod(nameof(GetType)); + var invoker = Expression.Call(CreateSelfExpression(), method, Expression.Call(p1, getType)); + return Expression.Call(invoker, "Invoke", null, p1); + } + internal LambdaExpression CreateMapExpression(TypeTuple tuple, MapType mapType) { var context = new CompileContext(this); @@ -427,20 +445,17 @@ internal Expression CreateMapInvokeExpressionBody(Type sourceType, Type destinat var method = (from m in typeof(TypeAdapterConfig).GetMethods(BindingFlags.Instance | BindingFlags.Public) where m.Name == nameof(GetMapFunction) select m).First().MakeGenericMethod(sourceType, destinationType); - invoker = Expression.Call(Expression.Constant(this), method); + invoker = Expression.Call(CreateSelfExpression(), method); } return Expression.Call(invoker, "Invoke", null, p); } - internal Expression CreateMapInvokeExpressionBody(Type sourceType, Type destinationType, Expression p1, Expression? p2) + internal Expression CreateMapToTargetInvokeExpressionBody(Type sourceType, Type destinationType, Expression p1, Expression p2) { - if (p2 == null) - return CreateMapInvokeExpressionBody(sourceType, destinationType, p1); - var method = (from m in typeof(TypeAdapterConfig).GetMethods(BindingFlags.Instance | BindingFlags.Public) where m.Name == nameof(GetMapToTargetFunction) select m).First().MakeGenericMethod(sourceType, destinationType); - var invoker = Expression.Call(Expression.Constant(this), method); + var invoker = Expression.Call(CreateSelfExpression(), method); return Expression.Call(invoker, "Invoke", null, p1, p2); } @@ -448,7 +463,7 @@ private LambdaExpression CreateMapToTargetInvokeExpression(Type sourceType, Type { var p1 = Expression.Parameter(sourceType); var p2 = Expression.Parameter(destinationType); - var invoke = CreateMapInvokeExpressionBody(sourceType, destinationType, p1, p2); + var invoke = CreateMapToTargetInvokeExpressionBody(sourceType, destinationType, p1, p2); return Expression.Lambda(invoke, p1, p2); }