Skip to content

Commit

Permalink
split EnumAdapter+StringAdapter+ObjectAdapter, fix dynamic
Browse files Browse the repository at this point in the history
  • Loading branch information
chaowlert committed May 27, 2017
1 parent cb2b752 commit e1207c0
Show file tree
Hide file tree
Showing 13 changed files with 192 additions and 157 deletions.
36 changes: 30 additions & 6 deletions src/Mapster/Adapter.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Reflection;

namespace Mapster
{
Expand All @@ -20,8 +21,11 @@ public TypeAdapterBuilder<TSource> BuildAdapter<TSource>(TSource source)

public TDestination Adapt<TDestination>(object source)
{
dynamic fn = _config.GetMapFunction(source.GetType(), typeof(TDestination));
return (TDestination)fn((dynamic)source);
if (source == null)
return default(TDestination);
var type = source.GetType();
var fn = _config.GetDynamicMapFunction<TDestination>(type);
return fn(source);
}

public TDestination Adapt<TSource, TDestination>(TSource source)
Expand All @@ -38,14 +42,34 @@ public TDestination Adapt<TSource, TDestination>(TSource source, TDestination de

public object Adapt(object source, Type sourceType, Type destinationType)
{
var fn = _config.GetMapFunction(sourceType, destinationType);
return fn.DynamicInvoke(source);
var del = _config.GetMapFunction(sourceType, destinationType);
if (sourceType.GetTypeInfo().IsVisible && destinationType.GetTypeInfo().IsVisible)
{
dynamic fn = del;
return fn((dynamic)source);
}
else
{
//NOTE: if type is non-public, we cannot use dynamic
//DynamicInvoke is slow, but works with non-public
return del.DynamicInvoke(source);
}
}

public object Adapt(object source, object destination, Type sourceType, Type destinationType)
{
dynamic fn = _config.GetMapFunction(sourceType, destinationType);
return fn((dynamic)source);
var del = _config.GetMapToTargetFunction(sourceType, destinationType);
if (sourceType.GetTypeInfo().IsVisible && destinationType.GetTypeInfo().IsVisible)
{
dynamic fn = del;
return fn((dynamic)source, (dynamic)destination);
}
else
{
//NOTE: if type is non-public, we cannot use dynamic
//DynamicInvoke is slow, but works with non-public
return del.DynamicInvoke(source, destination);
}
}
}

Expand Down
16 changes: 1 addition & 15 deletions src/Mapster/Adapters/BaseAdapter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,24 +54,10 @@ protected virtual void DecorateRule(TypeAdapterRule rule) { }

protected virtual bool CanInline(Expression source, Expression destination, CompileArgument arg)
{
if (arg.MapType == MapType.MapToTarget)
return false;
var constructUsing = arg.Settings.ConstructUsingFactory?.Invoke(arg);
if (constructUsing != null &&
constructUsing.Body.NodeType != ExpressionType.New &&
constructUsing.Body.NodeType != ExpressionType.MemberInit)
{
if (arg.MapType == MapType.Projection)
throw new InvalidOperationException("Input ConstructUsing is invalid for projection");
return false;
}

//IgnoreIf, PreserveReference, AfterMapping, Includes aren't supported by Projection
//PreserveReference, AfterMapping, Includes aren't supported by Projection
if (arg.MapType == MapType.Projection)
return true;

if (arg.Settings.IgnoreIfs.Any(item => item.Value != null))
return false;
if (arg.Settings.PreserveReference == true &&
!arg.SourceType.GetTypeInfo().IsValueType &&
!arg.DestinationType.GetTypeInfo().IsValueType)
Expand Down
16 changes: 15 additions & 1 deletion src/Mapster/Adapters/ClassAdapter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,25 @@ protected override bool CanInline(Expression source, Expression destination, Com
if (!base.CanInline(source, destination, arg))
return false;

//IgnoreNullValue isn't supported by projection
if (arg.MapType == MapType.MapToTarget)
return false;
var constructUsing = arg.Settings.ConstructUsingFactory?.Invoke(arg);
if (constructUsing != null &&
constructUsing.Body.NodeType != ExpressionType.New &&
constructUsing.Body.NodeType != ExpressionType.MemberInit)
{
if (arg.MapType == MapType.Projection)
throw new InvalidOperationException("ConstructUsing for projection is support only New and MemberInit expression.");
return false;
}

//IgnoreIfs, IgnoreNullValue isn't supported by projection
if (arg.MapType == MapType.Projection)
return true;
if (arg.Settings.IgnoreNullValues == true)
return false;
if (arg.Settings.IgnoreIfs.Any(item => item.Value != null))
return false;
return true;
}

Expand Down
2 changes: 1 addition & 1 deletion src/Mapster/Adapters/DelegateAdapter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ protected override Expression CreateBlockExpression(Expression source, Expressio

protected override Expression CreateInlineExpression(Expression source, CompileArgument arg)
{
return Expression.Empty();
return CreateInstantiationExpression(source, arg);
}
}
}
2 changes: 1 addition & 1 deletion src/Mapster/Adapters/DictionaryAdapter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ namespace Mapster.Adapters
{
internal class DictionaryAdapter : ClassAdapter
{
protected override int Score => -124;
protected override int Score => -124; //must do before CollectionAdapter

protected override bool CanMap(PreCompileArgument arg)
{
Expand Down
48 changes: 48 additions & 0 deletions src/Mapster/Adapters/EnumAdapter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
using Mapster.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace Mapster.Adapters
{
internal class EnumAdapter : PrimitiveAdapter
{
protected override int Score => -109; //must do before StringAdapter
protected override bool CheckExplicitMapping => false;

protected override bool CanMap(PreCompileArgument arg)
{
return arg.SourceType.GetTypeInfo().IsEnum || arg.DestinationType.GetTypeInfo().IsEnum;
}

protected override Expression ConvertType(Expression source, Type destinationType, CompileArgument arg)
{
var srcType = source.Type;
if (destinationType == typeof(string))
{
var method = typeof(Enum<>).MakeGenericType(srcType).GetMethod("ToString", new[] { srcType });
return Expression.Call(method, source);
}
else if (srcType == typeof(string))
{
var method = typeof(Enum<>).MakeGenericType(destinationType).GetMethod("Parse", new[] { typeof(string) });
return Expression.Call(method, source);
}
else if (destinationType.GetTypeInfo().IsEnum && srcType.GetTypeInfo().IsEnum && arg.Settings.MapEnumByName == true)
{
var method = typeof(Enum<>).MakeGenericType(srcType).GetMethod("ToString", new[] { srcType });
var tostring = Expression.Call(method, source);
var methodParse = typeof(Enum<>).MakeGenericType(destinationType).GetMethod("Parse", new[] { typeof(string) });

return Expression.Call(methodParse, tostring);
}


return base.ConvertType(source, destinationType, arg);
}
}
}
35 changes: 17 additions & 18 deletions src/Mapster/Adapters/ObjectAdapter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,36 +5,35 @@ namespace Mapster.Adapters
{
internal class ObjectAdapter : BaseAdapter
{
protected override int Score => -111;
protected override int Score => -111; //must do before all class adapters
protected override bool CheckExplicitMapping => false;

protected override bool CanMap(PreCompileArgument arg)
{
return arg.SourceType == typeof(object) || arg.DestinationType == typeof(object);
}

protected override Expression CreateInstantiationExpression(Expression source, Expression destination, CompileArgument arg)
{
var srcType = arg.SourceType;
var destType = arg.DestinationType;
if (srcType != destType)
return source;
else if (destType == typeof(object))
return Expression.Convert(source, destType);
else //if (srcType == typeof(object))
return ReflectionUtils.CreateConvertMethod(srcType, destType, source)
?? Expression.Convert(source, destType);
}

protected override Expression CreateBlockExpression(Expression source, Expression destination, CompileArgument arg)
{
throw new NotImplementedException();
return Expression.Empty();
}

protected override Expression CreateInlineExpression(Expression source, CompileArgument arg)
{
// object, T

//if (src.GetType() == typeof(object))
// return (T)src

//return src.Adapt<T>();

// poco, object

//return (object)src.Adapt<T, T>();

// object, object

//if (src.GetType() == typeof(object))
// return src;
// return src.Adapt(src.GetType(), src.GetType());
return CreateInstantiationExpression(source, arg);
}
}
}
74 changes: 12 additions & 62 deletions src/Mapster/Adapters/PrimitiveAdapter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ namespace Mapster.Adapters
{
internal class PrimitiveAdapter : BaseAdapter
{
protected override int Score => -200;
protected override int Score => -200; //must do last
protected override bool CheckExplicitMapping => false;

protected override bool CanMap(PreCompileArgument arg)
Expand All @@ -24,10 +24,11 @@ protected override Expression CreateExpressionBody(Expression source, Expression
if (sourceType != destinationType)
{
if (sourceType.IsNullable())
{
convert = Expression.Convert(convert, sourceType.GetGenericArguments()[0]);
}
convert = ConvertType(convert, arg);
var destType = arg.DestinationType.UnwrapNullable();

if (convert.Type != destType)
convert = ConvertType(convert, destType, arg);
if (convert.Type != destinationType)
convert = Expression.Convert(convert, destinationType);

Expand All @@ -43,32 +44,14 @@ protected override Expression CreateExpressionBody(Expression source, Expression
return convert;
}

protected virtual Expression ConvertType(Expression source, CompileArgument arg)
protected virtual Expression ConvertType(Expression source, Type destinationType, CompileArgument arg)
{
var srcType = arg.SourceType.UnwrapNullable();
var destType = arg.DestinationType.UnwrapNullable();

if (srcType == destType)
return source;

if (destType.GetTypeInfo().IsEnum && srcType.GetTypeInfo().IsEnum && arg.Settings.MapEnumByName == true)
{
var method = typeof(Enum<>).MakeGenericType(srcType).GetMethod("ToString", new[] { srcType });
var tostring = Expression.Call(method, source);
var methodParse = typeof(Enum<>).MakeGenericType(destType).GetMethod("Parse", new[] { typeof(string) });

return Expression.Call(methodParse, tostring);
}

if (IsObjectToPrimitiveConversion(srcType, destType))
{
return CreateConvertMethod(_primitiveTypes[destType], srcType, destType, source);
}
var srcType = source.Type;

//try using type casting
try
{
return Expression.Convert(source, destType);
return Expression.Convert(source, destinationType);
}
catch
{
Expand All @@ -79,28 +62,12 @@ protected virtual Expression ConvertType(Expression source, CompileArgument arg)
throw new InvalidOperationException("Cannot convert immutable type, please consider using 'MapWith' method to create mapping");

//using Convert
if (_primitiveTypes.ContainsKey(destType))
{
return CreateConvertMethod(_primitiveTypes[destType], srcType, destType, source);
}
var result = ReflectionUtils.CreateConvertMethod(srcType, destinationType, source);
if (result != null)
return result;

var changeTypeMethod = typeof(Convert).GetMethod("ChangeType", new[] { typeof(object), typeof(Type) });
return Expression.Convert(Expression.Call(changeTypeMethod, Expression.Convert(source, typeof(object)), Expression.Constant(destType)), destType);
}

private static bool IsObjectToPrimitiveConversion(Type sourceType, Type destinationType)
{
return (sourceType == typeof(object)) && _primitiveTypes.ContainsKey(destinationType);
}

private static Expression CreateConvertMethod(string name, Type srcType, Type destType, Expression source)
{
var method = typeof(Convert).GetMethod(name, new[] { srcType });
if (method != null)
return Expression.Call(method, source);

method = typeof(Convert).GetMethod(name, new[] { typeof(object) });
return Expression.Convert(Expression.Call(method, Expression.Convert(source, typeof(object))), destType);
return Expression.Convert(Expression.Call(changeTypeMethod, Expression.Convert(source, typeof(object)), Expression.Constant(destinationType)), destinationType);
}

protected override Expression CreateBlockExpression(Expression source, Expression destination, CompileArgument arg)
Expand All @@ -112,22 +79,5 @@ protected override Expression CreateInlineExpression(Expression source, CompileA
{
throw new NotImplementedException();
}

// Primitive types with their conversion methods from System.Convert class.
private static Dictionary<Type, string> _primitiveTypes = new Dictionary<Type, string>() {
{ typeof(bool), "ToBoolean" },
{ typeof(short), "ToInt16" },
{ typeof(int), "ToInt32" },
{ typeof(long), "ToInt64" },
{ typeof(float), "ToSingle" },
{ typeof(double), "ToDouble" },
{ typeof(decimal), "ToDecimal" },
{ typeof(ushort), "ToUInt16" },
{ typeof(uint), "ToUInt32" },
{ typeof(ulong), "ToUInt64" },
{ typeof(byte), "ToByte" },
{ typeof(sbyte), "ToSByte" },
{ typeof(DateTime), "ToDateTime" }
};
}
}
Loading

0 comments on commit e1207c0

Please sign in to comment.