c# – EF和Automapper.更新嵌套集合

前端之家收集整理的这篇文章主要介绍了c# – EF和Automapper.更新嵌套集合前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
我试图更新国家实体的嵌套集合(城市).

只是简单的enitities和dto:

// EF Models
public class Country
{
    public int Id { get; set; }
    public string Name { get; set; }
    public virtual ICollection<City> Cities { get; set; }
}

public class City
{
    public int Id { get; set; }
    public string Name { get; set; }
    public int CountryId { get; set; }
    public int? Population { get; set; }

    public virtual Country Country { get; set; }
}

// DTo's
public class CountryData : IDTO
{
    public int Id { get; set; }
    public string Name { get; set; }
    public virtual ICollection<CityData> Cities { get; set; }
}

public class CityData : IDTO
{
    public int Id { get; set; }
    public string Name { get; set; }
    public int CountryId { get; set; }
    public int? Population { get; set; }
}@H_403_5@ 
 

代码本身(为了简单起见,在控制台应用程序中测试):

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();
        }@H_403_5@ 
 

代码抛出异常:

The operation Failed: The relationship could not be changed because one or more of the foreign-key properties is non-nullable. When a change is made to a relationship,the related foreign-key property is set to a null value. If the foreign-key does not support null values,a new relationship must be defined,the foreign-key property must be assigned another non-null value,or the unrelated object must be deleted.

即使最后一次映射后的实体看起来很好,也能正确反映所有变化.

我花了很多时间寻找解决方案却没有结果.请帮忙.

解决方法

问题是您从数据库检索的国家已经有一些城市.当您使用AutoMapper时,如下所示:
// mapping 
        AutoMapper.Mapper.Map(countryDTO,country);@H_403_5@ 
 

AutoMapper正在做类似于创建IColletion< City>的事情.正确(在您的示例中有一个城市),并将此全新集合分配给您的country.Cities属性.

问题是EntityFramework不知道如何处理旧的城市集合.

>它应该删除您的旧城市并仅假设新的集合吗?
>它应该只合并两个列表并保存在数据库中吗?

事实上,EF无法为您做出决定.如果您想继续使用AutoMapper,可以像这样自定义映射:

// AutoMapper Profile
public class MyProfile : Profile
{

    protected override void Configure()
    {

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


    }

    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));
            }
        }
    }
}@H_403_5@ 
 

用于Cities的Ignore()配置使AutoMapper只保留EntityFramework构建的原始代理引用.

然后我们只使用AfterMap()来调用一个完全按照你所要求的行为:

>对于新城市,我们从DTO映射到实体(AutoMapper创建一个新的
实例)并将其添加到国家/地区的集合中.
>对于现有城市,我们使用Map的重载,其中我们将现有实体作为第二个参数传递,将城市代理作为第一个参数传递,因此automapper只更新现有实体的属性.

然后你可以保留原始代码

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,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();
    }@H_403_5@

猜你在找的C#相关文章