我最近刚开始使用Entity Framework 1.0,并相信我开始感受到每个人都在谈论的痛苦。我正在尝试使用最佳实践,因此我有一组DTO通过AutoMapper映射到我的实体和从我的实体映射。
真正的问题是当我尝试更新对象时。第一个问题是我找不到创建新实体的方法,从我的DTO传输数据,并且仍然让实体ObjectContext意识到它已被更改。我使用了以下代码:
public VideoDTO UpdateVideo(VideoDTO pVideo)
{
Video video = new Video();
Mapper.Map(pVideo, video);
context.Attach(video); //Successfully attaches
context.ApplyPropertyChanges("Videos", video); // no changes made as far as entity knows b/c it was attached in it's updated state
context.SaveChanges(); //doesn't save the entity
return pVideo;
}
然后我想,也许我需要先从数据库中获取实体,附加到上下文,在Mapper上调用Map方法,然后调用SaveChanges。我在这里做了什么:
public VideoDTO UpdateVideo(VideoDTO pVideo)
{
Video video = context.Videos.Where(v => v.VideoId == pVideo.VideoId).FirstOrDefault();
Mapper.Map(pVideo, video); //Error here: Can't change VideoId value on Video entity
//context.Attach(video);
//context.ApplyPropertyChanges("Videos", video);
context.SaveChanges();
return pVideo;
}
现在我们讨论了不允许更改属性VideoId的可爱EF问题,因为它被Video实体上的EntityKey属性使用。可爱。我已经设置了映射,以便当我从我的DTO映射到EF实体时,EntityKey属性将获得一个值。现在我需要一种方法来对该映射规则进行例外处理,但不知道从哪里开始。我想我可以在这个方法中创建一个全新的映射规则,并设置EntityKey& VideoId属性被忽略,但这看起来很草率。此外,我不确定此时创建的映射是否会成功。如果它覆盖了允许DTO将值映射到实体上的EntityKey的初始设置,那将以完全不同的方式适得其反。
任何人都有更好的主意吗?
答案 0 :(得分:6)
<强> AutoMapper 强>
你的第一个问题是,据我所知,AutoMapper并非设计用于来自DTO-&gt;仅实体实体 - &gt; DTO。这可能最近有所改变,所以我不太确定。有关automapper旨在执行的操作的详细信息,请参阅此链接:The case for two way mapping
PK制图
你说:“在此方法中映射规则,并将EntityKey&amp; VideoId属性设置为忽略,但这看起来很草率”
我认为这根本不是很邋..在持久化之后你真的不应该触及EntityKey / PK,并且可能应该以某种方式编写它的静态。
实体框架
“现在我们得到了不允许更改属性VideoId的可爱EF问题,因为它被视频实体上的EntityKey属性使用。很可爱。”
可爱? EF不会强迫您不更新PK。在生成的模型内部,您的密钥的setter内部会进行属性更改检查。解决方案是更改生成的代码。根据您的模型波动性,这可能不实用,但它是一种选择。
答案 1 :(得分:3)
尝试映射到现有对象:
entity = Mapper.Map<MyDTO, NyEntity>(dto, entity);
并将Ignore()保持在原位。
http://groups.google.com/group/automapper-users/browse_thread/thread/24a90f22323a27bc?fwc=1&pli=1
答案 2 :(得分:1)
如果您想避免将.Ignore()
放在要映射的每个实体上,这可能会有所帮助。
实质上,您要将AutoMapper配置为忽略所有非标量的实体属性:
AutoMapper.Mapper.CreateMap<EntityType, EntityType>()
.ForAllMembers(o => {
o.Condition(ctx =>
{
var members = ctx.Parent.SourceType.GetMember(ctx.MemberName); // get the MemberInfo that we are mapping
if (!members.Any())
return false;
return members.First().GetCustomAttributes(typeof(EdmScalarPropertyAttribute), false).Any(); // determine if the Member has the EdmScalar attribute set
});
});
如果属性是PK(EdmScalarPropertyAttribute
实例中的属性(EntityKey == true?
)告诉你这个),也许可以添加一些额外的工作来避免重置。
答案 3 :(得分:0)
我在同一个场景中。 我得到的唯一解决方案是忽略DTO映射中的PK字段 - &gt;实体。
此规则可以通过自动映射配置期间的以下代码行来实现:
Mapper.CreateMap<MyDTO, MyEntity>().ForMember("EntityPK",r=>r.Ignore());
据我所知,让EF与Detached Entities一起使用的唯一方法是将DTO映射到SaveChanges之前从DB获得的实体(正如您在示例中所做的那样)。
答案 4 :(得分:0)
请注意,“Mauricio Morales”提供的示例仅在您不使用前缀时才有效。如果您使用它们,那么您需要稍微改变以上代码:
Mapper.CreateMap<tempOR_Order, OR_Order>()
.ForMember(m => m.OR_ID, exp => exp.Ignore())
.ForMember(m => m.OR_CU_ID, exp => exp.Ignore())
.ForAllMembers(o => o.Condition(ctx =>
{
var members = ctx.Parent.SourceType.GetMember(ctx.MemberName); // get the MemberInfo that we are mapping
if (!members.Any())
{
members = ctx.Parent.SourceType.GetMember("temp" + ctx.MemberName);
if (!members.Any())
return false;
}
return members.First().GetCustomAttributes(typeof(EdmScalarPropertyAttribute), false).Any(); // determine if the Member has the EdmScalar attribute set
}));
也就是说,您需要在if (!members.Any())
语句中包含其他检查。如果没有这个,函数返回false并且映射将不起作用。