Skip to content

Commit

Permalink
fix MapsterMapper#242 Fork setting
Browse files Browse the repository at this point in the history
  • Loading branch information
chaowlert committed Jul 28, 2020
1 parent bdfde07 commit f21d4fc
Show file tree
Hide file tree
Showing 6 changed files with 166 additions and 50 deletions.
84 changes: 84 additions & 0 deletions src/Mapster.Tests/WhenForkingConfig.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Shouldly;
using System;
using System.Collections.Generic;

namespace Mapster.Tests
{
Expand Down Expand Up @@ -36,6 +37,73 @@ public void Forked_Config_Should_Not_Apply_To_Parent()
dtoParent.Name2.ShouldBe("Name2Parent");
}

[TestMethod]
public void Fork_Setting()
{
var config = new TypeAdapterConfig();
config.NewConfig<SimplePoco, SimpleDto>()
.Fork(cfg =>
cfg.ForType<string, string>()
.MapToTargetWith((src, dest) => string.IsNullOrEmpty(src) ? dest : src));

var poco = new SimplePoco
{
Id = Guid.NewGuid(),
Name1 = "Name1",
Name2 = "",
};

var dto = new SimpleDto
{
Id = poco.Id,
Name1 = "Foo",
Name2 = "Bar",
};

poco.Adapt(dto, config);

dto.Name1.ShouldBe(poco.Name1);
dto.Name2.ShouldBe("Bar");

var str = poco.Name2.Adapt(dto.Name2, config);
str.ShouldBe(poco.Name2);
}

[TestMethod]
public void Fork_Setting_2()
{
var config = new TypeAdapterConfig();
config.NewConfig<ParentPoco, ParentPoco>()
.Fork(cfg => cfg.Default.PreserveReference(true));

var grandChild = new GrandChildPoco
{
Id = Guid.NewGuid().ToString()
};
var child = new ChildPoco
{
GrandChildren = new List<GrandChildPoco>
{
grandChild, grandChild
}
};
var parent = new ParentPoco
{
Children = new List<ChildPoco>
{
child, child
}
};

var cloned = parent.Adapt<ParentPoco>(config);

cloned.Children[0].GrandChildren[0].ShouldBeSameAs(cloned.Children[1].GrandChildren[1]);

var childCloned = child.Adapt<ChildPoco>(config);

childCloned.GrandChildren[0].ShouldNotBeSameAs(childCloned.GrandChildren[1]);
}

#region test classes
public class SimplePoco
{
Expand All @@ -50,6 +118,22 @@ public class SimpleDto
public string Name1 { get; set; }
public string Name2 { get; set; }
}

class ParentPoco
{
public string Id { get; set; }
public List<ChildPoco> Children { get; set; }
}
class ChildPoco
{
public string Id { get; set; }
public List<GrandChildPoco> GrandChildren { get; set; }
}
class GrandChildPoco
{
public string Id { get; set; }
}

#endregion

}
Expand Down
5 changes: 3 additions & 2 deletions src/Mapster/Compile/CompileContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ namespace Mapster
public class CompileContext
{
public HashSet<TypeTuple> Running { get; } = new HashSet<TypeTuple>();
public TypeAdapterConfig Config { get; }
public Stack<TypeAdapterConfig> Configs { get; } = new Stack<TypeAdapterConfig>();
public TypeAdapterConfig Config => Configs.Peek();
public int? MaxDepth { get; set; }
public int Depth { get; set; }
public HashSet<ParameterExpression> ExtraParameters { get; } = new HashSet<ParameterExpression>();
Expand All @@ -19,7 +20,7 @@ internal bool IsSubFunction()

public CompileContext(TypeAdapterConfig config)
{
this.Config = config;
Configs.Push(config);
}
}
}
21 changes: 16 additions & 5 deletions src/Mapster/TypeAdapterConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -304,13 +304,24 @@ internal LambdaExpression CreateMapExpression(TypeTuple tuple, MapType mapType)
{
var context = new CompileContext(this);
context.Running.Add(tuple);
Action<TypeAdapterConfig>? fork = null;
try
{
var arg = GetCompileArgument(tuple, mapType, context);
fork = arg.Settings.Fork;
if (fork != null)
{
var cloned = this.Clone();
fork(cloned);
context.Configs.Push(cloned);
arg.Settings = cloned.GetMergedSettings(tuple, mapType);
}
return CreateMapExpression(arg);
}
finally
{
if (fork != null)
context.Configs.Pop();
context.Running.Remove(tuple);
}
}
Expand Down Expand Up @@ -475,7 +486,7 @@ internal TypeAdapterSettings GetMergedSettings(TypeTuple tuple, MapType mapType)
private CompileArgument GetCompileArgument(TypeTuple tuple, MapType mapType, CompileContext context)
{
var setting = GetMergedSettings(tuple, mapType);
var arg = new CompileArgument
return new CompileArgument
{
SourceType = tuple.Source,
DestinationType = tuple.Destination,
Expand All @@ -484,7 +495,6 @@ private CompileArgument GetCompileArgument(TypeTuple tuple, MapType mapType, Com
Context = context,
Settings = setting,
};
return arg;
}

public void Compile()
Expand Down Expand Up @@ -597,8 +607,9 @@ public TypeAdapterConfig Clone()
return fn(this);
}

