I've been grappling with a bizarre Automapper behavior the last few days. My AutoMapper version is 10.1.1; at this time I do not have access to .NET Standard 2.1, so upgrading to 12.0.1 is not possible.
The relationship of the classes involved looks like the following:
//On the From side:
class FromA {
// ...stuff...
public FromB MemberB { get; set; }
// ...stuff...
}
class FromB {
// ...stuff...
}
//On the To side:
class ToA {
// ...stuff...
public ToB MemberB { get; set; }
// ...stuff...
}
class ToB {
// ...stuff...
public ICollection<ToC> MemberC { get; set; }
// ...stuff...
}
class ToC {
// ...stuff...
}
Then on the mapping configuration side, we've got:
//I've got the following support member value resolver:
internal class TypeMappingMemberValueResolver<TSource, TDestination, TSourceMember, TDestinationMember>
: IMemberValueResolver<TSource, TDestination, TSourceMember, TDestinationMember>
{
public TDestinationMember Resolve(TSource source, TDestination destination, TSourceMember sourceMember, TDestinationMember destMember, ResolutionContext context)
{
return context.Mapper.Map<TSourceMember, TDestinationMember>(sourceMember, destMember);
}
}
//Then, inside Profiles:
CreateMap<FromA, ToA>()
...
.ForMember(to => to.MemberB, opt => opt.MapFrom(new TypeMappingMmeberValueResolver<FromA, ToA, FromB, ToB>(src => src.MemberB)
...
//Here, I'm just trying to be explicit that MemberB should be mapped according to another registered map. It might be possible to do this implicitly, but I'm uncertain how.
// Regardless, my remaining map:
CreateMap<FromB, ToB>()
...
.ForMember(to => to.MemberC, opt => opt.Ignore())
Given this, when I execute:
Mapper.Map<FromA, ToA>(instanceOfFromA, instanceOfToA);
The MemberC property of instanceOfToA will now be set to an empty array. I have a number of other properties that are Ignored() in the FromB -> ToB map, and those all work fine.
My expectation is that instanceOfA.MemberB.MemberC is untouched, and particularly not altered or having its data erased.
Some other things that should not be factors:
- I don't use any global or map-wide mapping settings, everything is managed on individual maps using ForMember.
- No BeforeMap or AfterMap actions that could be screwing things up; ForMember with MapFrom is the only functions used to set up the maps at all, in fact.
I've tried a number of other things, and had a number of findings:
- Using .UseDestinationValue() instead of .Ignore(), as well as an always false Precondition; nothing seems to actually have any effect, with the collection always being overwritten no matter how hard I try to say not to touch it. I assume it has to be something outside the "normal" mapping process.
- Changing from ICollection to just ToC makes it work - the destination value is ignored/untouched - so it's something about ICollection in particular that seems to be the source of the problem.
- Adding a dummy ToC collection to FromB, populating it with the value from instanceOfToA.MemberB.MemberC prior to mapping, and adding a direct map of FromB.MemberC to ToC.MemberC. Stepping through indicates that the value does actually set from the dummy copy at first, but it is later updated to the empty array.
- The update to the empty array happens after the call to the TypeMappingMemberValueResolver changing FromB to ToB, after any AfterMap actions specified on the mapping of FromB -> ToB, but prior to invocations to AfterMap for FromA -> ToA.
- The issue does not occur if I directly call a Map on instanceOfFromA.MemberB to instanceOfToA.MemberB. Something about that additional layer of indirection seems to be the problem. That it doesn't happen within the ValueResolver suggests that's not it, either.
- I've built my own copy of Automapper from source and hooked that up, hoping that I could step through to figure out what is inducing this change, but the compiled/generated nature of maps has proven a big obstacle to this.
I'm at a bit of a loss here, and as this erasure is causing critical production data loss this is no bueno. I'm really hoping there's some configuration setting that I'm overlooking somewhere that might prevent, fix, or workaround the behavior that I'm seeing. As it is, I'm wondering if I need to try to force the issue of an upgrade to .NET Standard 2.1 so I can try the latest version of AutoMapper, in the fingers-crossed hope that this addresses the issue.
Much obliged!