Skip to content

Commit

Permalink
fix #292 support MemberSide for AdaptMemberAttribute
Browse files Browse the repository at this point in the history
fix flattening with poco property type
  • Loading branch information
chaowlert committed Jan 9, 2021
1 parent 39ee1ba commit b56ff38
Show file tree
Hide file tree
Showing 9 changed files with 85 additions and 25 deletions.
6 changes: 6 additions & 0 deletions src/Mapster.Core/Attributes/AdaptMemberAttribute.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,17 @@ namespace Mapster
public class AdaptMemberAttribute : Attribute
{
public string? Name { get; set; }
public MemberSide? Side { get; set; }

public AdaptMemberAttribute() { }
public AdaptMemberAttribute(string name)
{
this.Name = name;
}
public AdaptMemberAttribute(string name, MemberSide side)
{
this.Name = name;
this.Side = side;
}
}
}
24 changes: 24 additions & 0 deletions src/Mapster.Tests/WhenUsingAttribute.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,15 @@ public void Using_Attributes()
dto.Name.ShouldBeNull();
}

[TestMethod]
public void Specify_MemberSide()
{
var user = new User {UserIdentification = 123};
var dto = user.Adapt<UserDto>();
var info = dto.Adapt<UserInfo>();
info.UserId.ShouldBe(123);
}

public class SimplePoco
{
public SimplePoco(Guid id) { this.id = id; }
Expand All @@ -38,5 +47,20 @@ public class SimpleDto
public string Name { get; set; }
}

public class User
{
public int UserIdentification {get; set;}
}

public class UserDto
{
[AdaptMember("UserIdentification", MemberSide.Destination)]
public int UserId {get; set;}
}

public class UserInfo
{
public int UserId {get; set;}
}
}
}
2 changes: 2 additions & 0 deletions src/Mapster.Tool/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,8 @@ private static void CreateModel(ModelOptions opt, Type type, BaseAdaptAttribute
foreach (var member in properties)
{
var adaptMember = member.GetCustomAttribute<AdaptMemberAttribute>();
if (!isTwoWays && adaptMember?.Side != null && adaptMember.Side != side)
adaptMember = null;
var propType = GetPropertyType(member, getPropType(member), attr.GetType(), opt.Namespace);
translator.Properties.Add(new PropertyDefinitions
{
Expand Down
8 changes: 7 additions & 1 deletion src/Mapster/Settings/GetMemberName.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ namespace Mapster
{
public static class GetMemberName
{
public static readonly Func<IMemberModel, string?> AdaptMember = model => model.GetCustomAttributeFromData<AdaptMemberAttribute>()?.Name;
public static readonly Func<IMemberModel, MemberSide, string?> AdaptMember = (model, side) =>
{
var memberAttr = model.GetCustomAttributeFromData<AdaptMemberAttribute>();
if (memberAttr == null)
return null;
return memberAttr.Side == null || memberAttr.Side == side ? memberAttr.Name : null;
};
}
}
8 changes: 7 additions & 1 deletion src/Mapster/Settings/ShouldMapMember.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,12 @@ public static class ShouldMapMember
return null;
return ignoreAttr.Side == null || ignoreAttr.Side == side ? (bool?) false : null;
};
public static readonly Func<IMemberModel, MemberSide, bool?> AllowAdaptMember = (model, _) => model.HasCustomAttribute<AdaptMemberAttribute>() ? (bool?)true : null;
public static readonly Func<IMemberModel, MemberSide, bool?> AllowAdaptMember = (model, side) =>
{
var memberAttr = model.GetCustomAttributeFromData<AdaptMemberAttribute>();
if (memberAttr == null)
return null;
return memberAttr.Side == null || memberAttr.Side == side ? (bool?) true : null;
};
}
}
35 changes: 18 additions & 17 deletions src/Mapster/Settings/ValueAccessingStrategy.cs
Original file line number Diff line number Diff line change
Expand Up @@ -73,10 +73,10 @@ public static class ValueAccessingStrategy
{
var members = source.Type.GetFieldsAndProperties(true);
var strategy = arg.Settings.NameMatchingStrategy;
var destinationMemberName = destinationMember.GetMemberName(arg.Settings.GetMemberNames, strategy.DestinationMemberNameConverter);
var destinationMemberName = destinationMember.GetMemberName(MemberSide.Destination, arg.Settings.GetMemberNames, strategy.DestinationMemberNameConverter);
return members
.Where(member => member.ShouldMapMember(arg, MemberSide.Source))
.Where(member => member.GetMemberName(arg.Settings.GetMemberNames, strategy.SourceMemberNameConverter) == destinationMemberName)
.Where(member => member.GetMemberName(MemberSide.Source, arg.Settings.GetMemberNames, strategy.SourceMemberNameConverter) == destinationMemberName)
.Select(member => member.GetExpression(source))
.FirstOrDefault();
}
Expand All @@ -86,7 +86,7 @@ public static class ValueAccessingStrategy
if (arg.MapType == MapType.Projection)
return null;
var strategy = arg.Settings.NameMatchingStrategy;
var destinationMemberName = "Get" + destinationMember.GetMemberName(arg.Settings.GetMemberNames, strategy.DestinationMemberNameConverter);
var destinationMemberName = "Get" + destinationMember.GetMemberName(MemberSide.Destination, arg.Settings.GetMemberNames, strategy.DestinationMemberNameConverter);
var getMethod = Array.Find(source.Type.GetMethods(BindingFlags.Public | BindingFlags.Instance), m => strategy.SourceMemberNameConverter(m.Name) == destinationMemberName && m.GetParameters().Length == 0);
if (getMethod == null)
return null;
Expand All @@ -98,7 +98,7 @@ public static class ValueAccessingStrategy
private static Expression? FlattenMemberFn(Expression source, IMemberModel destinationMember, CompileArgument arg)
{
var strategy = arg.Settings.NameMatchingStrategy;
var destinationMemberName = destinationMember.GetMemberName(arg.Settings.GetMemberNames, strategy.DestinationMemberNameConverter);
var destinationMemberName = destinationMember.GetMemberName(MemberSide.Destination, arg.Settings.GetMemberNames, strategy.DestinationMemberNameConverter);
return GetDeepFlattening(source, destinationMemberName, arg);
}

Expand All @@ -110,7 +110,11 @@ public static class ValueAccessingStrategy
{
if (!member.ShouldMapMember(arg, MemberSide.Source))
continue;
var sourceMemberName = member.GetMemberName(arg.Settings.GetMemberNames, strategy.SourceMemberNameConverter);

var sourceMemberName = member.GetMemberName(MemberSide.Source, arg.Settings.GetMemberNames, strategy.SourceMemberNameConverter);
if (string.Equals(propertyName, sourceMemberName))
return member.GetExpression(source);

var propertyType = member.Type;
if (propertyName.StartsWith(sourceMemberName) &&
(propertyType.IsPoco() || propertyType.IsRecordType()))
Expand All @@ -121,24 +125,21 @@ public static class ValueAccessingStrategy
continue;
return ifTrue;
}

if (string.Equals(propertyName, sourceMemberName))
return member.GetExpression(source);
}
return null;
}

