1

I'm having difficulty ignoring a property on an class which inherits from a base class.

Mapper.CreateMap<FormViewModelBase, EntityBase>()
.Include<FormViewModel, Entity>()
.ForMember(x => x.Id, o => o.Ignore());

Mapper.CreateMap<FormViewModel, Entity>();

The only thing to note here is that the property on the base class is String and the property on the derived class is a Int32.

No matter what, when i try map an instance of FormViewModel to Entity the String based Id property on the Entity class is always set to the Int value from the FormViewModel, even though i have specified to ignore it.

The reason I am using different types for the Id on FormViewModel and Entity, is that I am using RavenDB in a web app and objects can be loaded via a string or an int Id. On the client-side Int Id's are preferred as the standard Raven string based ID's do not play well when generating links.

Can anyone tell me what the problem is here ?

JCoder23
  • 551
  • 1
  • 4
  • 17

4 Answers4

1

So your base class looks something like this?

public class FormViewModelBase
{
   public string Id { get; set; }
   // other stuff
}

and your derived class looks like this?

public class FormViewModel : FormViewModelBase
{
   public new int Id { get; set; }
   // other stuff
}

I'm assuming that's the case.

And if that's the case, then the derived Id is hiding the base Id property.

Anyway, so are you passing around actual instances of a FormViewModel and making Entity objects from them?

I see this line:

Mapper.CreateMap<FormViewModel, Entity>();

Which says to me "make me a new Entity object from the FormViewModel object I send you and do the default, conventional thing to accomplish this."

So when you call something like:

var anEntity = AutoMapper.Mapper.Map<FormViewModel, Entity>(aFormViewModel);

It's going to use that Map, not the one for the base object. Because it is a derived FormViewModel.

If you did something like:

Mapper.CreateMap<FormViewModel, Entity>()
.ForMember(x => x.Id, o => o.Ignore());

to handle the mapping of your derived object, it would map it, I suspect, as you would want but it might be helpful to understand a bit more about what you're trying to do.

itsmatt
  • 31,265
  • 10
  • 100
  • 164
  • Simply, im trying to map a model posted from a form to an entity that will be saved in my DB. The FormViewModelBase has a property Id (Int32) and the FormViewModel inherits from this. The FormViewModel class does not specify a new Id property it just uses the Id from its base class, plus some other properties. I want to Ignore the Id when mapping from FormViewModel --> Entity. The mapping code i provided above does not work for me, the property on my Entity is always overwritten. – JCoder23 May 13 '13 at 13:14
  • OK, so FormViewModel doesn't have an Id but Entity has one you want to preserve. Gotcha. In my test code, the last bit of code I wrote there that does the Ignore on Id when mapping a FormViewModel to and Entity will preserve Entity's original Id. – itsmatt May 13 '13 at 13:47
  • Ok, yes that is my work around, but according to the Automapper documentation i should be able to Ignore the Id on the base class mapping so i dont have to specify the Ignore rule on each subclass mapping. – JCoder23 May 13 '13 at 14:08
  • 5
    Looking at https://github.com/AutoMapper/AutoMapper/wiki/Mapping-inheritance it looks like the problem is that convention (e.g., Id => Id) is higher than that ignore. – itsmatt May 13 '13 at 14:31
  • @itsmatt IMHO, that last comment is the "real" answer to the question. Much appreciated if you would consider editing your answer to put that at the top. I nearly overlooked the comments down here when looking for an answer to the same problem from the OP. It sounds like the OPer agrees that it's the "real" answer too. – pbristow Mar 18 '15 at 14:13
0

You'll need to specify mapping details (like ignoring certain properties) in the mapping of child classes, not parents. For example:

Mapper.CreateMap<ParentA, ParentB>
      .Include<ChildA, ChildB>();

Mapper.CreateMap<ChildA, ChildB>
      .ForMember(dest => dest.SomeProperty, opt => opt.Ignore());

The reason your code is not working is that you're specifying Ignore() on the parent mappings.

Mosh
  • 5,944
  • 4
  • 39
  • 44
0

I know this question is a year old but I haven't found a solution in SO that answers this kind of questions. I had identical issue and after some digging the only thing I found is that this behavior is not very well documented. And mapping base classes works but ignoring them doesn't for some reason.

I used this extension method and it worked out for me

public static IMappingExpression<TSource, TDestination>
        InheritMappingFromBaseType<TSource, TDestination>(this IMappingExpression<TSource, TDestination> expression)
    {
        var sourceType = typeof(TSource);
        var desctinationType = typeof(TDestination);
        var sourceParentType = sourceType.BaseType;
        var destinationParentType = desctinationType.BaseType;

        expression
            .ForAllMembers(x => x.Condition(r => NotAlreadyMapped(sourceParentType, destinationParentType, r)));

        return expression;
    }

    private static bool NotAlreadyMapped(Type sourceType, Type desitnationType, ResolutionContext r)
    {
        return !r.IsSourceValueNull &&
               Mapper.FindTypeMapFor(sourceType, desitnationType).GetPropertyMaps().Where(
                   m => m.DestinationProperty.Name.Equals(r.MemberName)).Select(y => !y.IsMapped()).All(b => b);
    }

And this is how it's used (Notice that you will need to but the ignore declaration in the base classes mapping)

CreateMap<DerivedClassSource, DerivedClassDestination>()
            .InheritMappingFromBaseType()

What it does is mapping any properties that were not mapped using the base class mapping. In other words it breaks the default mapping priorities.

I found the source code here and modified it a bit because some of the original method's code is now implemented in AutoMapper 2 and 3.

Adrian Hristov
  • 1,957
  • 2
  • 16
  • 22
0

After raising this as an issue with AutoMapper, the reason that Ignore is not added using Include is because there is no way to un-ignore - mappings can still be overridden in the base class.

The only way that you can currently achieve this behaviour is to decorate the destination property with the [IgnoreMap] attribute, but this will likely be inconsistent with the rest of your configuration, so you'd need to decide whether it's worth it!

Richard
  • 29,854
  • 11
  • 77
  • 120