Skip to content

Commit

Permalink
allow discover non-public poco
Browse files Browse the repository at this point in the history
  • Loading branch information
chaowlert committed May 27, 2017
1 parent e1207c0 commit 3e38326
Show file tree
Hide file tree
Showing 13 changed files with 145 additions and 49 deletions.
8 changes: 8 additions & 0 deletions src/Mapster.Tests/Mapster.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,12 @@
<AssemblyOriginatorKeyFile>Mapster.Tests.snk</AssemblyOriginatorKeyFile>
</PropertyGroup>
<ItemGroup>
<Reference Include="ExpressionDebugger, Version=1.0.2.0, Culture=neutral, PublicKeyToken=b4df16a241f24dde, processorArchitecture=MSIL">
<HintPath>..\packages\ExpressionDebugger.1.0.2\lib\net45\ExpressionDebugger.dll</HintPath>
</Reference>
<Reference Include="Newtonsoft.Json, Version=10.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\packages\Newtonsoft.Json.10.0.2\lib\net45\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="Shouldly, Version=2.8.2.0, Culture=neutral, PublicKeyToken=6042cbcb05cbc941, processorArchitecture=MSIL">
<HintPath>..\packages\Shouldly.2.8.2\lib\net451\Shouldly.dll</HintPath>
</Reference>
Expand Down Expand Up @@ -81,6 +87,7 @@
<Compile Include="WhenMappingWithExtensionMethods.cs" />
<Compile Include="WhenMappingWithFlexibleName.cs" />
<Compile Include="WhenMappingWithGetMethod.cs" />
<Compile Include="WhenMappingWithOpenGenerics.cs" />
<Compile Include="WhenPerformingAfterMapping.cs" />
<Compile Include="WhenPassingRuntimeValue.cs" />
<Compile Include="WhenPreserveReferences.cs" />
Expand All @@ -106,6 +113,7 @@
<Compile Include="WhenRegisteringAndMappingRace.cs" />
<Compile Include="WhenRunningOnMultipleThreads.cs" />
<Compile Include="WhenScanningForRegisters.cs" />
<Compile Include="WhenUsingAttribute.cs" />
<Compile Include="WhenUsingConverterFactory.cs" />
<Compile Include="WhenUsingRuleBasedSetting.cs" />
<Compile Include="WhenUsingNonDefaultConstructor.cs" />
Expand Down
7 changes: 7 additions & 0 deletions src/Mapster.Tests/WhenMappingPrimitives.cs
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,13 @@ public void Able_To_Map_Immutable_Class_With_MapWith()
.Compile();
}

[TestMethod]
public void String_Parse()
{
var i = "123".Adapt<int>();
i.ShouldBe(123);
}

