只是简单的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; } }
代码本身(为了简单起见,在控制台应用程序中测试):
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(); }
此代码抛出异常:
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.
即使最后一次映射后的实体看起来很好,也能正确反映所有变化.
我花了很多时间寻找解决方案却没有结果.请帮忙.
解决方法
// mapping AutoMapper.Mapper.Map(countryDTO,country);
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)); } } } }
用于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(); }