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

jle*_*bke 99 constructor automapper

如果我的目标setter是私有的,我可能想使用目标对象的构造函数映射到该对象.你会如何使用Automapper做到这一点?

Jon*_*son 143

使用 ConstructUsing

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

另请注意,这与ConvertUsing使用转换不会继续通过约定进行映射不同,它将使您完全控制映射.

Mapper.CreateMap<ObjectFrom, ObjectTo>()
    .ConstructUsing(x => new ObjectTo(arg0, arg1, etc));
Run Code Online (Sandbox Code Playgroud)

...

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; }
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 非常感谢Jon的这个例子."ConstructUsing"很棒!允许我通过标记为私有的setter来保持我的DTO不可变. (6认同)
  • 为我服务; AutoMapper目前不喜欢所有参数都是可选的构造函数,所以我只使用.ConstructUsing(x => new MyClass()); (3认同)
  • 如果我需要传递比`string`更复杂的东西怎么办?如果`ObjectFrom`包含必须传递给`ObjectTo`构造函数的`ChildObjectFrom`类型属性怎么办? (2认同)

Mat*_*ieu 9

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

Mapper.CreateMap<ObjectFrom, ObjectTo>()

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

var to = Mapper.Map(from, new ObjectTo(param1));
Run Code Online (Sandbox Code Playgroud)


小智 8

最佳实践是使用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)));
Run Code Online (Sandbox Code Playgroud)


lxa*_*lln 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>();
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

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


Jér*_*VEL 5

就我个人而言,我总是喜欢在使用 AutoMapper 时尽可能明确,以避免将来出现任何潜在的错误。

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

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

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

TMappingExpression ConstructUsing(Func<TSource, ResolutionContext, TDestination> ctor);
TMappingExpression ConstructUsing(Expression<Func<TSource, TDestination>> ctor);
Run Code Online (Sandbox Code Playgroud)

在这种情况下,我们希望使用第一个,因为不可能在表达式树中使用命名参数(您将收到编译错误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),
        );
    });
Run Code Online (Sandbox Code Playgroud)

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

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

TDestination Map<TSource, TDestination>(TSource source, TDestination destination);
Run Code Online (Sandbox Code Playgroud)

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

mapper.Map(updateModel, existingEntity);
await dbContext.SaveChangesAsync();
Run Code Online (Sandbox Code Playgroud)

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

  • 我今天学到的东西:在“ConstructUsing”中有一个映射器。 (2认同)