实体框架,AutoMapper,处理实体更新

时间:2010-01-29 21:26:00

标签: c# asp.net-mvc entity-framework automapper

我最近刚开始使用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的初始设置,那将以完全不同的方式适得其反。

任何人都有更好的主意吗?

5 个答案:

答案 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()放在要映射的每个实体上,这可能会有所帮助。

http://www.prosoftnearshore.com/blog/post/2012/03/14/Using-AutoMapper-to-update-Entity-Framework-properties.aspx

实质上,您要将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并且映射将不起作用。