尝试将AutoMapper用于具有子集合的模型,在Asp.Net MVC 3中获取null错误

前端之家收集整理的这篇文章主要介绍了尝试将AutoMapper用于具有子集合的模型,在Asp.Net MVC 3中获取null错误前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
我是AutoMapper的新手,我有一个看起来像这样的视图:
  1. @using (Html.BeginForm(null,null,FormMethod.Post,new { enctype = "multipart/form-data" }))
  2. {
  3. @Html.ValidationSummary(true)
  4. <fieldset>
  5. <legend>Consultant</legend>
  6. <div class="editor-label">
  7. @Html.LabelFor(model => model.FirstName)
  8. </div>
  9. <div class="editor-field">
  10. @Html.EditorFor(model => model.FirstName)
  11. @Html.ValidationMessageFor(model => model.FirstName)
  12. </div>
  13. <div class="editor-label">
  14. @Html.LabelFor(model => model.LastName)
  15. </div>
  16. <div class="editor-field">
  17. @Html.EditorFor(model => model.LastName)
  18. @Html.ValidationMessageFor(model => model.LastName)
  19. </div>
  20. <div class="editor-label">
  21. @Html.LabelFor(model => model.Description)
  22. </div>
  23. <div class="editor-field">
  24. @Html.TextAreaFor(model => model.Description)
  25. @Html.ValidationMessageFor(model => model.Description)
  26. </div>
  27. <div class="editor-label">
  28. Program du behärskar:
  29. </div>
  30. <div>
  31. <table id="programEditorRows">
  32. <tr>
  33. <th>
  34. Program
  35. </th>
  36. <th>
  37. Nivå
  38. </th>
  39. </tr>
  40. @foreach (var item in Model.Programs)
  41. {
  42. Html.RenderPartial("ProgramEditorRow",item);
  43. }
  44. </table>
  45. <a href="#" id="addProgram">Lägg till</a>
  46. </div>
  47. <div class="editor-label">
  48. Språk du behärskar:
  49. </div>
  50. <div>
  51. <table id="languageEditorRows">
  52. <tr>
  53. <th>
  54. Språk
  55. </th>
  56. <th>
  57. Nivå
  58. </th>
  59. </tr>
  60. @foreach (var item in Model.Languages)
  61. {
  62. Html.RenderPartial("LanguageEditorRow",item);
  63. }
  64. </table>
  65. <a href="#" id="addLanguage">Lägg till</a>
  66. </div>
  67. <div>
  68. <table id="educationEditorRows">
  69. <tr>
  70. <th>
  71. Utbildning
  72. </th>
  73. <th>
  74. Nivå
  75. </th>
  76. </tr>
  77. @foreach (var item in Model.Educations)
  78. {
  79. Html.RenderPartial("EducationEditorRow",item);
  80. }
  81. </table>
  82. <a href="#" id="addEducation">Lägg till</a>
  83. </div>
  84. <div>
  85. <table id="workExperienceEditorRows">
  86. <tr>
  87. <th>
  88. Arbetserfarenhet
  89. </th>
  90. <th>
  91. Startdatum
  92. </th>
  93. <th>
  94. Slutdatum
  95. </th>
  96. </tr>
  97. @foreach (var item in Model.WorkExperiences)
  98. {
  99. Html.RenderPartial("WorkExperienceEditorRow",item);
  100. }
  101. </table>
  102. <a href="#" id="addWorkExperience">Lägg till</a>
  103. </div>
  104. <div>
  105. <table id="competenceAreaEditorRows">
  106. <tr>
  107. <th>
  108. Kompetensområde
  109. </th>
  110. <th>
  111. Nivå
  112. </th>
  113. </tr>
  114. @foreach (var item in Model.CompetenceAreas)
  115. {
  116. Html.RenderPartial("CompetenceAreaEditorRow",item);
  117. }
  118. </table>
  119. <a href="#" id="addCompetenceArea">Lägg till</a>
  120. </div>
  121. <div>
  122. <input id="fileInput" name="FileInput" type="file" />
  123. </div>
  124. <p>
  125. <input type="submit" value="Spara" />
  126. </p>
  127. </fieldset>
  128. }
  129. <div>
  130. @Html.ActionLink("Back to List","Index")
  131. </div>

