fix: honor per-type ShallowCopyForSameType overrides (#938)#974
Conversation
Use merged mapping settings when deciding same-type shallow copy so a global default can be overridden per explicit config without disabling nested same-type shallow copies for other types. Co-authored-by: Cursor <cursoragent@cursor.com>
|
Fixes #938 — uses merged settings for same-type shallow copy when GitHub Actions workflows are waiting on maintainer approval for fork PRs ( |
| public class WhenPerTypeShallowCopyOverride | ||
| { | ||
| [TestMethod] | ||
| public void GlobalShallowCopy_WithPerTypeDeepCopy_ShouldRestoreViaMapToTarget() |
There was a problem hiding this comment.
This test is not valid and fail ❌
To obtain the desired result, the issue author used an incorrect configuration.
This is what the author wanted to achieve.
[TestMethod]
public void GlobalShallowCopy_WithPerTypeDeepCopy_ShouldRestoreViaMapToTarget()
{
var cfg = new TypeAdapterConfig();
cfg.RequireExplicitMapping = true;
cfg.Default.AvoidInlineMapping(true);
cfg.NewConfig<RandomObject2, RandomObject2>();
cfg.NewConfig<RandomObject1, RandomObject1>()
.Include<RandomObject2, RandomObject2>();
cfg.NewConfig<MyFailStuff, MyFailStuff>()
// .ShallowCopyForSameType(true)
.UseDestinationValue(x => x.Item1)
.UseDestinationValue(x => x.Item2);
cfg.Compile();
var mapper = new Mapper(cfg);
var dynamicStuff = new MyFailStuff();
dynamicStuff.Item1 = new RandomObject1();
dynamicStuff.Item1.SampleName = "SN1";
dynamicStuff.Item2 = new RandomObject2() { SampleNumber = 2 };
//dynamicStuff.Item2.SampleNumber = 2;
var str = dynamicStuff.BuildAdapter(cfg).CreateMapToTargetExpression<MyFailStuff>();
var originalStuff = mapper.Map<MyFailStuff, MyFailStuff>(dynamicStuff);
dynamicStuff.Item1.SampleName = "SN1CHANGED";
(dynamicStuff.Item2 as RandomObject2).SampleNumber = 3;
mapper.Map(originalStuff, dynamicStuff);
dynamicStuff.Item1.SampleName.ShouldBe("SN1");
(dynamicStuff.Item2 as RandomObject2).SampleNumber.ShouldBe(2);
ReferenceEquals(dynamicStuff.Item1, originalStuff.Item1).ShouldBeFalse();
ReferenceEquals(dynamicStuff.Item2, originalStuff.Item2).ShouldBeFalse();
}
public class RandomObject1
{
public string? SampleName { get; set; }
}
public class RandomObject2 : RandomObject1
{
public int SampleNumber { get; set; }
}
| if (_source.Type == destinationType && shallowCopySettings.ShallowCopyForSameType == true | ||
| && notUsingDestinationValue) | ||
| exp = _source; |
There was a problem hiding this comment.
This is the wrong level of mapping.
This is where properties are mapped, not types.
A shallow copy creates a new top-level object but does not duplicate nested elements. Instead, it copies the memory references of nested objects and arrays. This means modifying a nested property in the copy will inadvertently change it in the original object.
If you consider this a new TopLevel, a new object must be created here in any case, and its properties must be copied by reference.
Or if this is a parsing of the properties of the original TopLevel (the one specified in arg).
Then searching for this setting is pointless, since all TopLevel properties must be copied by reference in any case.
Restore the rule == null guard alongside merged settings so explicit type configs still map nested members on MapToTarget instead of reusing source references. Co-authored-by: Cursor <cursoragent@cursor.com>
|
CI fix pushed (
Restored Please re-run / approve CI when convenient. |
|
Friendly ping: CI fix is on |
Summary
GetMergedSettingsfor the current source/destination pair instead of onlyarg.Settingsand skipping whenever an explicit rule exists.cfg.Default.ShallowCopyForSameType(true)withcfg.NewConfig<T,T>().ShallowCopyForSameType(false)while preserving nested same-type shallow copy for types without an explicit override.Fixes #938
Test plan
GlobalShallowCopy_WithPerTypeDeepCopy_ShouldRestoreViaMapToTargetParentDeepCopyOverride_ShouldNotDisableImplicitNestedShallowCopyForSameTypedevelopment