使用 Automapper 从 DTO 数据中膨胀实体通常是个坏主意。它非常适合朝着相反的方向前进——将数据从实体传递到视图模型、webapimodel 或一般的 DTO。但尤其是使用 EntityFramework,在客户端到域的方向上使用它可能会变得混乱。
例如,您的实体上有 2 个属性不在您的视图模型上:id 和 CreatedAt(为什么不一致的大小写顺便说一句?)。为了使AutoMapper.Mapper.AssertConfigurationIsValid() 不引发异常,这意味着除了Departments 属性的忽略或自定义解析器之外,您还需要在CreateMap 调用中忽略或使用自定义解析器来处理这两个属性。最后,唯一被自动映射的是name,这有点违背了使用自动映射器的初衷。
您将 DTO 转换为实体的代码实际上非常简洁。老实说,我要改变的主要事情是删除自动映射器——在这种情况下,它真的没有必要。
var reg = new Registration { name = dto.name }; // less code than with automapper
reg.Departments = new List<int>(dto.Departments)
.ConvertAll(input => Context.Departments.Find(input));
if(reg.Departments.Contains(null)) //a department provided does not exist in the database
return Request.CreateResponse(HttpStatusCode.BadRequest, "invalid department");
你可能会想尝试这样的事情:
Mapper.CreateMap<RegistrationDTO, Registration>()
.ForMember(d => d.id, o => o.Ignore())
.ForMember(d => d.CreatedAt, o => o.UseValue(DateTime.Now))
.ForMember(d => d.Departments, o => o.MapFrom(s =>
{
var dbContext = new MyDbContext();
var departments = new List<int>(s.Departments)
.ConvertAll(input => dbContext.Departments.Find(input));
return departments;
}))
;
这将不起作用,因为委托块中的 DbContext 与您将用于将 Registration 实体添加到 (dbContext.Registrations.Add(reg)) 并调用 SaveChanges 的 DbContext 不同。当您有附加到不同上下文的实体时,您的数据库中最终会出现重复的 Department 实体(或者由于重复的主键可能导致 SQL 异常)。
更新
我选择 AutoMapper 是因为我的实体和 DTO 都有 15 个以上的字段,
两者之间的唯一区别是数据库特定的东西
我的实体有,如 id、创建日期、最后修改日期等。
在这种情况下,您是否会保留不使用 AutoMapper 的建议
考虑到我的实体比简化大得多
我在这里发帖?
这取决于。对于您的 15 多个其他属性,它们都是标量吗?它们中的任何一个是外键属性(暴露于管理非集合导航属性)吗?其中有多少人会要求自定义解析器?
我绝对不会将自动映射器用于 DTO 集合导航属性 (public virtual ICollection<SomeOtherEntity> OtherEntities { get; set; })。我也不会尝试将 automapper 用于不公开外键 (public virtual SomeOtherEntity OtherEntity { get; set; }) 的 DTO 非集合导航属性。
这里的代码味道是,对于每个 DTO 到实体的 CreateMap 调用,您将至少有几个 Ignores(忽略创建日期、最后修改日期等)。此外,如果您的非集合导航属性确实公开了外键属性,您可以自动映射 fk 属性并且它会起作用,但您最终会忽略实际的 (virtual) 导航属性。
此外,当涉及到域代码时,即您的记录系统,它有助于在阅读时公开所有内容,而不是在 AutoMapper 后面隐藏一些细节。考虑以下内容——它更加明确,虽然有些冗长,但我认为这不一定是一件坏事,因为它在一个源文件中显示了所有域转移代码:
var reg = new Registration
{
name = dto.name,
prop1 = dto.prop1,
prop2 = dto.prop2,
...
propN = dto.propN
};
比较在CreateMap 引导程序中需要的所有额外行(忽略、自定义解析器等)的额外行数。最后是你的电话,希望这会有所帮助。