我也将此问题发布到了https://github.com/AutoMapper/AutoMapper/issues/3372。
我有类似的模型:
public class Foo
{
public Foo(Guid id, string name, IEnumerable<string> enumerable, IReadOnlyDictionary<string, string> readOnlyDic, string[] array, Dictionary<string, string> dic)
{
Id = id;
Name = name;
Enumerable = enumerable;
ReadOnlyDic = readOnlyDic;
Array = array;
Dic = dic;
}
public Guid Id { get; }
public string Name { get; }
public IEnumerable<string> Enumerable { get; }
public IReadOnlyDictionary<string, string> ReadOnlyDic { get; }
public string[] Array { get; }
public Dictionary<string, string> Dic { get; }
}
public class FooDto
{
public Guid Id { get; set; }
public string Name { get; set; }
public IEnumerable<string> Enumerable { get; set; }
public IReadOnlyDictionary<string, string> ReadOnlyDic { get; set; }
public string[] Array { get; set; }
public Dictionary<string, string> Dic { get; set; }
}
如您所见,Foo
是一种“不可变”,没有任何成员的直接设置器。现在,我想将FooDto
“应用”到现有的Foo
,当然它应该创建一个Foo
的新实例。
[TestMethod]
public void revise()
{
var config = new MapperConfiguration(c => c.CreateMap<FooDto, Foo>());
var mapper = config.CreateMapper();
var dto = new FooDto
{
Id = Guid.NewGuid(),
Name = "name",
Enumerable = new string[] { "e" },
ReadOnlyDic = new Dictionary<string, string> { { "a", "b" } },
Array = new string[] { "a" },
Dic = new Dictionary<string, string> { { "1", "2" } }
};
var before = new Foo(
id: Guid.NewGuid(),
name: "before",
enumerable: new string[] { },
readOnlyDic: new Dictionary<string, string>(),
array: new string[] { },
dic: new Dictionary<string, string>());
var foo = mapper.Map(dto, before);
Assert.AreEqual(dto.Id, foo.Id);
Assert.AreEqual(dto.Name, foo.Name);
Assert.AreEqual(dto.Enumerable.Single(), foo.Enumerable.Single());
Assert.AreEqual(dto.ReadOnlyDic["a"], foo.ReadOnlyDic["a"]);
Assert.AreEqual(dto.Array[0], foo.Array[0]);
Assert.AreEqual(dto.Dic["1"], foo.Dic["1"]);
}
dto
不适用于新的foo
,但Dic
是Dictionary<string, string>
的类型。很奇怪。
如何配置AutoMapper以将更改应用于“不可变”对象并为其创建新实例?
添加
我也明确地像这样用ForCtorParam
进行了测试:
var config = new MapperConfiguration(c =>
{
c.CreateMap<FooDto, Foo>()
.ForCtorParam("id", opt => opt.MapFrom(s => s.Id))
.ForCtorParam("name", opt => opt.MapFrom(s => s.Name))
.ForCtorParam("enumerable", opt => opt.MapFrom(s => s.Enumerable))
.ForCtorParam("readOnlyDic", opt => opt.MapFrom(s => s.ReadOnlyDic))
.ForCtorParam("array", opt => opt.MapFrom(s => s.Array))
.ForCtorParam("dic", opt => opt.MapFrom(s => s.Dic));
});
但是它也不起作用。即使有效,它也看起来根本不像convention-based
映射。
Add2
有趣的是,如果我像这样从destination
创建了source
,它就可以工作:
var foo = mapper.Map<Foo>(dto);
但是我真正想要的是,基于源应用的先前实例创建目标类型的新实例。就像merge
或partial update
。
您可以使用ForCtorParam
执行此操作>
方法1
Automapper(以及几乎所有其他映射器)默认情况下依赖公共设置器。由于您没有公开这些内容,因此需要编写一个自定义ConvertUsing()
来告诉它如何进行映射。