这是GET Edit方法

  1. public ActionResult Edit(int id)
  2. {
  3. Consultant consultant = _repository.GetConsultant(id);
  4. Consultantviewmodel vm = Mapper.Map<Consultant,Consultantviewmodel>(consultant);
  5. return View(vm);
  6. }

和POST编辑方法

  1. [HttpPost]
  2. [ValidateInput(false)] //To allow HTML in description Box
  3. public ActionResult Edit(int id,Consultantviewmodel vm,FormCollection collection)
  4. {
  5.  
  6. Consultant consultant = Mapper.Map<Consultantviewmodel,Consultant>(vm);
  7. _repository.Save();
  8. return RedirectToAction("Index");
  9.  
  10. }

现在,当AutoMapper创建viewmodel时,它似乎工作正常(使用其最简单的形式,没有解析器或任何东西,只是将Consultant映射到Consultantviewmodel),包括子集合和所有.此外,UserName属性在那里.现在,在视图中我没有UserName的字段,因为它总是由当前用户(User.Identity.Name)自动填充.但是当我返回vm时,UserName属性为null,可能是因为View中没有该字段.

我怀疑一些集合会导致相同的错误,即使我在那里为UserName放置一个隐藏字段,因为它是可选的,以便顾问填写语言等…所以即使viewmodel有一个实例化的列表,每个这些子集合进入View(计数为0),它们返回时返回null值.

我该如何解决这个问题?我不想强迫用户在所有子集合中填充值.我的意思是,我总是可以为Name属性创建一个带有空字符串的Language对象,但这意味着不必要的额外代码,我真正想要的是让孩子集合(和UserName)回到他们进入的方式.视图 – 填写UserName,并且实例化子集合,但如果用户添加任何项目,则计数为0.

更新:

我不知道,我认为我在某种程度上误解了AutoMapper ……我发现实际上子集合在映射方面确实不是问题,它可以很好地将它映射回Consultant对象.但是……我还需要将id放回Consultant对象中,因为viewmodel没有.但即便如此,当我保存到存储库时,它也不会被保存.这就是我认为我误解了AutoMapper的地方 – 我曾经以为它会以某种方式用viewmodel中的值填充Consultant对象,但我想它会导致顾问变量引用Map()语句中的另一个对象?因为没有一个坚持……

这是修改后的POST方法(不起作用):

  1. [HttpPost]
  2. [ValidateInput(false)] //To allow HTML in description Box
  3. public ActionResult Edit(int id,FormCollection collection)
  4. {
  5.  
  6. vm.UserName = User.Identity.Name;
  7. Consultant consultant = _repository.GetConsultant(id);
  8. consultant = Mapper.Map<Consultantviewmodel,Consultant>(vm);
  9. consultant.Id = id;
  10. _repository.Save();
  11. return RedirectToAction("Index");
  12.  
  13. }

我究竟做错了什么?如何将viewmodel中的填充值返回到Consultant对象并将其保存到数据库

更新2:

好吧,彻底迷茫…开始有点:这是Application_Start中的地图创建:

  1. Mapper.CreateMap<Consultantviewmodel,Consultant>().ForMember("Id",opts => opts.Ignore());
  2. Mapper.CreateMap<Consultant,Consultantviewmodel>();

编辑方法

  1. // GET: /Consultant/Edit/5
  2.  
  3. public ActionResult Edit(int id)
  4. {
  5. Consultant consultant = _repository.GetConsultant(id);
  6. Consultantviewmodel vm = Mapper.Map<Consultant,Consultantviewmodel>(consultant);
  7. return View(vm);
  8. }
  9.  
  10. //
  11. // POST: /Consultant/Edit/5
  12.  
  13. [HttpPost]
  14. [ValidateInput(false)] //To allow HTML in description Box
  15. public ActionResult Edit(int id,FormCollection collection)
  16. {
  17. vm.UserName = User.Identity.Name;
  18. Consultant consultant = _repository.GetConsultant(id);
  19. consultant = Mapper.Map<Consultantviewmodel,Consultant>(vm,consultant);
  20. _repository.Save();
  21. return RedirectToAction("Index");
  22. }

