使用 Automapper 从 DTO 数据中膨胀实体通常是一个坏主意。它非常适合朝着相反的方向前进——将数据从实体传递到视图模型、webapimodel 或一般的 DTO。但特别是对于 EntityFramework,在客户端到域的方向上使用它会变得混乱。
例如,您的实体上有 2 个属性不在您的视图模型上:id
和CreatedAt
(为什么不一致的大小写顺便说一句?)。为了AutoMapper.Mapper.AssertConfigurationIsValid()
不引发异常,这意味着CreateMap
除了属性的忽略或自定义解析器之外,您还需要在调用中忽略或使用自定义解析器来处理这两个Departments
属性。最后,唯一被自动映射的是name
,这首先违背了使用 automapper 的目的。
您将 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
委托块中的 与DbContext
您将用于将Registration
实体添加到 ( dbContext.Registrations.Add(reg)
) 并调用SaveChanges
的不同。当您有附加到不同上下文的实体时,您最终会Department
在数据库中出现重复的实体(或者由于重复的主键可能导致 SQL 异常)。
更新
我选择 AutoMapper 是因为我的实体和 DTO 都有 15 个以上的字段,两者之间的唯一区别是我的实体拥有的数据库特定的东西,比如 id、创建日期、最后修改日期等。你会保持你不使用的建议吗AutoMapper 在这种情况下考虑到我的实体比我在这里发布的简化要大得多?
那要看。对于您的 15 多个其他属性,它们都是标量吗?它们中的任何一个是外键属性(用于管理非集合导航属性)吗?他们中有多少人会要求自定义解析器?
我绝对不会使用自动映射器来 DTO 集合导航属性 ( public virtual ICollection<SomeOtherEntity> OtherEntities { get; set; }
)。我也不会尝试将自动映射器用于不公开外键 ( public virtual SomeOtherEntity OtherEntity { get; set; }
) 的 DTO 非集合导航属性。
这里的代码味道是,对于每个 DTO 到实体的CreateMap
调用,您将至少有几个Ignore
s(忽略创建日期、最后修改日期等)。此外,如果您的非集合导航属性确实公开了外键属性,您可以自动映射 fk 属性并且它会起作用,但您最终会忽略实际的 ( virtual
) 导航属性。
此外,当涉及到域代码时,即您的记录系统,它有助于在阅读时公开所有内容,而不是在 AutoMapper 后面隐藏一些细节。考虑以下内容——它更加明确,虽然有些冗长,但我认为这不一定是一件坏事,因为它在单个源文件中显示了所有域传输代码:
var reg = new Registration
{
name = dto.name,
prop1 = dto.prop1,
prop2 = dto.prop2,
...
propN = dto.propN
};
将您在此处的额外行数与您在引导程序中需要的所有额外行(忽略、自定义解析器等)进行比较CreateMap
。最后是你的电话,希望这会有所帮助。