Akm*_*hov 13 c# entity-framework-6 automapper-5
我试图更新国家实体的嵌套集合(城市).
只是简单的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; }
}
Run Code Online (Sandbox Code Playgroud)
代码本身(为了简单起见,在控制台应用程序中测试):
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();
}
Run Code Online (Sandbox Code Playgroud)
此代码抛出异常:
操作失败:无法更改关系,因为一个或多个外键属性不可为空.当对关系进行更改时,相关的外键属性将设置为空值.如果外键不支持空值,则必须定义新关系,必须为外键属性分配另一个非空值,或者必须删除不相关的对象.
即使最后一次映射后的实体看起来很好并且正确反映了所有更改.
我花了很多时间寻找解决方案却没有结果.请帮忙.
Ali*_*son 27
问题是country你从数据库检索已经有一些城市.当您使用AutoMapper时,如下所示:
// mapping
AutoMapper.Mapper.Map(countryDTO, country);
Run Code Online (Sandbox Code Playgroud)
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(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));
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
Ignore()用于的配置Cities使AutoMapper只保留原始代理引用EntityFramework.
然后我们只是AfterMap()用来调用一个完全按你所做的行动:
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,
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();
}
Run Code Online (Sandbox Code Playgroud)
这本身并不是对 OP 的回答,但是今天任何看到类似问题的人都应该考虑使用AutoMapper.Collection。它为这些过去需要大量代码来处理的父子集合问题提供支持。
我很抱歉没有提供一个好的解决方案或更多的细节,但我现在只是加快速度。上面链接中显示的 README.md 中有一个很好的简单示例。
使用它需要一些重写,但它大大减少了您必须编写的代码量,特别是如果您使用 EF 并且可以使用AutoMapper.Collection.EntityFramework.
| 归档时间: |
|
| 查看次数: |
7314 次 |
| 最近记录: |