Skip to content

Commit

Permalink
UseInterfaceHierarchy configuration
Browse files Browse the repository at this point in the history
  • Loading branch information
satano committed Jan 18, 2020
1 parent d526e0a commit 7f95158
Show file tree
Hide file tree
Showing 7 changed files with 129 additions and 22 deletions.
88 changes: 86 additions & 2 deletions src/Mapster.Tests/WhenMappingToInterface.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ public void MapToInterface()
}

[TestMethod]
public void MapToInheritedInterface()
public void MapToInheritedInterface_InterfaceHierarchyEnabled()
{
var dto = new InheritedDto
{
Expand All @@ -46,7 +46,10 @@ public void MapToInheritedInterface()
UnmappedSource = "Lorem ipsum"
};

IInheritedDto idto = dto.Adapt<IInheritedDto>();
var config = new TypeAdapterConfig();
config.Default.UseInterfaceHierarchy(true);

IInheritedDto idto = dto.Adapt<IInheritedDto>(config);

idto.ShouldNotBeNull();
idto.ShouldSatisfyAllConditions(
Expand All @@ -57,6 +60,79 @@ public void MapToInheritedInterface()
);
}

[TestMethod]
public void MapToInheritedInterface_InterfaceHierarchyDisabled()
{
var dto = new InheritedDto
{
Id = 1,
Name = "Test",
DateOfBirth = new DateTime(1978, 12, 10),
UnmappedSource = "Lorem ipsum"
};

var config = new TypeAdapterConfig();

IInheritedDto idto = dto.Adapt<IInheritedDto>(config);

idto.ShouldNotBeNull();
idto.ShouldSatisfyAllConditions(
() => idto.Id.ShouldBe(default),
() => idto.Name.ShouldBeNull(),
() => idto.DateOfBirth.ShouldBe(dto.DateOfBirth),
() => idto.UnmappedTarget.ShouldBeNull()
);
}

[TestMethod]
public void MapToInstanceWithInterface_InterfaceHierarchyEnabled()
{
var dto = new InheritedDto
{
Id = 1,
Name = "Test",
DateOfBirth = new DateTime(1978, 12, 10),
UnmappedSource = "Lorem ipsum"
};

var config = new TypeAdapterConfig();
config.Default.UseInterfaceHierarchy(true);

IInheritedDto target = new ImplementedDto();
dto.Adapt(target, config);

target.ShouldNotBeNull();
target.ShouldSatisfyAllConditions(
() => target.Id.ShouldBe(dto.Id),
() => target.Name.ShouldBe(dto.Name),
() => target.DateOfBirth.ShouldBe(dto.DateOfBirth),
() => target.UnmappedTarget.ShouldBeNull()
);
}

[TestMethod]
public void MapToInstanceWithInterface_InterfaceHierarchyDisabled()
{
var dto = new InheritedDto
{
Id = 1,
Name = "Test",
DateOfBirth = new DateTime(1978, 12, 10),
UnmappedSource = "Lorem ipsum"
};

IInheritedDto target = new ImplementedDto();
dto.Adapt(target);

target.ShouldNotBeNull();
target.ShouldSatisfyAllConditions(
() => target.Id.ShouldBe(default),
() => target.Name.ShouldBeNull(),
() => target.DateOfBirth.ShouldBe(dto.DateOfBirth),
() => target.UnmappedTarget.ShouldBeNull()
);
}

[TestMethod]
public void MapToComplexInterface()
{
Expand Down Expand Up @@ -202,6 +278,14 @@ public class InheritedDto
public string UnmappedSource { get; set; }
}

public class ImplementedDto : IInheritedDto
{
public int Id { get; set; }
public string Name { get; set; }
public DateTime DateOfBirth { get; set; }
public string UnmappedTarget { get; set; }
}

public interface IComplexInterface
{
string Name { get; set; }
Expand Down
2 changes: 1 addition & 1 deletion src/Mapster/Adapters/BaseClassAdapter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ protected virtual ClassModel GetSetterModel(CompileArgument arg)
{
return new ClassModel
{
Members = arg.DestinationType.GetFieldsAndProperties(requireSetter: true, accessorFlags: BindingFlags.NonPublic | BindingFlags.Public)
Members = arg.DestinationType.GetFieldsAndProperties(useInterfaceHierarchy: arg.Settings.UseInterfaceHierarchy, requireSetter: true, accessorFlags: BindingFlags.NonPublic | BindingFlags.Public)
};
}

Expand Down
6 changes: 3 additions & 3 deletions src/Mapster/Adapters/DictionaryAdapter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ protected override bool CanInline(Expression source, Expression? destination, Co
return false;

//allow inline for dict-to-dict, only when IgnoreNonMapped
return arg.SourceType.GetDictionaryType() == null
return arg.SourceType.GetDictionaryType() == null
|| arg.Settings.IgnoreNonMapped == true;
}

Expand Down Expand Up @@ -83,7 +83,7 @@ protected override Expression CreateBlockExpression(Expression source, Expressio
dict.Add(ignore.Key, setWithCondition);
}
}

//dict to switch
if (dict.Count > 0 || ignores.Count > 0)
{
Expand Down Expand Up @@ -175,7 +175,7 @@ protected override ClassModel GetSetterModel(CompileArgument arg)
if (arg.SourceType.GetDictionaryType() == null)
{
var srcNames = arg.GetSourceNames();
var propNames = arg.SourceType.GetFieldsAndProperties(accessorFlags: BindingFlags.NonPublic | BindingFlags.Public)
var propNames = arg.SourceType.GetFieldsAndProperties(useInterfaceHierarchy: arg.Settings.UseInterfaceHierarchy, accessorFlags: BindingFlags.NonPublic | BindingFlags.Public)
.Where(model => model.ShouldMapMember(arg, MemberSide.Source))
.Select(model => model.Name)
.Where(name => !srcNames.Contains(name))
Expand Down
8 changes: 4 additions & 4 deletions src/Mapster/Settings/ValueAccessingStrategy.cs
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ public static class ValueAccessingStrategy

private static Expression? PropertyOrFieldFn(Expression source, IMemberModel destinationMember, CompileArgument arg)
{
var members = source.Type.GetFieldsAndProperties(accessorFlags: BindingFlags.NonPublic | BindingFlags.Public);
var members = source.Type.GetFieldsAndProperties(useInterfaceHierarchy: arg.Settings.UseInterfaceHierarchy, accessorFlags: BindingFlags.NonPublic | BindingFlags.Public);
var strategy = arg.Settings.NameMatchingStrategy;
var destinationMemberName = destinationMember.GetMemberName(arg.Settings.GetMemberNames, strategy.DestinationMemberNameConverter);
return members
Expand Down Expand Up @@ -110,7 +110,7 @@ public static class ValueAccessingStrategy
private static Expression? GetDeepFlattening(Expression source, string propertyName, CompileArgument arg)
{
var strategy = arg.Settings.NameMatchingStrategy;
var members = source.Type.GetFieldsAndProperties(accessorFlags: BindingFlags.NonPublic | BindingFlags.Public);
var members = source.Type.GetFieldsAndProperties(useInterfaceHierarchy: arg.Settings.UseInterfaceHierarchy, accessorFlags: BindingFlags.NonPublic | BindingFlags.Public);
foreach (var member in members)
{
if (!member.ShouldMapMember(arg, MemberSide.Source))
Expand All @@ -137,7 +137,7 @@ internal static IEnumerable<InvokerModel> FindUnflatteningPairs(Expression sourc
{
var strategy = arg.Settings.NameMatchingStrategy;
var destinationMemberName = destinationMember.GetMemberName(arg.Settings.GetMemberNames, strategy.DestinationMemberNameConverter);
var members = source.Type.GetFieldsAndProperties(accessorFlags: BindingFlags.NonPublic | BindingFlags.Public);
var members = source.Type.GetFieldsAndProperties(useInterfaceHierarchy: arg.Settings.UseInterfaceHierarchy, accessorFlags: BindingFlags.NonPublic | BindingFlags.Public);

foreach (var member in members)
{
Expand All @@ -160,7 +160,7 @@ internal static IEnumerable<InvokerModel> FindUnflatteningPairs(Expression sourc
private static IEnumerable<string> GetDeepUnflattening(IMemberModel destinationMember, string propertyName, CompileArgument arg)
{
var strategy = arg.Settings.NameMatchingStrategy;
var members = destinationMember.Type.GetFieldsAndProperties(accessorFlags: BindingFlags.NonPublic | BindingFlags.Public);
var members = destinationMember.Type.GetFieldsAndProperties(useInterfaceHierarchy: arg.Settings.UseInterfaceHierarchy, accessorFlags: BindingFlags.NonPublic | BindingFlags.Public);
foreach (var member in members)
{
if (!member.ShouldMapMember(arg, MemberSide.Destination))
Expand Down
19 changes: 17 additions & 2 deletions src/Mapster/TypeAdapterSetter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,14 @@ public static class TypeAdapterSetterExtensions
setter.Settings.Unflattening = value;
return setter;
}

public static TSetter UseInterfaceHierarchy<TSetter>(this TSetter setter, bool value) where TSetter : TypeAdapterSetter
{
setter.CheckCompiled();

setter.Settings.UseInterfaceHierarchy = value;
return setter;
}
}

public class TypeAdapterSetter<TDestination> : TypeAdapterSetter
Expand Down Expand Up @@ -444,7 +452,7 @@ internal TypeAdapterSetter(TypeAdapterSettings settings, TypeAdapterConfig paren
Expression<Func<TSource, TSourceMember>> source, Expression<Func<TSource, bool>>? shouldMap = null)
{
this.CheckCompiled();

Settings.Resolvers.Add(new InvokerModel
{
DestinationMemberName = memberName,
Expand Down Expand Up @@ -553,7 +561,7 @@ internal TypeAdapterSetter(TypeAdapterSettings settings, TypeAdapterConfig paren
return this;
}

public TypeAdapterSetter<TSource, TDestination> Include<TDerivedSource, TDerivedDestination>()
public TypeAdapterSetter<TSource, TDestination> Include<TDerivedSource, TDerivedDestination>()
where TDerivedSource: class, TSource
where TDerivedDestination: class, TDestination
{
Expand Down Expand Up @@ -808,5 +816,12 @@ public TwoWaysTypeAdapterSetter(TypeAdapterConfig config)
DestinationToSourceSetter.MaxDepth(value);
return this;
}

public TwoWaysTypeAdapterSetter<TSource, TDestination> UseInterfaceHierarchy(bool value)
{
SourceToDestinationSetter.UseInterfaceHierarchy(value);
DestinationToSourceSetter.UseInterfaceHierarchy(value);
return this;
}
}
}
14 changes: 9 additions & 5 deletions src/Mapster/TypeAdapterSettings.cs
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
using System;
using Mapster.Models;
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Reflection;
using Mapster.Adapters;
using Mapster.Models;

namespace Mapster
{
public class TypeAdapterSettings: SettingStore
public class TypeAdapterSettings : SettingStore
{
public IgnoreDictionary Ignore
{
Expand Down Expand Up @@ -69,6 +67,12 @@ public NameMatchingStrategy NameMatchingStrategy
set => Set("SkipDestinationMemberCheck", value);
}

public bool? UseInterfaceHierarchy
{
get => Get(nameof(UseInterfaceHierarchy));
set => Set(nameof(UseInterfaceHierarchy), value);
}

public List<Func<IMemberModel, MemberSide, bool?>> ShouldMapMember
{
get => Get("ShouldMapMember", () => new List<Func<IMemberModel, MemberSide, bool?>>());
Expand Down
14 changes: 9 additions & 5 deletions src/Mapster/Utils/ReflectionUtils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -79,10 +79,14 @@ public static bool IsPoco(this Type type)
if (type.IsConvertible())
return false;

return type.GetFieldsAndProperties(requireSetter: true).Any();
return type.GetFieldsAndProperties(useInterfaceHierarchy: true, requireSetter: true).Any();
}

public static IEnumerable<IMemberModelEx> GetFieldsAndProperties(this Type type, bool requireSetter = false, BindingFlags accessorFlags = BindingFlags.Public)
public static IEnumerable<IMemberModelEx> GetFieldsAndProperties(
this Type type,
bool? useInterfaceHierarchy,
bool requireSetter = false,
BindingFlags accessorFlags = BindingFlags.Public)
{
var bindingFlags = BindingFlags.Instance | accessorFlags;

Expand All @@ -99,9 +103,9 @@ IEnumerable<IMemberModelEx> getFieldsFunc(Type t) => t.GetFields(bindingFlags)
IEnumerable<IMemberModelEx> fields;

#if NETSTANDARD1_3
if (type.IsInterface())
if (type.IsInterface() && (useInterfaceHierarchy == true))
#else
if (type.IsInterface)
if (type.IsInterface && (useInterfaceHierarchy == true))
#endif
{
IEnumerable<Type> allInterfaces = GetAllInterfaces(type);
Expand Down Expand Up @@ -232,7 +236,7 @@ public static bool IsRecordType(this Type type)
return false;

//no public setter
var props = type.GetFieldsAndProperties().ToList();
var props = type.GetFieldsAndProperties(useInterfaceHierarchy: true).ToList();
if (props.Any(p => p.SetterModifier == AccessModifier.Public))
return false;

Expand Down

0 comments on commit 7f95158

Please sign in to comment.