Skip to content

Commit e2674aa

Browse files
committed
Making object tracking optional
1 parent 3c63d56 commit e2674aa

12 files changed

+274
-149
lines changed

AgileMapper.UnitTests/Members/MemberTestsBase.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
public abstract class MemberTestsBase
1111
{
12+
internal static readonly MapperContext DefaultMapperContext = new MapperContext();
1213
internal static readonly MemberFinder MemberFinder = GlobalContext.Instance.MemberFinder;
1314

1415
internal IQualifiedMember SourceMemberFor<T>(T sourceObject)

AgileMapper.UnitTests/Members/WhenDeterminingRecursion.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ public class WhenDeterminingRecursion : MemberTestsBase
1010
[Fact]
1111
public void ShouldNotCountARootMemberAsRecursive()
1212
{
13-
var rootMember = new RootQualifiedMemberFactory(MapperContext.Default)
13+
var rootMember = new RootQualifiedMemberFactory(DefaultMapperContext)
1414
.RootTarget<PublicProperty<string>>();
1515

1616
rootMember.IsRecursive.ShouldBeFalse();

AgileMapper.UnitTests/Members/WhenFindingDataSources.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ public void ShouldNotMatchSameNameIncompatibleTypeProperties()
1313
var source = new TwoValues { Value = new int[5], value = string.Empty };
1414
var targetMember = TargetMemberFor<PublicProperty<byte>>(x => x.Value);
1515

16-
var mappingContext = new MappingExecutor<TwoValues>(new MappingRuleSetCollection().CreateNew, MapperContext.Default);
16+
var mappingContext = new MappingExecutor<TwoValues>(new MappingRuleSetCollection().CreateNew, DefaultMapperContext);
1717
var rootMappingData = mappingContext.CreateRootMappingData(source, targetMember);
1818
var rootMapperData = rootMappingData.MapperData;
1919

AgileMapper.UnitTests/WhenMappingCircularReferences.cs

Lines changed: 187 additions & 146 deletions
Large diffs are not rendered by default.

AgileMapper.UnitTests/WhenViewingMappingPlans.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,20 @@ public void ShouldShowMapElementCalls()
200200
plan.ShouldContain("products.Add(oaToPsData.Map(objectArray[i]");
201201
}
202202

203+
[Fact]
204+
public void ShouldShowObjectTracking()
205+
{
206+
using (var mapper = Mapper.CreateNew())
207+
{
208+
mapper.WhenMapping.TrackMappedObjects();
209+
210+
var plan = mapper.GetPlanFor<Parent>().ToANew<Parent>();
211+
212+
plan.ShouldContain("pToPData.TryGet(sourceChild.EldestParent, out parent)");
213+
plan.ShouldContain("pToPData.Register(sourceChild.EldestParent, parent)");
214+
}
215+
}
216+
203217
[Fact]
204218
public void ShouldNotDuplicateChildMappingPlans()
205219
{

AgileMapper/Api/Configuration/IFullMappingConfigurator.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,16 @@ public interface IFullMappingConfigurator<TSource, TTarget> : IConditionalMappin
3636
/// </param>
3737
void PassExceptionsTo(Action<IMappingExceptionData<TSource, TTarget>> callback);
3838

39+
/// <summary>
40+
/// Configure this mapper to keep track of objects during a mapping in order to short-circuit
41+
/// circular relationships and ensure a 1-to-1 relationship between source and mapped objects.
42+
/// </summary>
43+
/// <returns>
44+
/// A MappingConfigContinuation to enable further configuration of mappings from and to the source and
45+
/// target type being configured.
46+
/// </returns>
47+
MappingConfigContinuation<TSource, TTarget> TrackMappedObjects();
48+
3949
/// <summary>
4050
/// Configure a derived target type to which to map instances of the given derived source type.
4151
/// </summary>

AgileMapper/Api/Configuration/MappingConfigStartingPoint.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,17 @@ private void UseNamePatterns(IEnumerable<string> patterns)
169169

170170
#endregion
171171

172+
#region Object Equality
173+
174+
/// <summary>
175+
/// Configure this mapper to keep track of objects during a mapping in order to short-circuit
176+
/// circular relationships and ensure a 1-to-1 relationship between source and mapped objects.
177+
/// </summary>
178+
public void TrackMappedObjects()
179+
=> _mapperContext.UserConfigurations.Add(ObjectTrackingMode.TrackAll(_mapperContext));
180+
181+
#endregion
182+
172183
/// <summary>
173184
/// Configure how this mapper maps objects of the type specified by the type argument.
174185
/// </summary>

AgileMapper/Api/Configuration/MappingConfigurator.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,15 @@ public void PassExceptionsTo(Action<IMappingExceptionData<TSource, TTarget>> cal
5656
_configInfo.MapperContext.UserConfigurations.Add(exceptionCallback);
5757
}
5858

59+
public MappingConfigContinuation<TSource, TTarget> TrackMappedObjects()
60+
{
61+
var trackingMode = new ObjectTrackingMode(_configInfo.ForTargetType<TTarget>());
62+
63+
_configInfo.MapperContext.UserConfigurations.Add(trackingMode);
64+
65+
return new MappingConfigContinuation<TSource, TTarget>(_configInfo);
66+
}
67+
5968
public MappingConfigContinuation<TSource, TTarget> Ignore(params Expression<Func<TTarget, object>>[] targetMembers)
6069
{
6170
var configInfo = _configInfo.ForTargetType<TTarget>();

AgileMapper/Configuration/ConfiguredIgnoredMember.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,17 @@ namespace AgileObjects.AgileMapper.Configuration
22
{
33
using System.Linq.Expressions;
44

5+
internal class ObjectTrackingMode : UserConfiguredItemBase
6+
{
7+
public ObjectTrackingMode(MappingConfigInfo configInfo)
8+
: base(configInfo)
9+
{
10+
}
11+
12+
public static ObjectTrackingMode TrackAll(MapperContext mapperContext)
13+
=> new ObjectTrackingMode(MappingConfigInfo.AllRuleSetsSourceTypesAndTargetTypes(mapperContext));
14+
}
15+
516
internal class ConfiguredIgnoredMember : UserConfiguredItemBase
617
{
718
public ConfiguredIgnoredMember(MappingConfigInfo configInfo, LambdaExpression targetMemberLambda)

AgileMapper/Configuration/UserConfigurationSet.cs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
internal class UserConfigurationSet
1313
{
14+
private readonly ICollection<ObjectTrackingMode> _trackingModeSettings;
1415
private readonly ICollection<ConfiguredObjectFactory> _objectFactories;
1516
private readonly ICollection<ConfiguredIgnoredMember> _ignoredMembers;
1617
private readonly ICollection<ConfiguredDataSourceFactory> _dataSourceFactories;
@@ -20,6 +21,7 @@ internal class UserConfigurationSet
2021

2122
public UserConfigurationSet()
2223
{
24+
_trackingModeSettings = new List<ObjectTrackingMode>();
2325
_objectFactories = new List<ConfiguredObjectFactory>();
2426
Identifiers = new MemberIdentifierSet();
2527
_ignoredMembers = new List<ConfiguredIgnoredMember>();
@@ -30,6 +32,23 @@ public UserConfigurationSet()
3032
DerivedTypes = new DerivedTypePairSet();
3133
}
3234

35+
#region Tracking Modes
36+
37+
public void Add(ObjectTrackingMode trackingMode) => _trackingModeSettings.Add(trackingMode);
38+
39+
public bool DisableObjectTracking(IBasicMapperData basicData)
40+
{
41+
if (_trackingModeSettings.None())
42+
{
43+
// Object tracking switched off by default:
44+
return true;
45+
}
46+
47+
return _trackingModeSettings.All(tm => !tm.AppliesTo(basicData));
48+
}
49+
50+
#endregion
51+
3352
#region ObjectFactories
3453

3554
public void Add(ConfiguredObjectFactory objectFactory) => _objectFactories.Add(objectFactory);

0 commit comments

Comments
 (0)