AutoMapper using the wrong constructor
Asked Answered
J

2

28

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.

Jesseniajessey answered 21/6, 2012 at 21:54 Comment(2)
possible duplicate of Automapper - how to map to constructor parameters instead of property settersHospitium
I keep getting the same error I mention above when using "ConstructUsing". "Ambiguous Invocation" when I create a new object with parameter-less constructor.Jesseniajessey
L
53

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.

Laurellaurella answered 27/6, 2012 at 12:4 Comment(2)
This just saved me some grief. Works perfectly on the most recent version of AutoMapper (3.2.1.0) :) Thanks!Participation
Although this is a fairly old post, I'm glad I found it: The sequence of what AutoMapper considers a match for the constructor seems to have changed again with version 4.0.4. To avoid issues it might be on the more conservative side to always specify which constructor AutoMapper should use if you have more than one constructor. Also, as a second recommendation, have an automated test in place that sets up all maps then calls Mapper.AssertConfigurationIsValid(). That way you can void problems early on. Happy coding!Mabuse
H
1

Here's an extension method...

    public static void CreateMapWithDefaultConstructor<T, TU>(this Profile profile)
        where TU : class, new()
    {
        profile.CreateMap<T, TU>().ConstructUsing(source => new TU());
    }
Helio answered 26/5, 2020 at 17:45 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.