自动映射 - 如何映射到构造函数参数而不是属性设置器

时间:2010-02-10 17:55:14

标签: automapper

如果我的目标setter是私有的,我可能想使用目标对象的构造函数映射到该对象。你会怎样使用Automapper?

5 个答案:

答案 0 :(得分:136)

使用ConstructUsing

这将允许您指定在映射期间使用的构造函数。但随后所有其他属性将根据惯例自动映射。

另请注意,这与ConvertUsing不同,因为转换使用不会继续通过约定进行映射,而是让您完全控制映射。

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; }
    }
}

答案 1 :(得分:9)

您应该使用允许您设置目的地的Map方法。例如:

Mapper.CreateMap<ObjectFrom, ObjectTo>()

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

var to = Mapper.Map(from, new ObjectTo(param1));

答案 2 :(得分:6)

最佳做法是使用AutoMapper中的文档化方法 https://github.com/AutoMapper/AutoMapper/wiki/Construction

public class SourceDto
{
        public SourceDto(int valueParamSomeOtherName)
        {
            Value = valueParamSomeOtherName;
        }

        public int Value { get; }
}

Mapper.Initialize(cfg => cfg.CreateMap<Source, SourceDto>().ForCtorParam("valueParamSomeOtherName", opt => opt.MapFrom(src => src.Value)));

答案 3 :(得分:1)

我个人始终希望在使用AutoMapper时尽可能地露骨,以免将来出现任何潜在的错误。

如果调用ConstructUsing方法只是按照良好的顺序逐个传递参数,则有一天可能会遇到错误。

如果开发人员反转2个字符串参数或在某些现有可选参数之前添加新的可选参数怎么办?您将得到一个映射错误,其中一个属性未映射到它应该映射到的目标属性。 因此,我喜欢在实例化对象时使用命名参数来定义映射。

以下是ConstructUsing方法的不同签名:

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

在那种情况下,我们要使用第一个,因为不可能在表达式树中使用命名参数(您会得到编译错误an expression tree may not contain a named argument specification)。

这里是使用方法:

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

请注意Func的第二个参数res,即Resolution Context。此参数允许您使用已经注册的映射。

但是请注意,我想引起您注意使用构造函数声明映射的缺点。如果您的类没有公共设置器(只读属性或private set),则将无法使用Map方法的以下重载:

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

例如,当使用EF Core更新实体时,此重载可能非常方便

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

非常感谢another way to update entities with EF Core

答案 4 :(得分:0)

在撰写此答案时,如果属性与构造函数参数匹配,AutoMapper将自动执行此操作(通过简单的CreateMap<>()调用)。当然,如果事情不匹配,那么使用.ConstructUsing(...)是可行的方法。

public class PersonViewModel
{
    public int Id { get; set; }

    public string Name { get; set; }
}

public class Person
{
    public Person (int id, string name)
    {
        Id = id;
        Name = name;
    }

    public int Id { get; }

    public string Name { get; }
}

public class PersonProfile : Profile
{
    public PersonProfile()
    {
        CreateMap<PersonProfile, Person>();
    }
}

注意:这假设您使用Profiles来设置自动播放器映射。

如下所示使用时,会生成正确的对象:

var model = new PersonModel
{
    Id = 1
    Name = "John Smith"
}

// will correctly call the (id, name) constructor of Person
_mapper.Map<Person>(model);

您可以在官方wiki on GitHub

中阅读有关自动播放器构建的更多信息