型号:
public class Client{
public int Id {get;set;}
public string Name {get;set;}
public Address Address {get;set;}
public int AddressId {get;set;}
}
public class Address{
public int Id
public string Address1 {get;set;}
public string PostCode {get;set;}
}
查看模特:
public class ClientViewNodel{
public int Id {get;set;}
public string Name {get;set;}
public Address Address {get;set;}
public int AddressId {get;set;}
}
public class AddressViewModel{
public int Id
public string Address1 {get;set;}
public string PostCode {get;set;}
}
映射:
Mapper.Initialize(config =>
{
config.CreateMap<ClientViewModel, Client>().ReverseMap();
config.CreateMap<AddressViewModel, Address>().ReverseMap();
});
控制器更新操作:
[HttpPost]
public async Task<IActionResult> Update(cLIENTViewModel viewModel)
{
if (!ModelState.IsValid)
{
return View("Client",viewModel);
}
var client= _clientRepository.GetClient(viewModel.Id);
if (client == null)
return NotFound();
client= Mapper.Map<ClientViewModel, Client>(viewModel);
_clientRepository.Update(client);
var result = await _clientRepository.SaveChangesAsync();
if (result.Success)
{
return RedirectToAction("Index");
}
ModelState.AddModelError("", result.Message);
return View("Client",viewModel);
}
问题是当调用_clientRepository.Update(client)
时,我收到一条错误消息:
无法跟踪实体类型“客户端”的实例,因为已经跟踪了具有相同密钥的此类型的另一个实例。添加新实体时,对于大多数密钥类型,如果未设置密钥,则将创建唯一的临时密钥值(即,如果为密钥属性指定了其类型的默认值)。如果要为新实体显式设置键值,请确保它们不会与现有实体或为其他新实体生成的临时值发生冲突。附加现有实体时,请确保只有一个具有给定键值的实体实例附加到上下文。
当我调试代码时,我可以看到当我将viewModel映射到模型时,客户端模型中的AddressID被设置为0.我猜这是导致问题。
如何将viewModel映射回模型,其中将更新地址的详细信息,例如Address1和Postcode,而不是Id。
我还试图在.ForMember(x => x.AddressId, opt => opt.Ignore())
但它仍然将AddressId设置为0.
我错过了什么?
答案 0 :(得分:3)
执行Mapper.Map<ClientViewModel, Client>(viewModel)
时,AutoMapper会创建一个新的Client对象,其ID与现有Client对象的ID相同。
然后,您指示EntityFramework更新此对象图。实体框架不跟踪您的新客户端对象,因此它将对象附加到其内部魔术缓存/跟踪酱。由于ID的冲突,这会失败。因此,错误“无法跟踪实体类型'客户'的实例,因为已经跟踪了具有相同密钥的此类型的另一个实例”。
这也是0
AddressId的来源。 Address对象也是一个由AutoMapper创建的全新对象,该属性的值为default(int)
,因为AutoMapper创建后它从未被赋予其他值。