Alex Davidson
Alex Davidson

Reputation: 357

Why does AutoMapper try to map a null property if the property is of an interface type?

Under AutoMapper 5.0.2:

When a source object has an interface-typed property, the mapping defined for that interface type will be fully evaluated even if the property is null. This is problematic if the mapping uses ResolveUsing, since it may not be expecting a null and could throw.

The following test fixture highlights the problem using a rather contrived mapping. One test passes, the other fails:

[TestFixture]
public class NullPropertyTests
{
    public class ChildClass : IChildInterface
    {
        public string OtherValue { get; set; }
        public string Value { get; set; }
    }

    public interface IChildInterface
    {
        string Value { get; set; }
    }

    public class ClassReferencesChildClass
    {
        public ChildClass Child { get; set; }
    }

    public class ClassReferencesChildInterface
    {
        public IChildInterface Child { get; set; }
    }

    public class DtoClass
    {
        public DtoChildClass Child { get; set; }
    }

    public class DtoChildClass
    {
        public string OtherValue { get; set; }
        public string Value { get; set; }
    }

    [Test]
    public void BreakingTest()
    {
        var mapper = CreateAndValidateMapper(e =>
        {
            e.CreateMap<ClassReferencesChildInterface, DtoClass>();

            e.CreateMap<IChildInterface, DtoChildClass>()
                .Include<ChildClass, DtoChildClass>()
                .ForMember(d => d.Value, x => x.ResolveUsing(c => DoStuff(c.Value?.Split(' '))))
                .ForMember(d => d.OtherValue, x => x.Ignore());
            e.CreateMap<ChildClass, DtoChildClass>();
        });

        // This fails due to a NullReferenceException:
        mapper.Map<DtoClass>(new ClassReferencesChildInterface { Child = null });
    }

    [Test]
    public void PassingTest()
    {
        var mapper = CreateAndValidateMapper(e =>
        {
            e.CreateMap<ClassReferencesChildClass, DtoClass>();

            e.CreateMap<IChildInterface, DtoChildClass>()
                .Include<ChildClass, DtoChildClass>()
                .ForMember(d => d.Value, x => x.ResolveUsing(c => DoStuff(c.Value?.Split(' '))))
                .ForMember(d => d.OtherValue, x => x.Ignore());
            e.CreateMap<ChildClass, DtoChildClass>();
        });

        mapper.Map<DtoClass>(new ClassReferencesChildClass { Child = null });
    }

    private static string DoStuff(string[] value)
    {
        return value.FirstOrDefault();
    }

    private static IMapper CreateAndValidateMapper(Action<IMapperConfigurationExpression> configure)
    {
        var configuration = new MapperConfiguration(configure);
        configuration.AssertConfigurationIsValid();
        return configuration.CreateMapper();
    }
}

Under AutoMapper 3.2.1 (with types etc adjusted as necessary for compilation) both tests will pass.

We could modify several dozen mappings to tolerate nulls explicitly, but that seems overly verbose for something which previously worked just fine.

Is this intended behaviour? Is there a workaround for it which doesn't require duplicating lots of mappings?

Upvotes: 2

Views: 619

Answers (1)

Jim Gilmartin
Jim Gilmartin

Reputation: 793

I raised this issue with the Automapper project. It appears to be fixed and the fix is coming in 5.1.0.

Upvotes: 1

Related Questions