Skip to content

Commit

Permalink
fix MapsterMapper#251 Map nested IDictionary<string, object>
Browse files Browse the repository at this point in the history
  • Loading branch information
chaowlert committed Jul 28, 2020
1 parent 3890544 commit f553365
Show file tree
Hide file tree
Showing 6 changed files with 68 additions and 20 deletions.
29 changes: 29 additions & 0 deletions src/Mapster.Tests/WhenMappingWithDictionary.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<string, object> {{"Name", "Fluffy"}, {"Type", "Cat"}};
var dictionary = new Dictionary<string, object>
{
{"Name", "Alice"},
{"Pet", pet}
};

var person = dictionary.Adapt<Person>();
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; }
Expand Down Expand Up @@ -313,5 +329,18 @@ public class OtherClassWithPocoKeyDictionary
{
public Dictionary<SimplePoco, int> 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; }
}

}
}
1 change: 0 additions & 1 deletion src/Mapster.Tests/WhenMappingWithPath.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
using ExpressionDebugger;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Shouldly;

Expand Down
13 changes: 10 additions & 3 deletions src/Mapster/Adapters/BaseAdapter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down
6 changes: 2 additions & 4 deletions src/Mapster/Adapters/ObjectAdapter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
8 changes: 4 additions & 4 deletions src/Mapster/Compile/PreCompileArgument.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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; }
}
}
31 changes: 23 additions & 8 deletions src/Mapster/TypeAdapterConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -292,14 +292,32 @@ internal MethodCallExpression GetProjectionCallExpression(Type sourceType, Type
}

private readonly ConcurrentDictionary<TypeTuple, Delegate> _dynamicMapDict = new ConcurrentDictionary<TypeTuple, Delegate>();
internal Func<object, TDestination> GetDynamicMapFunction<TDestination>(Type sourceType)
public Func<object, TDestination> GetDynamicMapFunction<TDestination>(Type sourceType)
{
var key = new TypeTuple(sourceType, typeof(TDestination));
if (!_dynamicMapDict.TryGetValue(key, out var del))
del = AddToHash(_dynamicMapDict, key, tuple => Compiler(CreateDynamicMapExpression(tuple)));
return (Func<object, TDestination>)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);
Expand Down Expand Up @@ -427,28 +445,25 @@ 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);
}

private LambdaExpression CreateMapToTargetInvokeExpression(Type sourceType, Type destinationType)
{
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);
}

Expand Down

0 comments on commit f553365

Please sign in to comment.