internal static IEnumerable<InvokerModel> FindUnflatteningPairs(Expression source, IMemberModel destinationMember, CompileArgument arg)
{
var strategy = arg.Settings.NameMatchingStrategy;
var destinationMemberName = destinationMember.GetMemberName(arg.Settings.GetMemberNames, strategy.DestinationMemberNameConverter);
var destinationMemberName = destinationMember.GetMemberName(MemberSide.Destination, arg.Settings.GetMemberNames, strategy.DestinationMemberNameConverter);
var members = source.Type.GetFieldsAndProperties(true);

foreach (var member in members)
{
if (!member.ShouldMapMember(arg, MemberSide.Source))
continue;
var sourceMemberName = member.GetMemberName(arg.Settings.GetMemberNames, strategy.SourceMemberNameConverter);
var sourceMemberName = member.GetMemberName(MemberSide.Source, arg.Settings.GetMemberNames, strategy.SourceMemberNameConverter);
if (!sourceMemberName.StartsWith(destinationMemberName) || sourceMemberName == destinationMemberName)
continue;
foreach (var prop in GetDeepUnflattening(destinationMember, sourceMemberName.Substring(destinationMemberName.Length).TrimStart('_'), arg))
Expand All @@ -160,20 +161,20 @@ private static IEnumerable<string> GetDeepUnflattening(IMemberModel destinationM
{
if (!member.ShouldMapMember(arg, MemberSide.Destination))
continue;
var destMemberName = member.GetMemberName(arg.Settings.GetMemberNames, strategy.DestinationMemberNameConverter);
var destMemberName = member.GetMemberName(MemberSide.Destination, arg.Settings.GetMemberNames, strategy.DestinationMemberNameConverter);
var propertyType = member.Type;
if (propertyName.StartsWith(destMemberName) &&
if (string.Equals(propertyName, destMemberName))
{
yield return member.Name;
}
else if (propertyName.StartsWith(destMemberName) &&
(propertyType.IsPoco() || propertyType.IsRecordType()))
{
foreach (var prop in GetDeepUnflattening(member, propertyName.Substring(destMemberName.Length).TrimStart('_'), arg))
{
yield return member.Name + "." + prop;
}
}
else if (string.Equals(propertyName, destMemberName))
{
yield return member.Name;
}
}
}

Expand All @@ -184,7 +185,7 @@ private static IEnumerable<string> GetDeepUnflattening(IMemberModel destinationM
return null;

var strategy = arg.Settings.NameMatchingStrategy;
var destinationMemberName = destinationMember.GetMemberName(arg.Settings.GetMemberNames, strategy.DestinationMemberNameConverter);
var destinationMemberName = destinationMember.GetMemberName(MemberSide.Destination, arg.Settings.GetMemberNames, strategy.DestinationMemberNameConverter);
var key = Expression.Constant(destinationMemberName);
var args = dictType.GetGenericArguments();
if (strategy.SourceMemberNameConverter != MapsterHelper.Identity)
Expand Down
19 changes: 17 additions & 2 deletions src/Mapster/TypeAdapterSetter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,15 @@ public static TSetter AvoidInlineMapping<TSetter>(this TSetter setter, bool valu
return setter;
}

public static TSetter GetMemberName<TSetter>(this TSetter setter, Func<IMemberModel, string> func) where TSetter : TypeAdapterSetter
public static TSetter GetMemberName<TSetter>(this TSetter setter, Func<IMemberModel, string?> func) where TSetter : TypeAdapterSetter
{
setter.CheckCompiled();

setter.Settings.GetMemberNames.Add((member, _) => func(member));
return setter;
}

public static TSetter GetMemberName<TSetter>(this TSetter setter, Func<IMemberModel, MemberSide, string?> func) where TSetter : TypeAdapterSetter
{
setter.CheckCompiled();

Expand Down Expand Up @@ -816,13 +824,20 @@ public TwoWaysTypeAdapterSetter<TSource, TDestination> AvoidInlineMapping(bool v
return this;
}

public TwoWaysTypeAdapterSetter<TSource, TDestination> GetMemberName(Func<IMemberModel, string> func)
public TwoWaysTypeAdapterSetter<TSource, TDestination> GetMemberName(Func<IMemberModel, string?> func)
{
SourceToDestinationSetter.GetMemberName(func);
DestinationToSourceSetter.GetMemberName(func);
return this;
}

public TwoWaysTypeAdapterSetter<TSource, TDestination> GetMemberName(Func<IMemberModel, MemberSide, string?> func)
{
SourceToDestinationSetter.GetMemberName(func);
DestinationToSourceSetter.GetMemberName((model, side) => func(model, side == MemberSide.Source ? MemberSide.Destination : MemberSide.Source));
return this;
}

public TwoWaysTypeAdapterSetter<TSource, TDestination> MapToConstructor(bool value)
{
SourceToDestinationSetter.MapToConstructor(value);
Expand Down
4 changes: 2 additions & 2 deletions src/Mapster/TypeAdapterSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -106,9 +106,9 @@ public List<TypeTuple> Includes
{
get => Get(nameof(Includes), () => new List<TypeTuple>());
}
public List<Func<IMemberModel, string?>> GetMemberNames
public List<Func<IMemberModel, MemberSide, string?>> GetMemberNames
{
get => Get(nameof(GetMemberNames), () => new List<Func<IMemberModel, string?>>());
get => Get(nameof(GetMemberNames), () => new List<Func<IMemberModel, MemberSide, string?>>());
}
public List<Func<IMemberModel, bool>> UseDestinationValues
{
Expand Down
4 changes: 2 additions & 2 deletions src/Mapster/Utils/ReflectionUtils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -315,9 +315,9 @@ public static bool UseDestinationValue(this IMemberModel member, CompileArgument
return predicates.Any(predicate => predicate(member));
}

public static string GetMemberName(this IMemberModel member, List<Func<IMemberModel, string?>> getMemberNames, Func<string, string> nameConverter)
public static string GetMemberName(this IMemberModel member, MemberSide side, List<Func<IMemberModel, MemberSide, string?>> getMemberNames, Func<string, string> nameConverter)
{
var memberName = getMemberNames.Select(predicate => predicate(member))
var memberName = getMemberNames.Select(func => func(member, side))
.FirstOrDefault(name => name != null)
?? member.Name;

Expand Down

0 comments on commit b56ff38

Please sign in to comment.