AutoMapper / AutoMapper.Collection

AutoMapper support for updating existing collections by equivalency
MIT License
245 stars 59 forks source link

destination collection is cleared when mapping collection #114

Closed xetod closed 6 years ago

xetod commented 6 years ago

I have two models that have a one to many relation:

public class Make
{
    public int Id { get; set; }
    public string Name { get; set; }
    public ICollection<Model> Models { get; set; }
}

public class Model
{
    public int Id { get; set; }
    public string Name { get; set; }
    public Make Make { get; set; }
    public int MakeId { get; set; }
}

and the DTO as follows:

public class MakeDto
{
    public int Id { get; set; }
    public string Text { get; set; }
    public ICollection<Models> Models { get; set; }
}

I'm using Automapper.Collection to map DTO in update operation:

var config = new MapperConfiguration(cfg =>
    {
        cfg.AddCollectionMappers();
    });
var mapper = config.CreateMapper();

mapper.Map<MakeDto, Make>(makeDto, make);

the issue is I don't know why Automapper.Collection first clears destination collection and then again add new ones, no matter entities in source collection are new, removed or updated.

Tasteful commented 6 years ago

I can't see that you configure the collection mapping for the entity.

If you try to register the equality comparison when you register the type map like the following. Then AutoMapper.Collection knows how the object should be compared. If this step is omitted the standard collection mapping from AutoMapping is used.

var config = new MapperConfiguration(cfg =>
    {
        cfg.AddCollectionMappers();
        cfg.CreateMap<MakeDto, Make>()
                    .EqualityComparison((dto, entity) => dto.Id == entity.Id);

    });
var mapper = config.CreateMapper();
xetod commented 6 years ago

when I want to map in my action I got this error:

InvalidOperationException: The instance of entity type 'Model' cannot be tracked because another instance with the same key value for {'Id'} is already being tracked. When attaching existing entities, ensure that only one entity instance with a given key value is attached. Consider using 'DbContextOptionsBuilder.EnableSensitiveDataLogging' to see the conflicting key values.

the action code as follows:

        [AllowAnonymous]
        [HttpPut("{id}")]
        public IActionResult UpdateMake(int id, [FromBody]MakeDto makeDto)
        {
            var make = _context.Makes
                .Include(m => m.Models)
                .SingleOrDefault(m => m.Id == id);

            var mapper = new MapperConfiguration(
                cfg =>
                {
                    cfg.AddCollectionMappers();
                    cfg.CreateMap<MakeDto, Make>()
                        .EqualityComparison((dto, entity) => dto.Id == entity.Id);
                })
                .CreateMapper();            

            mapper.Map<MakeDto, Make>(makeDto, make);

            _context.SaveChanges();

            return Ok(make);
        }

and the JSON that I'm sending in Postman is as follows and I got the above error (Model table has two records with Id 1 and 2 and Make table has one record with Id 1):

{
    id: 1,
    Name: "Make 1",
    Models: [
        {
            Id: 1,
            Name: "Model 1"
        },
        {
            Id: 2,
            Name: "Model 2"
        }
    ]
}

but if I omit the Id of table Model in JSON, Automapper.Collection removes all records on table Model and save the records again..

TylerCarlson1 commented 6 years ago

You need to do equality comparison on the items inside the collections. Model and Models. (I'm asusming they are two distinct classes) cfg.CreateMap<Model, Models>().EqualityComparison((dto, entity) => dto.Id == entity.Id); Adding this to Automapper Configuration should fix the problem.

Also if it's a misspelled and they are the same class this might still work, although it's not an officially supported case for AutoMapper. Recommendation would be to make a ModelDto and map to that.

xetod commented 6 years ago

It's working now. Many Thanks.