且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

自动映射器创建新实例而不是映射属性

更新时间:2022-02-24 23:08:03

据我所知,这是AutoMapper的局限性.请记住,虽然该库通常用于从视图模型和实体映射到/从视图模型和实体映射,但它是一个通用库,用于将任何类映射到任何其他类,因此,它没有考虑到像实体框架这样的ORM.

This is a limitation of AutoMapper as far as I'm aware. It's helpful to keep in mind that while the library is popularly used to map to/from view models and entities, it's a generic library for mapping any class to any other class, and as such, doesn't take into account all the eccentricities of an ORM like Entity Framework.

所以,这是正在发生的事情的解释.当您使用AutoMapper将一个集合映射到另一个集合时,实际上是在映射集合,而不是将该集合中的项目的值映射到相似集合中的项目.回想起来,这是有道理的,因为AutoMapper没有可靠且独立的方式来确定应如何将集合中的单个项目与另一个项目对齐:按ID?哪个属性是ID?也许名字应该匹配?

So, here's the explanation of what's happening. When you map a collection to another collection with AutoMapper, you are literally mapping the collection, not the values from the items in that collection to items in a similar collection. In retrospect, this makes sense because AutoMapper has no reliable and independent way to ascertain how it should line up one individual item in a collection to another: by id? which property is the id? maybe the names should match?

因此,正在发生的是,您实体上的原始集合已完全替换为由全新项目实例组成的全新集合.在许多情况下,这不是问题,但是当您将其与Entity Framework中的更改跟踪结合使用时,您现在已经发出信号,应删除整个原始集合,并用一组全新的实体替换.显然,这不是您想要的.

So, what's happening is that the original collection on your entity is entirely replaced with a brand new collection composed of brand new item instances. In many situations, this wouldn't be a problem, but when you combine that with the change tracking in Entity Framework, you've now signaled that the entire original collection should be removed and replaced with a brand new set of entities. Obviously, that's not what you want.

那么,如何解决呢?好吧,不幸的是,这有点痛苦.第一步是告诉AutoMapper映射时完全忽略集合:

So, how to solve this? Well, unfortunately, it's a bit of a pain. The first step is to tell AutoMapper to ignore the collection completely when mapping:

Mapper.CreateMap<User, UserViewModel>();
Mapper.CreateMap<UserViewModel, User>()
    .ForMember(dest => dest.UserPreferences, opts => opts.Ignore());

请注意,我将其分解为两张地图.将映射到视图模型时,您无需忽略集合.这不会造成任何问题,因为EF并未对此进行跟踪.仅当您映射回实体类时才重要.

Notice that I broke this up into two maps. You don't need to ignore the collection when mapping to your view model. That won't cause any problems because EF isn't tracking that. It only matters when you're mapping back to your entity class.

但是,现在您根本不映射该集合,那么如何将值重新返回到项目?不幸的是,这是一个手动过程:

But, now you're not mapping that collection at all, so how do you get the values back on to the items? Unfortunately, it's a manual process:

foreach (var pref in model.UserPreferences)
{
    var existingPref = user.UserPreferences.SingleOrDefault(m => m.Id == pref.Id);
    if (existingPref == null) // new item
    {
        user.UserPreferences.Add(Mapper.Map<UserPreference>(pref));
    }
    else // existing item
    {
        Mapper.Map(pref, existingPref);
    }
}