Automapper creating new instance rather than map properties

后端 未结 3 567
不思量自难忘°
不思量自难忘° 2020-12-15 21:05

This is a long one.

So, I have a model and a viewmodel that I\'m updating from an AJAX request. Web API controller receives the viewmodel, which I then update the ex

3条回答
  •  时光说笑
    2020-12-15 21:45

    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.

    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?

    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.

    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();
    Mapper.CreateMap()
        .ForMember(dest => dest.UserPreferences, opts => opts.Ignore());
    

    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(pref));
        }
        else // existing item
        {
            Mapper.Map(pref, existingPref);
        }
    }
    

提交回复
热议问题