Automapper - how to map to constructor parameters instead of property setters

Viewed 68914

In cases where my destination setters are private, I might want to map to the object using the destination object's constructor. How would you do this using Automapper?

5 Answers

Use ConstructUsing

this will allow you to specify which constructor to use during the mapping. but then all of the other properties will be automatically mapped according to the conventions.

Also note that this is different from ConvertUsing in that convert using will not continue to map via the conventions, it will instead give you full control of the mapping.

Mapper.CreateMap<ObjectFrom, ObjectTo>()
    .ConstructUsing(x => new ObjectTo(arg0, arg1, etc));

...

using AutoMapper;
using NUnit.Framework;

namespace UnitTests
{
    [TestFixture]
    public class Tester
    {
        [Test]
        public void Test_ConstructUsing()
        {
            Mapper.CreateMap<ObjectFrom, ObjectTo>()
                .ConstructUsing(x => new ObjectTo(x.Name));

            var from = new ObjectFrom { Name = "Jon", Age = 25 };

            ObjectTo to = Mapper.Map<ObjectFrom, ObjectTo>(from);

            Assert.That(to.Name, Is.EqualTo(from.Name));
            Assert.That(to.Age, Is.EqualTo(from.Age));
        }
    }

    public class ObjectFrom
    {
        public string Name { get; set; }
        public int Age { get; set; }
    }

    public class ObjectTo
    {
        private readonly string _name;

        public ObjectTo(string name)
        {
            _name = name;
        }

        public string Name
        {
            get { return _name; }
        }

        public int Age { get; set; }
    }
}

Personally I always prefer to be as explicit as possible when using AutoMapper to avoid any potential bugs in the future.

If you call the ConstructUsing method just passing the parameters one by one in the good order you might face a bug one day.

What if a developer inverts 2 string parameters or add a new optional parameter before some existing optional parameters? You would get a mapping bug where a property isn't mapped to the destination property it's supposed to. For that reason I prefer to define my mapping using named parameters when instanciating my object.

Here are the different signatures of the ConstructUsing method:

TMappingExpression ConstructUsing(Func<TSource, ResolutionContext, TDestination> ctor);
TMappingExpression ConstructUsing(Expression<Func<TSource, TDestination>> ctor);

In that case we want to use the first one because it's not possible to use named parameters in an Expression Tree (you would get a compilation error an expression tree may not contain a named argument specification).

Here is how to use it:

 CreateMap<FromType, ToType>()
    .ConstructUsing((src, res) =>
    {
        return new ToType(
            foo: src.MyFoo,
            bar: res.Mapper.Map<BarModel>(src.MyBar),
        );
    });

Note the Func's 2nd parameter res which is the Resolution Context. This parameter allows you to use already registered mappings.

Careful though, I'd like to catch your attention on a drawback of declaring mappings with constructors. If your classes don't have public setters (read-only properties or private set) you won't be able to use the following overload of the Map method:

TDestination Map<TSource, TDestination>(TSource source, TDestination destination);

This overload could be very handy when updating an entity with EF Core for example

mapper.Map(updateModel, existingEntity);
await dbContext.SaveChangesAsync();

Thankfully there's another way to update entities with EF Core.

Related