0

When using Internal().AllowAdditiveTypeMapCreation = true;, I was expecting that

CreateMap<Source, Destination>()
    .ForMember(dest => dest.Id, opt => opt.Ignore());

CreateMap<Source, Destination>()
    .ForMember(dest => dest.Name, opt => opt.Ignore());

would be merged/combined to

CreateMap<Source, Destination>()
    .ForMember(dest => dest.Id, opt => opt.Ignore())
    .ForMember(dest => dest.Name, opt => opt.Ignore());

However, it doesn't seem to be the case. The last CreateMap() was given priority and during test, it's complaining that dest.Id wasn't mapped. It's still throwing the same exception even if I place both CreateMap() in different Profiles.

You can find its fiddle here https://dotnetfiddle.net/FEu6Td

Is my understanding of AllowAdditiveTypeMapCreation incorrect? There isn't any documentation on it. I'm only able to find a brief description from the source code

Allow the same map to exist in different profiles. The default is to throw an exception, true means the maps are merged.

Use Case

A little detail about what I'm trying to achieve. Assuming I have a handful of Source/Destination pairs with a common property that I would like to ignore

public class Source1 { public DateTime CreationDate { get; set; } }
public class Destination1 { public DateTime CreationDate { get; set; } }

public class Source2 { public DateTime CreationDate { get; set; } }
public class Destination2 { public DateTime CreationDate { get; set; } }

public class Source3 { public DateTime CreationDate { get; set; } }
public class Destination3 {
    public DateTime CreationDate { get; set; }

    public string ExtraProperty { get; set; } // an additional property that I would want to ignore later.
}

These Source/Destination pairs can be marked with an interface or System.Attribute and retrieved using reflection into an array. Let's pretend the array below is obtained using some other means.

var listSourceDestination = new[]
{
    new KeyValuePair<Type, Type>(typeof(Source1), typeof(Destination1)),
    new KeyValuePair<Type, Type>(typeof(Source2), typeof(Destination2)),
    new KeyValuePair<Type, Type>(typeof(Source3), typeof(Destination3))
}

I can then loop through each pair and use the non-generic CreateMap() to ignore the common property.

foreach (var pair in listSourceDestination)
{
    CreateMap(pair.Key, pair.Value)
        .ForMember("CreationDate", opt => opt.Ignore());
}

After that I would like to ignore the ExtraProperty of Destination3. Since I know the type, I can use the generic CreateMap<,>().

CreateMap<Source3, Destination3>()
    .ForMember(dest => dest.ExtraProperty, opt => opt.Ignore());

Since the mapping of the pair Source3/Destination3 was created twice: once using the non-generic CreateMap() inside a loop and the other using the generic CreateMap<,>(). I was hoping that both CreateMap() would merge the mapping into something like

CreateMap<Source3, Destination3>()
    .ForMember(dest => dest.CreationDate, opt => opt.Ignore())
    .ForMember(dest => dest.ExtraProperty, opt => opt.Ignore());

Mapping Inheritance

@Lucian Bargaoanu mentioned the use of mapping inheritance, so I'd explore the idea here with the above example. I created a base Source/Destination pair which would be inherited by the rest of the pairs.

public abstract class BaseSource { public DateTime CreationDate { get; set; } }
public abstract class BaseDestination { public DateTime CreationDate { get; set; } }

public class Source1 : BaseSource {}
public class Destination1 : BaseDestination {}

public class Source2 : BaseSource {}
public class Destination2 : BaseDestination {}

public class Source3 : BaseSource {}
public class Destination3 : BaseDestination { public string ExtraProperty { get; set; } }

Then we have the same list of source/destination pairs from above

var listSourceDestination = new[]
{
    new KeyValuePair<Type, Type>(typeof(Source1), typeof(Destination1)),
    new KeyValuePair<Type, Type>(typeof(Source2), typeof(Destination2)),
    new KeyValuePair<Type, Type>(typeof(Source3), typeof(Destination3))
}

Now we create a map for the base pair first.

CreateMap<BaseSource, BaseDestination>()
    .ForMember(dest => dest.CreationDate, opt => opt.Ignore());

// we could also use the non-generic version
CreateMap(typeof(BaseSource), typeof(BaseDestination))
    .ForMember("CreationDate", opt => opt.Ignore());

Next, we loop through the list of derived source/destination pairs and inherit from the base map.

foreach (var pair in listSourceDestination)
{
    CreateMap(pair.Key, pair.Value)
        .IncludeBase(typeof(BaseSource), typeof(BaseDestination)); // we still need this line to indicate that we want to inherit from the base map.
}

And finally we still need the same map to ignore the ExtraProperty from Destination3.

CreateMap<Source3, Destination3>()
    //.IncludeBase(typeof(BaseSource), typeof(BaseDestination)) // I shouldn't be needing this line here because I'm expecting the loop above to already included it. If I still needed this line, then this map essentially overriden/replaced the one in the loop above and the whole `AllowAdditiveTypeMapCreation = true` isn't what I think it is anymore (which is to merge mappings and not overriding it - last one wins).
    .ForMember(dest => dest.ExtraProperty, opt => opt.Ignore());
Twisted Whisper
  • 1,166
  • 2
  • 15
  • 27
  • Having the same map in different profiles is not a good idea, How is one supposed to make sense of your mappings? For every map one is supposed to look _everywhere_ for ovverides? – Lucian Bargaoanu Apr 02 '23 at 15:11
  • I don't put the same map in different profiles. I mainly use double mapping in the same profile like in the fiddle. I'm just explaining what I have attempted so far. I'll update my question with a use case. – Twisted Whisper Apr 02 '23 at 17:19
  • You reuse configuration with mapping inheritance, not like this. – Lucian Bargaoanu Apr 02 '23 at 18:23
  • Even with inheritance, I'd still need to loop each of them and `.IncludeBase()` onto the non-generic CreateMap(). And I'd still need to ignore the `ExtraProperty` after that on `Destination3` using the generic `CreateMap<,>()`. I have updated my question to explore your idea. – Twisted Whisper Apr 03 '23 at 01:58
  • I'm sure that if you simplify and write some maps everything will make sense. – Lucian Bargaoanu Apr 03 '23 at 05:54
  • Not unless I create each source/destination pair manually using the generic `CreateMap<,>()`. Simple but tedious. Or if you could think of anything else that could simplify it further and post it as a solution? – Twisted Whisper Apr 03 '23 at 06:35
  • Anyway, back to my actual question. Is my understanding of `AllowAdditiveTypeMapCreation` incorrect? If so, what's actually is it for? – Twisted Whisper Apr 03 '23 at 06:36

0 Answers0