private readonly ConcurrentDictionary<string, TypeAdapterConfig> _inlineConfigs =
new ConcurrentDictionary<string, TypeAdapterConfig>();
private ConcurrentDictionary<string, TypeAdapterConfig>? _inlineConfigs;
private ConcurrentDictionary<string, TypeAdapterConfig> InlineConfigs =>
_inlineConfigs ??= new ConcurrentDictionary<string, TypeAdapterConfig>();
public TypeAdapterConfig Fork(Action<TypeAdapterConfig> action,
#if !NET40
[CallerFilePath]
Expand All @@ -610,7 +621,7 @@ public TypeAdapterConfig Clone()
int key2 = 0)
{
var key = $"{key1}|{key2}";
return _inlineConfigs.GetOrAdd(key, _ =>
return InlineConfigs.GetOrAdd(key, _ =>
{
var cloned = this.Clone();
action(cloned);
Expand Down
4 changes: 2 additions & 2 deletions src/Mapster/TypeAdapterRule.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ namespace Mapster
{
public class TypeAdapterRule
{
public Func<PreCompileArgument, int?> Priority;
public TypeAdapterSettings Settings;
public Func<PreCompileArgument, int?> Priority { get; set; }
public TypeAdapterSettings Settings { get; set; }
}
}
15 changes: 15 additions & 0 deletions src/Mapster/TypeAdapterSetter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -617,6 +617,14 @@ internal TypeAdapterSetter(TypeAdapterSettings settings, TypeAdapterConfig paren
return this;
}

public TypeAdapterSetter<TSource, TDestination> Fork(Action<TypeAdapterConfig> action)
{
this.CheckCompiled();

Settings.Fork = action;
return this;
}

public TwoWaysTypeAdapterSetter<TSource, TDestination> TwoWays()
{
return new TwoWaysTypeAdapterSetter<TSource, TDestination>(this.Config);
Expand Down Expand Up @@ -841,5 +849,12 @@ public TwoWaysTypeAdapterSetter(TypeAdapterConfig config)
DestinationToSourceSetter.MaxDepth(value);
return this;
}

public TwoWaysTypeAdapterSetter<TSource, TDestination> Fork(Action<TypeAdapterConfig> action)
{
SourceToDestinationSetter.Fork(action);
DestinationToSourceSetter.Fork(action);
return this;
}
}
}
87 changes: 46 additions & 41 deletions src/Mapster/TypeAdapterSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,124 +10,129 @@ public class TypeAdapterSettings : SettingStore
{
public IgnoreDictionary Ignore
{
get => Get("Ignore", () => new IgnoreDictionary());
get => Get(nameof(Ignore), () => new IgnoreDictionary());
}
public List<DestinationTransform> DestinationTransforms
{
get => Get("DestinationTransforms", () => new List<DestinationTransform>());
get => Get(nameof(DestinationTransforms), () => new List<DestinationTransform>());
}
public NameMatchingStrategy NameMatchingStrategy
{
get => Get("NameMatchingStrategy", () => new NameMatchingStrategy());
set => Set("NameMatchingStrategy", value);
get => Get(nameof(NameMatchingStrategy), () => new NameMatchingStrategy());
set => Set(nameof(NameMatchingStrategy), value);
}

public bool? PreserveReference
{
get => Get("PreserveReference");
set => Set("PreserveReference", value);
get => Get(nameof(PreserveReference));
set => Set(nameof(PreserveReference), value);
}
public bool? ShallowCopyForSameType
{
get => Get("ShallowCopyForSameType");
set => Set("ShallowCopyForSameType", value);
get => Get(nameof(ShallowCopyForSameType));
set => Set(nameof(ShallowCopyForSameType), value);
}
public bool? IgnoreNullValues
{
get => Get("IgnoreNullValues");
set => Set("IgnoreNullValues", value);
get => Get(nameof(IgnoreNullValues));
set => Set(nameof(IgnoreNullValues), value);
}
public bool? MapEnumByName
{
get => Get("MapEnumByName");
set => Set("MapEnumByName", value);
get => Get(nameof(MapEnumByName));
set => Set(nameof(MapEnumByName), value);
}
public bool? IgnoreNonMapped
{
get => Get("IgnoreNonMapped");
set => Set("IgnoreNonMapped", value);
get => Get(nameof(IgnoreNonMapped));
set => Set(nameof(IgnoreNonMapped), value);
}
public bool? AvoidInlineMapping
{
get => Get("AvoidInlineMapping");
set => Set("AvoidInlineMapping", value);
get => Get(nameof(AvoidInlineMapping));
set => Set(nameof(AvoidInlineMapping), value);
}
public int? MaxDepth
{
get => (int?) Get<object>("MaxDepth");
set => Set("MaxDepth", value);
get => (int?) Get<object>(nameof(MaxDepth));
set => Set(nameof(MaxDepth), value);
}
public bool? Unflattening
{
get => Get("Unflattening");
set => Set("Unflattening", value);
get => Get(nameof(Unflattening));
set => Set(nameof(Unflattening), value);
}
public bool? SkipDestinationMemberCheck
{
get => Get("SkipDestinationMemberCheck");
set => Set("SkipDestinationMemberCheck", value);
get => Get(nameof(SkipDestinationMemberCheck));
set => Set(nameof(SkipDestinationMemberCheck), value);
}
public bool? EnableNonPublicMembers
{
get => Get("EnableNonPublicMembers");
set => Set("EnableNonPublicMembers", value);
get => Get(nameof(EnableNonPublicMembers));
set => Set(nameof(EnableNonPublicMembers), value);
}

public List<Func<IMemberModel, MemberSide, bool?>> ShouldMapMember
{
get => Get("ShouldMapMember", () => new List<Func<IMemberModel, MemberSide, bool?>>());
get => Get(nameof(ShouldMapMember), () => new List<Func<IMemberModel, MemberSide, bool?>>());
}
public List<Func<Expression, IMemberModel, CompileArgument, Expression?>> ValueAccessingStrategies
{
get => Get("ValueAccessingStrategies", () => new List<Func<Expression, IMemberModel, CompileArgument, Expression?>>());
get => Get(nameof(ValueAccessingStrategies), () => new List<Func<Expression, IMemberModel, CompileArgument, Expression?>>());
}
public List<InvokerModel> Resolvers
{
get => Get("Resolvers", () => new List<InvokerModel>());
get => Get(nameof(Resolvers), () => new List<InvokerModel>());
}
public List<object> ExtraSources
{
get => Get("ExtraSources", () => new List<object>());
get => Get(nameof(ExtraSources), () => new List<object>());
}
public List<Func<CompileArgument, LambdaExpression>> BeforeMappingFactories
{
get => Get("BeforeMappingFactories", () => new List<Func<CompileArgument, LambdaExpression>>());
get => Get(nameof(BeforeMappingFactories), () => new List<Func<CompileArgument, LambdaExpression>>());
}
public List<Func<CompileArgument, LambdaExpression>> AfterMappingFactories
{
get => Get("AfterMappingFactories", () => new List<Func<CompileArgument, LambdaExpression>>());
get => Get(nameof(AfterMappingFactories), () => new List<Func<CompileArgument, LambdaExpression>>());
}
public List<TypeTuple> Includes
{
get => Get("Includes", () => new List<TypeTuple>());
get => Get(nameof(Includes), () => new List<TypeTuple>());
}
public List<Func<IMemberModel, string?>> GetMemberNames
{
get => Get("GetMemberNames", () => new List<Func<IMemberModel, string?>>());
get => Get(nameof(GetMemberNames), () => new List<Func<IMemberModel, string?>>());
}
public List<Func<IMemberModel, bool>> UseDestinationValues
{
get => Get("UseDestinationValues", () => new List<Func<IMemberModel, bool>>());
get => Get(nameof(UseDestinationValues), () => new List<Func<IMemberModel, bool>>());
}
public Func<CompileArgument, LambdaExpression>? ConstructUsingFactory
{
get => Get<Func<CompileArgument, LambdaExpression>>("ConstructUsingFactory");
set => Set("ConstructUsingFactory", value);
get => Get<Func<CompileArgument, LambdaExpression>>(nameof(ConstructUsingFactory));
set => Set(nameof(ConstructUsingFactory), value);
}
public Func<CompileArgument, LambdaExpression>? ConverterFactory
{
get => Get<Func<CompileArgument, LambdaExpression>>("ConverterFactory");
set => Set("ConverterFactory", value);
get => Get<Func<CompileArgument, LambdaExpression>>(nameof(ConverterFactory));
set => Set(nameof(ConverterFactory), value);
}
public Func<CompileArgument, LambdaExpression>? ConverterToTargetFactory
{
get => Get<Func<CompileArgument, LambdaExpression>>("ConverterToTargetFactory");
set => Set("ConverterToTargetFactory", value);
get => Get<Func<CompileArgument, LambdaExpression>>(nameof(ConverterToTargetFactory));
set => Set(nameof(ConverterToTargetFactory), value);
}
public object? MapToConstructor
{
get => Get<object>("MapToConstructor");
set => Set("MapToConstructor", value);
get => Get<object>(nameof(MapToConstructor));
set => Set(nameof(MapToConstructor), value);
}
public Action<TypeAdapterConfig>? Fork
{
get => Get<Action<TypeAdapterConfig>>(nameof(Fork));
set => Set(nameof(Fork), value);
}

internal bool Compiled { get; set; }
Expand Down

0 comments on commit f21d4fc

Please sign in to comment.