public class ImmutableA
{
public ImmutableA(string name)
Expand Down
36 changes: 36 additions & 0 deletions src/Mapster.Tests/WhenMappingWithOpenGenerics.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Shouldly;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Mapster.Tests
{
[TestClass]
public class WhenMappingWithOpenGenerics
{
[TestMethod]
public void Map_With_Open_Generics()
{
TypeAdapterConfig.GlobalSettings.ForType(typeof(GenericPoco<>), typeof(GenericDto<>))
.Map("value", "Value");

var poco = new GenericPoco<int> { Value = 123 };
var dto = poco.Adapt<GenericDto<int>>();
dto.value.ShouldBe(poco.Value);
}

public class GenericPoco<T>
{
public T Value { get; set; }
}

public class GenericDto<T>
{
public T value { get; set; }
}

}
}
42 changes: 42 additions & 0 deletions src/Mapster.Tests/WhenUsingAttribute.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Shouldly;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Mapster.Tests
{
[TestClass]
public class WhenUsingAttribute
{
[TestMethod]
public void Using_Attributes()
{
var id = Guid.NewGuid();
var poco = new SimplePoco(id) { Name = "test" };
var dto = poco.Adapt<SimpleDto>();
dto.Id.ShouldBe(id);
dto.Name.ShouldBeNull();
}

public class SimplePoco
{
public SimplePoco(Guid id) { this.id = id; }

[AdaptMember("Id")]
private Guid id { get; }

[AdaptIgnore]
public string Name { get; set; }
}

public class SimpleDto
{
public Guid Id { get; set; }
public string Name { get; set; }
}

}
}
2 changes: 2 additions & 0 deletions src/Mapster.Tests/packages.config
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="ExpressionDebugger" version="1.0.2" targetFramework="net451" />
<package id="Newtonsoft.Json" version="10.0.2" targetFramework="net451" />
<package id="Shouldly" version="2.8.2" targetFramework="net451" />
</packages>
2 changes: 1 addition & 1 deletion src/Mapster.sln
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.26430.4
VisualStudioVersion = 15.0.26430.6
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Benchmark", "Benchmark\Benchmark.csproj", "{CFC3298D-C6C3-4B3D-B342-D4E7D2B47C5F}"
EndProject
Expand Down
5 changes: 4 additions & 1 deletion src/Mapster/Adapters/ClassAdapter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,10 @@ internal class ClassAdapter : BaseClassAdapter

protected override bool CanMap(PreCompileArgument arg)
{
if (!arg.DestinationType.IsPoco())
var bindingFlags = arg.ExplicitMapping
? BindingFlags.Public | BindingFlags.NonPublic
: BindingFlags.Public;
if (!arg.DestinationType.IsPoco(bindingFlags))
return false;

return true;
Expand Down
2 changes: 1 addition & 1 deletion src/Mapster/Adapters/ObjectAdapter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ protected override Expression CreateInstantiationExpression(Expression source, E
{
var srcType = arg.SourceType;
var destType = arg.DestinationType;
if (srcType != destType)
if (srcType == destType)
return source;
else if (destType == typeof(object))
return Expression.Convert(source, destType);
Expand Down
2 changes: 1 addition & 1 deletion src/Mapster/Adapters/RecordTypeAdapter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ namespace Mapster.Adapters
{
internal class RecordTypeAdapter : BaseClassAdapter
{
protected override int Score => -151;
protected override int Score => -149;

protected override bool CanMap(PreCompileArgument arg)
{
Expand Down
2 changes: 1 addition & 1 deletion src/Mapster/Mapster.NetCore.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
<GenerateAssemblyCopyrightAttribute>false</GenerateAssemblyCopyrightAttribute>
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
<Version>3.0.1</Version>
<Version>3.0.2</Version>
</PropertyGroup>

<ItemGroup Condition=" '$(TargetFramework)' == 'net40' ">
Expand Down
34 changes: 33 additions & 1 deletion src/Mapster/Settings/ValueAccessingStrategy.cs
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,39 @@ private static Expression FlattenMemberFn(Expression source, IMemberModel destin
{
var strategy = arg.Settings.NameMatchingStrategy;
var destinationMemberName = destinationMember.GetMemberName(arg.Settings.GetMemberNames, strategy.DestinationMemberNameConverter);
return ReflectionUtils.GetDeepFlattening(source, destinationMemberName, arg);
return GetDeepFlattening(source, destinationMemberName, arg);
}

private static Expression GetDeepFlattening(Expression source, string propertyName, CompileArgument arg)
{
var strategy = arg.Settings.NameMatchingStrategy;
var members = source.Type.GetFieldsAndProperties();
foreach (var member in members)
{
if (!member.ShouldMapMember(arg.Settings.ShouldMapMember, MemberSide.Source))
continue;
var sourceMemberName = member.GetMemberName(arg.Settings.GetMemberNames, strategy.SourceMemberNameConverter);
var propertyType = member.Type;
if (propertyType.GetTypeInfo().IsClass && propertyType != typeof(string)
&& propertyName.StartsWith(sourceMemberName))
{
var exp = member.GetExpression(source);
var ifTrue = GetDeepFlattening(exp, propertyName.Substring(sourceMemberName.Length).TrimStart('_'), arg);
if (ifTrue == null)
continue;
if (arg.MapType == MapType.Projection)
return ifTrue;
return Expression.Condition(
Expression.Equal(exp, Expression.Constant(null, exp.Type)),
Expression.Constant(ifTrue.Type.GetDefault(), ifTrue.Type),
ifTrue);
}
else if (string.Equals(propertyName, sourceMemberName))
{
return member.GetExpression(source);
}
}
return null;
}

private static Expression DictionaryFn(Expression source, IMemberModel destinationMember, CompileArgument arg)
Expand Down
11 changes: 6 additions & 5 deletions src/Mapster/TypeAdapterConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ private static List<TypeAdapterRule> CreateRuleTemplate()
return new List<TypeAdapterRule>
{
new PrimitiveAdapter().CreateRule(), //-200
new RecordTypeAdapter().CreateRule(), //-151
new ClassAdapter().CreateRule(), //-150
new RecordTypeAdapter().CreateRule(), //-149
new CollectionAdapter().CreateRule(), //-125
new DictionaryAdapter().CreateRule(), //-124
new ObjectAdapter().CreateRule(), //-111
Expand All @@ -41,8 +41,8 @@ private static List<TypeAdapterRule> CreateRuleTemplate()
NameMatchingStrategy = NameMatchingStrategy.Exact,
ShouldMapMember =
{
ShouldMapMember.AllowPublic, //match public prop
ShouldMapMember.IgnoreAdaptIgnore, //ignore AdaptIgnore attribute
ShouldMapMember.AllowPublic, //match public prop
ShouldMapMember.AllowAdaptMember, //match AdaptMember attribute
},
GetMemberNames =
Expand Down Expand Up @@ -204,20 +204,20 @@ private static TypeAdapterRule CreateDestinationTypeRule(TypeTuple key)
{
if (type1 == type2)
return 50;
if (!allowInheritance)
return null;

//generic type definition
int score = 35;
if (type2.GetTypeInfo().IsGenericTypeDefinition)
{
while (type1 != null && type1.GetGenericTypeDefinition() != type2)
while (type1 != null && type1.GetTypeInfo().IsGenericType && type1.GetGenericTypeDefinition() != type2)
{
score--;
type1 = type1.GetTypeInfo().BaseType;
}
return type1 == null ? null : (int?) score;
}
if (!allowInheritance)
return null;

if (!type2.GetTypeInfo().IsAssignableFrom(type1.GetTypeInfo()))
return null;
Expand Down Expand Up @@ -468,6 +468,7 @@ CompileArgument GetCompileArgument(TypeTuple tuple, MapType mapType, CompileCont
{
SourceType = tuple.Source,
DestinationType = tuple.Destination,
ExplicitMapping = this.RuleMap.ContainsKey(tuple),
MapType = mapType,
Context = context,
Settings = setting,
Expand Down
41 changes: 3 additions & 38 deletions src/Mapster/Utils/ReflectionUtils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@ namespace Mapster
{
internal static class ReflectionUtils
{
private static readonly Type _stringType = typeof (string);

// Primitive types with their conversion methods from System.Convert class.
private static Dictionary<Type, string> _primitiveTypes = new Dictionary<Type, string>() {
{ typeof(bool), "ToBoolean" },
Expand Down Expand Up @@ -41,12 +39,9 @@ public static bool IsNullable(this Type type)
return type.GetTypeInfo().IsGenericType && type.GetGenericTypeDefinition() == typeof (Nullable<>);
}

public static bool IsPoco(this Type type)
public static bool IsPoco(this Type type, BindingFlags accessorFlags = BindingFlags.Public)
{
if (type.GetTypeInfo().IsEnum)
return false;

return type.GetFieldsAndProperties(allowNoSetter: false).Any();
return type.GetFieldsAndProperties(allowNoSetter: false, accessorFlags: accessorFlags).Any();
}

public static IEnumerable<IMemberModelEx> GetFieldsAndProperties(this Type type, bool allowNoSetter = true, BindingFlags accessorFlags = BindingFlags.Public)
Expand All @@ -66,7 +61,7 @@ public static IEnumerable<IMemberModelEx> GetFieldsAndProperties(this Type type,

public static bool IsCollection(this Type type)
{
return typeof (IEnumerable).GetTypeInfo().IsAssignableFrom(type.GetTypeInfo()) && type != _stringType;
return typeof (IEnumerable).GetTypeInfo().IsAssignableFrom(type.GetTypeInfo()) && type != typeof(string);
}

public static Type ExtractCollectionType(this Type collectionType)
Expand Down Expand Up @@ -146,36 +141,6 @@ public static MemberExpression GetMemberInfo(Expression method, bool noThrow = f
return memberExpr;
}

public static Expression GetDeepFlattening(Expression source, string propertyName, CompileArgument arg)
{
var strategy = arg.Settings.NameMatchingStrategy;
var properties = source.Type.GetFieldsAndProperties();
foreach (var property in properties)
{
var sourceMemberName = property.GetMemberName(arg.Settings.GetMemberNames, strategy.SourceMemberNameConverter);
var propertyType = property.Type;
if (propertyType.GetTypeInfo().IsClass && propertyType != _stringType
&& propertyName.StartsWith(sourceMemberName))
{
var exp = property.GetExpression(source);
var ifTrue = GetDeepFlattening(exp, propertyName.Substring(sourceMemberName.Length).TrimStart('_'), arg);
if (ifTrue == null)
continue;
if (arg.MapType == MapType.Projection)
return ifTrue;
return Expression.Condition(
Expression.Equal(exp, Expression.Constant(null, exp.Type)),
Expression.Constant(ifTrue.Type.GetDefault(), ifTrue.Type),
ifTrue);
}
else if (string.Equals(propertyName, sourceMemberName))
{
return property.GetExpression(source);
}
}
return null;
}

public static bool IsReferenceAssignableFrom(this Type destType, Type srcType)
{
if (destType == srcType)
Expand Down

0 comments on commit 3e38326

Please sign in to comment.