Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Automapper's condition gets ignored

Tags:

automapper

Issue Seems like the condition gets ignored. Here is my scenario:

Source class

public class Source
{
    public IEnumerable<Enum1> Prop1{ get; set; }

    public IEnumerable<Enum2> Prop2{ get; set; }

    public IEnumerable<Enum3> Prop3{ get; set; }
}

The enums subclass from a byte and are decorated with [Flags]. The destination class simply contains properties like Enum1, Enum2 and Enum3 containging the "total" bitwise value. So in essense if the Enumeration contains Enum1.value!, Enum1.Value2 and Enum1.Value3, the destination will contain the bitwise value of Enum1.Value1 | Enum1.Value2 | Enum1.Value3

Destination class

    public Enum1 Prop1 { get; set; }

    public Enum2 Prop2 { get; set; }

    public Enum3 Prop3 { get; set; }

AutoMapper Mapping

    Mapper.CreateMap<Source, Destination>()
            .ForMember(m => m.Prop1, o =>
                {
                    o.Condition(c => !c.IsSourceValueNull);
                    o.MapFrom(f => f.Prop1.Aggregate((current, next) => current | next));
                })
            .ForMember(m => m.Prop2, o =>
            {
                o.Condition(c => !c.IsSourceValueNull);
                o.MapFrom(f => f.Prop2.Aggregate((current, next) => current | next));
            })
            .ForMember(m => m.Prop3, o =>
            {
                o.Condition(c => !c.IsSourceValueNull);
                o.MapFrom(f => f.Prop3.Aggregate((current, next) => current | next));
            });  

The mapping works fine when the inner properties are not null and mapping succeeds and sets destination correctly. However, I want to skip the mapping when the member source value is null (when Prop1 is null, then skip the mapping).

I can see from debugging that Source.Prop1 is null. The condition gets completely ignored and get the exception saying the value is null.

Trying to map Source to Destination. Destination property: Prop1. Exception of type 'AutoMapper.AutoMapperMappingException' was thrown. --> Value cannot be null. Parameter name: source

I'm not sure if IsSourceValueNull checking for Prop1 or the the actual Source class which is not null. Only the member Prop1 is null.

Any help isappreciated.

like image 252
TimJohnson Avatar asked Nov 04 '11 16:11

TimJohnson


People also ask

When should you not use AutoMapper?

If you have to do complex mapping behavior, it might be better to avoid using AutoMapper for that scenario. Reverse mapping can get very complicated very quickly, and unless it's very simple, you can have business logic showing up in mapping configuration.

How do I ignore property in AutoMapper?

So, the AutoMapper Ignore() method is used when you want to completely ignore the property in the mapping. The ignored property could be in either the source or the destination object.

What can I use instead of AutoMapper?

I've found ValueInjecter the best out of the automapper, emitmapper, and a few others on codeplex. I choose ValueInjector because it's the most flexible of them all.

Does AutoMapper handle null?

The Null substitution allows us to supply an alternate value for a destination member if the source value is null. That means instead of mapping the null value from the source object, it will map from the value we supply.


2 Answers

I think you need to do the Condition and MapFrom in two steps:

.ForMember(c => c.Prop1, o => o.Condition(c => !c.IsSourceValueNull));
.ForMember(c => c.Prop1, o => o.MapFrom(f => f.Prop1.Aggregate(...));

The MapFrom will never be used if the Condition evaluates to false.

EDIT

Hmmm... That doesn't seem to work. I thought I had used that before somewhere. You could resort to just the MapFrom:

.MapFrom(f => f.Prop1 == null ? null : f.Prop1.Aggregate(...));
like image 145
PatrickSteele Avatar answered Oct 06 '22 01:10

PatrickSteele


IMO, this is actually a bug in AutoMapper's EnumMapper. The condition statements, as you have them above, should work fine. For example, when mapping from one concrete type to another the TypeMapMapper will correctly call the conditional:

object mappedObject = !context.TypeMap.ShouldAssignValue(context) ? null : mapperToUse.Map(context, mapper);

Which eventually calls the defined condition:

    public bool ShouldAssignValue(ResolutionContext context)
    {
        return _condition == null || _condition(context);
    }

But the EnumMapper doesn't call the TypeMap's ShouldAssignValue method to find out if it should indeed map that field. Similarly, I don't see any reference to AfterMap so it's unlikely that anything defined there is not going to work as well.

like image 32
Kaleb Pederson Avatar answered Oct 06 '22 01:10

Kaleb Pederson