15

I have a simple update function:

public void Update(Users user)
{
    tblUserData userData = _context.tblUserDatas.Where(u => u.IDUSER == user.IDUSER).FirstOrDefault();
    if (userData != null)
    {
        Mapper.CreateMap<Users, tblUserData>();
        userData = Mapper.Map<Users, tblUserData>(user);
        _context.SaveChanges()
    }
}

userData is an EF entity, and it's Entity Key property gets nulled out because, I believe, it exists in the destination object, but not in the source object, so it gets mapped over with its default value (for the Entity Key, that's null)

So, my question is can Automapper be configured to only attempt to map the properties that exist in both the source and destination objects? I'd like things like the Entity Key and navigation properties to be skipped over.

hutchonoid
  • 32,982
  • 15
  • 99
  • 104
Mister Epic
  • 16,295
  • 13
  • 76
  • 147

4 Answers4

10

You can explicitly tell AutoMapper to Ignore certain properties if required like this:

Mapper.CreateMap<Users, tblUserData>()
        .ForMember(dest => dest.Id, opt => opt.Ignore());

which will mean the Id column in the destination object will ALWAYS be left untouched.

You can use the Condition option to specify whether or not a mapping is applied depending on the result of a logical condition, like this:

Mapper.CreateMap<Users, tblUserData>()
        .ForMember(dest => dest.Id, opt => opt.Condition(src=>src.Id.HasValue));

or

Mapper.CreateMap<Users, tblUserData>()
        .ForMember(dest => dest.Id, opt => opt.Condition(src=>src.Id != null));

depending on your specific requirements.

connectedsoftware
  • 6,987
  • 3
  • 28
  • 43
  • 5
    Right, but I have have to explicitly create the list of properties to ignore for each of my entities. What would be nice for me in my case would be to have a way to turn off "greedy" mapping and have AutoMapper compare the properties of the source and destination objects, and map only over the properties that exist in both. I didn't see any such option in the docs, so I figured I would ask here, but I don't think AutoMapper behaves this way. – Mister Epic Jun 17 '13 at 12:48
  • I understand the rule you want to apply: "when my source property is not null and my destination property is not null then map the source to the destination", but I don't think it is possible to apply this logic globally to every property in a mapping without explicitly creating a condition for each as I have done above. – connectedsoftware Jun 17 '13 at 12:56
  • 5
    The rule I want to apply is "when my source property exists in the destination, map the source to the destination, and ignore all other properties in the destination", but I believe you are correct in that I would need to create the ignore conditions as you have indicated. – Mister Epic Jun 17 '13 at 13:13
8

Instead of

userData = _mapper.Map<Users, tblUserData>(user);

use

 _mapper.Map(user, userData);

The former creates a new userData object with only the properties available in user object. The later overwrites the existing userData object's properties with properties present in user object, while retaining the rest.

leaner12
  • 111
  • 1
  • 6
5

You can tell AutoMapper to ignore fields that you do not want mapped like this:

userData = Mapper.Map<Users, tblUserData>(user).ForMember(m => m.EntityKey, opt => opt.Ignore());
hutchonoid
  • 32,982
  • 15
  • 99
  • 104
5

You can override this behavior by making a small extension method to ignore all properties that do not exist on the target type.

public static IMappingExpression<TSource, TDestination> IgnoreAllNonExisting<TSource, TDestination>(this IMappingExpression<TSource, TDestination> expression)
{
    var sourceType = typeof(TSource);
    var destinationType = typeof(TDestination);
    var existingMaps = Mapper.GetAllTypeMaps().First(x => x.SourceType.Equals(sourceType)
        && x.DestinationType.Equals(destinationType));
    foreach (var property in existingMaps.GetUnmappedPropertyNames())
    {
        expression.ForMember(property, opt => opt.Ignore());
    }
    return expression;
}

Then it is possible to do the mapping as follows:

Mapper.CreateMap<SourceType, DestinationType>().IgnoreAllNonExisting();

It is also possible to customize this method to your needs, by specifically ignoring properties which have a protected or private setter, for example.

Khalil Liraqui
  • 385
  • 1
  • 5
  • 21
  • 3
    `Mapper.GetAllTypeMaps()` is Obsolete use `Mapper.Configuration.GetAllTypeMaps()` [Mapper.GetAllTypeMaps() obsolete](https://github.com/AutoMapper/AutoMapper/issues/1252) – HostMyBus Mar 08 '19 at 10:09