119

如果我的目标设置器是私有的,我可能想使用目标对象的构造函数映射到该对象。您将如何使用 Automapper 执行此操作?

4

5 回答 5

160

采用ConstructUsing

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

另请注意,这与ConvertUsingconvert using 不会继续通过约定映射,而是让您完全控制映射。

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; }
    }
}
于 2010-02-10T19:21:03.053 回答
15

最佳实践是使用 AutoMapper http://docs.automapper.org/en/stable/Construction.html中记录的方法

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)
  )
);
于 2017-08-06T17:09:27.153 回答
12

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

Mapper.CreateMap<ObjectFrom, ObjectTo>()

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

var to = Mapper.Map(from, new ObjectTo(param1));
于 2014-02-28T19:22:01.313 回答
5

在编写此答案时,CreateMap<>()如果属性与构造函数参数匹配,AutoMapper 将自动(通过简单调用)为您执行此操作。当然,如果事情不匹配,那么使用.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<PersonViewModel, Person>();
    }
}

注意:这假设您正在使用配置文件来设置您的自动映射器映射。

当像下面这样使用时,这会产生正确的对象:

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

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

您可以在 GitHub 上的官方 wiki 中阅读有关 automapper 构建的更多信息

于 2017-05-04T14:39:49.093 回答
3

就我个人而言,我总是更喜欢在使用 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 的第二个参数resResolution Context. 此参数允许您使用已注册的映射。

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

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

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

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

值得庆幸的是,还有另一种使用 EF Core 更新实体的方法。

于 2020-08-27T10:19:21.710 回答