这也不起作用,但显然至少会尝试更新实体模型,因为现在我得到一个例外:

无法初始化EntityCollection,因为EntityCollection所属的对象的关系管理器已附加到ObjectContext.只应调用InitializeRelatedCollection方法在对象图的反序列化期间初始化新的EntityCollection.

和YSOD错误代码示例:

  1. Line 698: if ((value != null))
  2. Line 699: {
  3. Line 700: ((IEntityWithRelationships)this).RelationshipManager.InitializeRelatedCollection<Program>("ConsultantsModel.ConsultantProgram","Program",value);
  4. Line 701: }
  5. Line 702: }

这与我在尝试直接将实体对象用作模型时获得的错误完全相同,而不是让AutoMapper创建viewmodel.那么我做错了什么?这真让我抓狂…

更新3:

好吧,无尽的故事……我在AutoMapper的CreateMap方法中找到了一些使用UseDestinationValue的信息.所以我试过了,好吧,实际上让我更进一步.但是……现在我在SaveChanges()上获得了一个新例外(在EF模型中).现在的例外是:“操作失败:由于一个或多个外键属性不可为空,因此无法更改关系.”这似乎是一个异常,如果您没有级联删除集,当尝试删除一对多关系中的子对象时也会发生异常,但这不是我在这里尝试做的…

这是更新的CreateMap方法

  1. Mapper.CreateMap<Consultantviewmodel,opts => opts.Ignore()).ForMember(
  2. x => x.Programs,opts => opts.UseDestinationValue());
  3. Mapper.CreateMap<Consultant,Consultantviewmodel>();

有任何想法吗?

解决方法

还没有得到任何答案,我实际上找到了一种方法来使它工作.仍然感觉不好,因为代码有点冗长…所以如果有人有更好的想法,请带上它们!

我改变它,以便我现在为子集合中的每个类型都有一个DTO对象(可能应该在使用AutoMapper时开始使用它).例如,我现在有一个ProgramDTO类型与Program一起映射.

我尝试使用Consultant对象进行映射,希望嵌套集合能够自行运行,但只能再次出现“EntityCollection已初始化”错误.所以在预感中我尝试了这种方法

  1. private Consultant CreateConsultant(Consultantviewmodel vm,Consultant consultant) //Parameter Consultant needed because an object may already exist from Edit method.
  2. {
  3.  
  4. Mapper.Map(vm,consultant);
  5. //To do this I had to add an Ignore in the mapping configuration:
  6. //Mapper.CreateMap<Consultantviewmodel,Consultant>().ForMember(x => x.Programs,opts => opts.Ignore());
  7.  
  8. //Delete items "marked for deletion" by removing with jQuery in the View:
  9. var programs = consultant.Programs.Except(consultant.Programs.Join(vm.Programs,p => p.Id,d => d.Id,(p,d) => p)).ToList();
  10. Delete(programs);
  11.  
  12. foreach (var programDto in vm.Programs)
  13. {
  14. Program program = consultant.Programs.SingleOrDefault(x => x.Id == programDto.Id);
  15. if (program == null)
  16. {
  17. program = new Program();
  18. consultant.Programs.Add(program);
  19. }
  20. program = Mapper.Map(programDto,program);
  21. }
  22.  
  23. _repository.Save();
  24.  
  25. return consultant;
  26. }

不同之处在于我通过UpdateModel()填充Consultant的简单属性,然后遍历ProgramDTO集合并映射每个程序.

这很有用,虽然我不太喜欢这个代码……在我这样做之后它也打了我,我还需要能够删除用户标记删除”的任何项目,可以这么说在视图中.我正在使用一个视图,您可以按照Steven Sanderson的教程,通过jQuery添加删除文本框等.但是这使得代码变得更加复杂……无论如何,这是我能想到的最好的,所以如果你能改进这个,请再提供任何其他想法!我特别希望有一个解决方案,我不需要在POST上手动循环集合,但让AutoMapper处理嵌套集合本身,没有上面提到的错误

猜你在找的asp.Net相关文章