EF & Automapper. Update nested collections

automapper-5 c# entity-framework-6

Accepted Answer

The issue is thatcountry You are obtaining data from a database that contains certain cities. When you use AutoMapper in this manner:

// mapping 
        AutoMapper.Mapper.Map(countryDTO, country);

AutoMapper is acting in a manner similar to developing anIColletion<City> the right way (using one city as an example) and adding this new collection to yourcountry.Cities property.

EntityFramework's inability to handle the legacy collection of cities is the issue.

  • Should it exclude your previous cities and solely assume the most recent collection?
  • Should the two lists just be combined and kept in the database?

EF can't really make a decision for you. If you decide to continue using AutoMapper, you may modify your mapping as follows:

// AutoMapper Profile
public class MyProfile : Profile
{

    protected override void Configure()
    {

        Mapper.CreateMap<CountryData, Country>()
            .ForMember(d => d.Cities, opt => opt.Ignore())
            .AfterMap(AddOrUpdateCities);
    }

    private void AddOrUpdateCities(CountryData dto, Country country)
    {
        foreach (var cityDTO in dto.Cities)
        {
            if (cityDTO.Id == 0)
            {
                country.Cities.Add(Mapper.Map<City>(cityDTO));
            }
            else
            {
                Mapper.Map(cityDTO, country.Cities.SingleOrDefault(c => c.Id == cityDTO.Id));
            }
        }
    }
}

The Ignore() the settings forCities only allows AutoMapper to maintain the original proxy reference created byEntityFramework .

Next, we just utiliseAfterMap() to cause something to happen doing precisely what you thought:

  • To add a new city to the collection for a nation, we map it from DTO to Entity (AutoMapper produces a new instance).
  • For already-built cities, we use an abundance ofMap When the city proxy is the first argument, the existing entity is the second, and the automapper simply changes the attributes of the existing entity.

Your original code is then safe to keep:

using (var context = new Context())
    {
        // getting entity from db, reflect it to dto
        var countryDTO = context.Countries.FirstOrDefault(x => x.Id == 1).ToDTO<CountryData>();

        // add new city to dto 
        countryDTO.Cities.Add(new CityData 
                                  { 
                                      CountryId = countryDTO.Id, 
                                      Name = "new city", 
                                      Population = 100000 
                                  });

        // change existing city name
        countryDTO.Cities.FirstOrDefault(x => x.Id == 4).Name = "another name";

        // retrieving original entity from db
        var country = context.Countries.FirstOrDefault(x => x.Id == 1);

        // mapping 
        AutoMapper.Mapper.Map(countryDTO, country);

        // save and expecting ef to recognize changes
        context.SaveChanges();
    }
30
7/17/2019 2:00:34 PM

Popular Answer

Anyone looking at an issue similar to the one in the OP today might think about using AutoMapper.Collection, however this is not a solution per per. It offers assistance for the parent-child collection problems that previously required a substantial amount of code to solve.

I apologise for not providing a better answer or more information, but I am just learning about it now. The README.md file linked above provides a nice, straightforward example.

Although utilising this necessitates a little amount of rewriting, it reduces the amount of code you must write, particularly if you're using EF and can useAutoMapper.Collection.EntityFramework .



Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow