AutoMapper using the wrong constructor

2019-03-23 07:25发布

问题:

Today I upgraded a fully functioning application using AutoMapper v1.1 to now use AutoMapper v2.1 and I am coming across some issues that I never encountered using the previous version.

Here is an example of my code mapping back from Dto to Domain object

public class TypeOne
{
   public TypeOne()
   {
   }

   public TypeOne(TypeTwo two)
   {
      //throw ex if two is null
   }

   public TypeOne(TypeTwo two, TypeThree three)
   {
      //throw ex if two or three are null
   }

   public TypeTwo Two {get; private set;}

   public TypeThree Three {get; private set;}
}

public class TypeOneDto
{
   public TypeOneDto()
   {
   }

   public TypeTwoDto Two {get; set;}

   public TypeThreeDto Three {get; set;}
}

...

Mapper.CreateMap<TypeThreeDto, TypeThree>();
Mapper.CreateMap<TypeTwoDto, TypeTwo>();
Mapper.CreateMap<TypeOneDto, TypeOne>();

var typeOne = Mapper.Map<TypeOne>(typeOneDto);

However the first problem I encountered with v2.1 was that AutoMapper was trying to use the constructor with 2 args when one of the args was null and should be using the 1 arg constructor.

I then tried to use

Mapper.CreateMap<TypeOneDto, TypeOne>().ConstructUsing(x => new TypeOne());

But I kept getting an 'Ambiguous Invocation' error that I couldn't resolve.

I then tried

Mapper.CreateMap<TypeOneDto, TypeOne>().ConvertUsing(x => new TypeOne());

and that did successfully create the TypeOne object using the parameterless constructor but then it failed to set the private setter properties.

I have looked for help on the AutoMapper website and downloaded the source code to have a good look but didn't get far with the little documentation about and there were not many unit tests for ConstructUsing.

Is there anything obvious I am missing that I should change with v2.1? I am surprised that it has changed so much from v1.1.

回答1:

You just need to add explicit cast to

Func<ResolutionContext, TypeOne>

Here is the code:

Mapper.CreateMap<TypeOneDto, TypeOne>().ConstructUsing(
            (Func<ResolutionContext, TypeOne>) (r => new TypeOne()));

Current version of AutoMapper works as described below:

  1. Sorts destination type constructors by parameter count

    destTypeInfo.GetConstructors().OrderByDescending(ci => ci.GetParameters().Length);
    
  2. Takes first constructor which parameters match source properties (without any check for null value). In your case it is constructor with two parameters.