Ada*_*ada 1 c# entity-framework automapper asp.net-web-api
我在我的web api项目中收到了一个DTO,我想使用AutoMapper自动将我的DTO转换为我插入数据库的实体.
以下是DTO和实体的简化:
class RegistrationDTO
{
string name;
ICollection<int> Departments;
}
class Registration
{
int id;
DateTime CreatedAt;
string name;
virtual ICollection<Department> Departments;
}
class Department
{
int id;
string name;
virtual ICollection<Registration> Registrations;
}
Run Code Online (Sandbox Code Playgroud)
问题是RegistrationDTO只有部门的ID,我找不到让AutoMapper从数据库中获取部门的方法(使用Entity Framework 5).
使用自定义ValueResolver我可以将一个int列表转换为Departments列表,但是我想从数据库中获取Departments,而不是创建新的Departments.
这是我提出的解决方案,但我很确定有更好的方法:
var reg= Mapper.Map<Registration>(dto);
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");
...
Run Code Online (Sandbox Code Playgroud)
有人可以帮我解决这个问题吗?
使用Automapper从DTO数据中膨胀实体通常是个坏主意.这是相反的方向 - 通常将数据从实体传递到视图模型,webapimodel或DTO.但特别是对于EntityFramework,在Client-to-Domain方向使用它可能会变得混乱.
例如,您的实体上有2个属性不在您的viewmodel上:id和CreatedAt(为什么不一致的外壳btw?).为了AutoMapper.Mapper.AssertConfigurationIsValid()不抛出异常,这意味着除了属性CreateMap的忽略或自定义解析器之外,您还需要在调用中忽略或使用这两个属性的自定义解析器Departments.最后,唯一被自动化的是name,哪种方式首先打败了使用automapper的目的.
您将DTO转换为实体的代码实际上非常简洁.老实说,我要改变的主要事情是删除了automapper - 在这种情况下,它确实没有必要.
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");
Run Code Online (Sandbox Code Playgroud)
你可能想尝试这样的事情:
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;
}))
;
Run Code Online (Sandbox Code Playgroud)
这不起作用,因为DbContext委托块中的不一样,DbContext您将用于将Registration实体添加到(dbContext.Registrations.Add(reg))并调用SaveChanges.如果有附加到不同上下文的实体,则最终会Department在数据库中出现重复的实体(或者由于重复的主键,可能会出现SQL异常).
更新
我去了AutoMapper,因为我的实体和DTO都有15个以上的字段,两者之间的唯一区别是我的实体拥有的数据库特定的东西,比如id,创建日期,最后修改日期等.你会保留你不使用的建议吗?在这种情况下,AutoMapper考虑到我的实体比我在这里发布的简化要大很多?
那要看.对于您的15多个其他房产,它们都是标量吗?它们中的任何一个都是外键属性(暴露给管理非集合导航属性)?有多少人会要求定制解析器?
我绝对不会将automapper用于DTO集合导航属性(public virtual ICollection<SomeOtherEntity> OtherEntities { get; set; }).我也不会尝试将automapper用于不公开外键(public virtual SomeOtherEntity OtherEntity { get; set; })的DTO非集合导航属性.
这里的代码气味是,对于每个DTO到实体的CreateMap调用,您将至少有几个Ignores(忽略创建日期,最后修改日期等).此外,如果您的非收集导航属性确实公开了外键属性,您可以自动执行fk属性并且它将起作用,但您最终会对actual(virtual)nav属性进行另一次忽略.
此外,当涉及到域代码时,这就是您的记录系统,它有助于在阅读时将所有内容都打开,而不是在AutoMapper后面隐藏一些细节.考虑以下内容 - 它更加明确,虽然它有点冗长,但我认为这不一定是坏事,因为它在单个源文件中显示所有域转移代码:
var reg = new Registration
{
name = dto.name,
prop1 = dto.prop1,
prop2 = dto.prop2,
...
propN = dto.propN
};
Run Code Online (Sandbox Code Playgroud)
比较一下你在CreateMap引导程序中需要的所有额外行(忽略,自定义解析器等)所需的额外行数.最后这是你的电话,希望这会有所帮助.
| 归档时间: |
|
| 查看次数: |
2061 次 |
| 最近记录: |