【问题标题】:AutoMapper 5.2.0 VIewModel to Model issue in Core MVCAutoMapper 5.2.0 VIewModel 到 Core MVC 中的模型问题
【发布时间】:2016-12-05 19:34:12
【问题描述】:

型号:

 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 =&gt; x.AddressId, opt =&gt; opt.Ignore()) 的映射中忽略 Id for Address 的映射

但它仍然将 AddressId 设置为 0。

我错过了什么?

【问题讨论】:

  • 抱歉直接说,但您使用 AutoMapper 的方式完全错误! AutoMapper从来没有用于双向映射,它的作者 Jimmy Bogard 从来没有一个合理的用例。当你仔细观察它时,除了懒惰之外,没有理由进行 2 路映射。随时阅读 Jimmy 的这篇 lostechies.com/jimmybogard/2009/09/18/… 的帖子,他解释了为什么以及它的用例。它总是从域或持久性模型到 dto 或 ViewModel!
  • 因此,当您从 MVC 操作或 WebAPI 调用中获得回传时,只需像使用 AutoMapper 之前一样执行此操作。使用 ID,从数据库中获取模型,更新它的字段,调用 SaveChanges()(或者不调用,如果发生了一些事情并且你想回滚它)。无论如何,您都需要验证您的数据,并且根据您的逻辑,某些字段在某些条件下不会结转(即,如果订单未包装为“包装为礼物”,则不要将“用户问候”添加到订单中
  • 这也可能对你有用:devtrends.co.uk/blog/…
  • 当我看复数视觉教程时,他们都用它来做 2 路映射。我可以看到这样做的危险。但他们也说一个动作必须最多 10 行。那么告诉我如何在不使用自动映射器的情况下将动作保持 10 行?当我需要将 10 多个属性映射回模型时?我应该将视图模型传回服务并在服务中进行映射?
  • 首先它是一个指导方针,而不是硬性规则。如果他们在这些教程中使用 AutoMapper 和 2 向绑定,他们也不了解该工具。你会质疑工具的作者和 DDD 的专家吗? :P 其次,我认为这些人将代码行与指令混淆了。当您将每个属性分配放在新行中时,它仍然是一个“指令”,只是分成多行。您可能还想阅读这个rogerjohansson.blog/2013/12/01/… 关于该主题的内容通过这次尝试让自己远离恐怖

标签: c# asp.net-core asp.net-core-mvc automapper-5


【解决方案1】:

当您执行Mapper.Map&lt;ClientViewModel, Client&gt;(viewModel) 时,AutoMapper 会创建一个新的 Client 对象,其 ID 与现有 Client 对象相同。

然后您指示 EntityFramework 更新此对象图。实体框架不跟踪您的新客户端对象,因此它将对象附加到其内部的魔术缓存/跟踪酱。由于 ID 冲突,此操作失败。因此出现错误“无法跟踪实体类型 'Client' 的实例,因为已经在跟踪具有相同键的该类型的另一个实例”。

这也是0 AddressId 的来源。 Address 对象也是一个全新的对象,由 AutoMapper 创建,属性值为default(int),因为 AutoMapper 在创建后从未为其分配其他值。

【讨论】:

  • 是的,没错。当我手动加载模型并映射每个单独的属性时,_clientRepository.SaveChangesAsync();无需使用 _clientRepository.Update(client); 即可自行工作。我知道我不知道如何使用 AutoMapper 将 viewModel 映射到模型。你能举个例子我应该如何将 viewModel 映射到模型吗?
  • 您可以通过 Mapper.Map&lt;TSource,TDest&gt;(sourceObject, destObject) 将“映射到”现有对象。这将适用于顶级客户端,但我不知道它是否也适用于孩子。值得一试,我想:)
  • 这一定是个玩笑,但它确实有效 :) `Mapper.Map(viewModel, property);'谢谢。然后我在这里找到了一篇解释差异的文章:link
  • 太棒了!不过要小心。如果此映射更改了 EntityFramework 跟踪的对象的 ID,或者删除了两个实体之间的关系,您遇到麻烦。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-10-19
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多