使用 automapper 更新实体框架实体

Posted

技术标签:

【中文标题】使用 automapper 更新实体框架实体【英文标题】:Use automapper to update an Entity Framework Entity 【发布时间】:2011-12-10 05:58:13 【问题描述】:

我正在尝试使用实体框架并让 Automapper 从我的合同中更新我的实体。

我的代码如下所示:

var temp = OrderContract;
Order order = dataAccess.FindOne<Order>(x => x.OrderId == temp.OrderId) 
              ?? new Order();

Mapper.Map(OrderContract, order);

if (order.OrderId <= 0)
   dataAccess.Add(order);

(注意:我使用的是存储库模式。dataAccess.FindOne 调用 CreateQuery 以返回一个实体。)

我遇到的问题是人际关系。当我进行更新时出现此错误(插入工作正常):

操作失败:无法更改关系,因为一个或多个外键属性不可为空。当对关系进行更改时,相关的外键属性将设置为空值。如果外键不支持空值,则必须定义新关系,必须为外键属性分配另一个非空值,或者必须删除不相关的对象。

我猜 automapper 没有按照我想要的方式更新。根据错误消息和谷歌搜索,我推测 Automapper 正在重新创建我的集合关系(甚至可能不是集合的关系)。

如何告诉 Automapper 只更新而不重新制作任何对象或集合?

猜测:

我读到也许我需要为 automapper 使用 UseDestinationValue 选项。我回去把它放在我所有的集合上但是当我这样做时,我的插入因外键违规而失败。

代码映射:

仅在一个集合上使用 UseDestinationValue(此集合插入但不会更新):

//+ Source
Mapper.CreateMap<SourceContract, Source>()
    .IgnoreAllNonExisting();

//+ SelectedRequirement
Mapper.CreateMap<SelectedRequirementContract, SelectedRequirement>()
    .ForMember(x => x.SelectedRequirementId, opt => opt.MapFrom(src => src.RequirementId))
    .IgnoreAllNonExisting();

//+ Comment Contract
Mapper.CreateMap<CommentContract, Comment>()
    .ForMember(x => x.CommentText, opt => opt.MapFrom(src => src.Comment))
    .IgnoreAllNonExisting();

//+ Order Automapper setup
Mapper.CreateMap<OrderContract, Order>()
    .ForMember(x => x.Source, opt => opt.MapFrom(src => src.Source))
    .ForMember(x => x.Comment, opt => opt.MapFrom(src => src.Comment))
    //Although a mapping was created for Comment entity,
    //we still need to map the CommentId of the Order entity otherwise it will remain null during an update.
    //Another way to handle this would be to Delete CommentId from the Order entity.
    //However, if anyone updates (Update from model) OrderDataModel.edmx that property would show up again thus causing
    //a null value to be inserted during an update.
    .ForMember(x => x.CommentId, opt => opt.MapFrom(src => src.Comment.CommentId))
    .ForMember(x => x.SelectedRequirements, opt => opt.UseDestinationValue(); opt.MapFrom(src => src.Requirements);)
    .ForMember(x => x.OrderStateId, opt => opt.MapFrom(src => src.StateId))
    .ForMember(x => x.OrderStateId, opt => opt.MapFrom(src => src.StateId))
    .IgnoreAllNonExisting();

处处使用 UseDestinationValue(这个不插入):

//+ Source
Mapper.CreateMap<SourceContract, Source>()
    .IgnoreAllNonExisting();

//+ SelectedRequirement
Mapper.CreateMap<SelectedRequirementContract, SelectedRequirement>()
    .ForMember(x => x.SelectedRequirementId, opt =>  opt.UseDestinationValue(); opt.MapFrom(src => src.RequirementId); )
    .IgnoreAllNonExisting();

//+ Comment Contract
Mapper.CreateMap<CommentContract, Comment>()
    .ForMember(x => x.CommentText, opt =>  opt.UseDestinationValue(); opt.MapFrom(src => src.Comment); )
    .IgnoreAllNonExisting();

//+ Order Automapper setup
Mapper.CreateMap<OrderContract, Order>()
    .ForMember(x => x.Source, opt =>  opt.UseDestinationValue(); opt.MapFrom(src => src.Source); )
    .ForMember(x => x.Comment, opt =>  opt.UseDestinationValue(); opt.MapFrom(src => src.Comment); )
    //Although a mapping was created for Comment entity,
    //we still need to map the CommentId of the Order entity otherwise it will remain null during an update.
    //Another way to handle this would be to Delete CommentId from the Order entity.
    //However, if anyone updates (Update from model) OrderDataModel.edmx that property would show up again thus causing
    //a null value to be inserted during an update.
    .ForMember(x => x.CommentId, opt =>  opt.UseDestinationValue(); opt.MapFrom(src => src.Comment.CommentId); )
    .ForMember(x => x.SelectedRequirements, opt =>  opt.UseDestinationValue(); opt.MapFrom(src => src.Requirements); )
    .ForMember(x => x.OrderStateId, opt =>  opt.UseDestinationValue(); opt.MapFrom(src => src.StateId); )
    .ForMember(x => x.OrderStateId, opt =>  opt.UseDestinationValue(); opt.MapFrom(src => src.StateId); )
    .IgnoreAllNonExisting();

我需要做什么才能插入和更新?

【问题讨论】:

你能展示你的OrderOrderContract课程吗? @shuniar - 我的合同可以在这里看到:raw.github.com/gist/1296415/…。 Order 类是一个实体对象,非常大。除非有明确的理由,否则我宁愿不让它看起来像样。 您解决了这个问题吗?我也有同样的问题。 AutoMapper 不会合并,它会创建新的。如果要合并,那就不要使用AutoMapper,自己写a -> b 映射方法。 @O.O - AutoMapper 两者都可以。 Mapper.Map(sourceObject, destObject) 【参考方案1】:

我认为您希望 AutoMapper 不创建您的 EF 实体并获取您发送的实体。在 auto mapper 的 version2.0 中有一个重载到 map 方法,您基本上可以传递您的 datacontext 委托并让 EF 创建您的对象。

请看这个article。

【讨论】:

以上是关于使用 automapper 更新实体框架实体的主要内容,如果未能解决你的问题,请参考以下文章

使用 Automapper,将 DTO 映射回实体框架,包括引用的实体

将 AutoMapper 与实体框架一起使用时出现异常

OOM框架AutoMapper基本使用

AutoMapper 静态类不映射列表和嵌套实体

Automapper 总是显示子实体

ASP.NET Core搭建多层网站架构6.2-使用AutoMapper映射实体对象