在目标属性是私有的情况下,我可能希望通过目标对象的构造函数映射到该对象。如何使用Automapper实现此功能?
在目标属性是私有的情况下,我可能希望通过目标对象的构造函数映射到该对象。如何使用Automapper实现此功能?
使用ConstructUsing
,可以在映射过程中指定要使用的构造函数。但其他属性将根据约定自动映射。
还要注意,这与ConvertUsing
不同,因为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; }
}
}
最佳实践是使用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)
)
);
nameof
来避免脆弱的硬编码魔术字符串,而且(除非 AutoMapper 现在有一个自定义的 Roslyn 规则),如果您省略参数或指定不存在的参数,您将不会得到编译时错误。 - Dai您应该使用Map
方法来设置目标。例如:
Mapper.CreateMap<ObjectFrom, ObjectTo>()
var from = new ObjectFrom { Name = "Jon", Age = 25 };
var to = Mapper.Map(from, new ObjectTo(param1));
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<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上的维基中了解有关自动映射器构建的更多信息。
个人而言,我在使用 AutoMapper 时更喜欢尽可能地明确,以避免未来出现任何潜在的错误。
如果您只是按照正确的顺序一个接一个地传递参数调用 ConstructUsing
方法,那么您可能会在某一天面临错误。
如果开发人员颠倒了两个字符串参数或在一些现有可选参数之前添加了一个新的可选参数,会怎样呢?这将导致映射错误,其中一个属性没有被映射到它应该映射到的目标属性。 因此,我更喜欢在实例化对象时使用命名参数来定义我的映射。
以下是 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),
);
});
res
是Resolution Context
,它允许您使用已经注册的映射。private set
),你将无法使用Map
方法的以下重载:TDestination Map<TSource, TDestination>(TSource source, TDestination destination);
这个重载在使用EF Core更新实体时非常方便。
mapper.Map(updateModel, existingEntity);
await dbContext.SaveChangesAsync();
谢天谢地,EF Core 还有另一种更新实体的方式。
string
更复杂的东西怎么办?如果ObjectFrom
包含了必须传递给ObjectTo
构造函数的ChildObjectFrom
类型属性,该怎么办? - Gabrielius