2011-02-26 2 views
6

Asp.Net에 널 (null) 오류가 나는 AutoMapper에 완전히 새로운 해요, 자식 컬렉션에 모델 AutoMapper를 사용하려고, 나는 다음과 같습니다 뷰가 : 여기 는 MVC 3

@using (Html.BeginForm(null, null, FormMethod.Post, new { enctype = "multipart/form-data" })) 
{ 
    @Html.ValidationSummary(true) 
    <fieldset> 
     <legend>Consultant</legend> 
     <div class="editor-label"> 
      @Html.LabelFor(model => model.FirstName) 
     </div> 
     <div class="editor-field"> 
      @Html.EditorFor(model => model.FirstName) 
      @Html.ValidationMessageFor(model => model.FirstName) 
     </div> 
     <div class="editor-label"> 
      @Html.LabelFor(model => model.LastName) 
     </div> 
     <div class="editor-field"> 
      @Html.EditorFor(model => model.LastName) 
      @Html.ValidationMessageFor(model => model.LastName) 
     </div> 
     <div class="editor-label"> 
      @Html.LabelFor(model => model.Description) 
     </div> 
     <div class="editor-field"> 
      @Html.TextAreaFor(model => model.Description) 
      @Html.ValidationMessageFor(model => model.Description) 
     </div> 
     <div class="editor-label"> 
      Program du behärskar: 
     </div> 
     <div> 
      <table id="programEditorRows"> 
       <tr> 
        <th> 
         Program 
        </th> 
        <th> 
         Nivå 
        </th> 
       </tr> 
       @foreach (var item in Model.Programs) 
       { 
        Html.RenderPartial("ProgramEditorRow", item); 
       } 
      </table> 
      <a href="#" id="addProgram">Lägg till</a> 
     </div> 
     <div class="editor-label"> 
      Språk du behärskar: 
     </div> 
     <div> 
      <table id="languageEditorRows"> 
       <tr> 
        <th> 
         Språk 
        </th> 
        <th> 
         Nivå 
        </th> 
       </tr> 
       @foreach (var item in Model.Languages) 
       { 
        Html.RenderPartial("LanguageEditorRow", item); 
       } 
      </table> 
      <a href="#" id="addLanguage">Lägg till</a> 
     </div> 
     <div> 
      <table id="educationEditorRows"> 
       <tr> 
        <th> 
         Utbildning 
        </th> 
        <th> 
         Nivå 
        </th> 
       </tr> 
       @foreach (var item in Model.Educations) 
       { 
        Html.RenderPartial("EducationEditorRow", item); 
       } 
      </table> 
      <a href="#" id="addEducation">Lägg till</a> 
     </div> 
     <div> 
      <table id="workExperienceEditorRows"> 
       <tr> 
        <th> 
         Arbetserfarenhet 
        </th> 
        <th> 
         Startdatum 
        </th> 
        <th> 
         Slutdatum 
        </th> 
       </tr> 
       @foreach (var item in Model.WorkExperiences) 
       { 
        Html.RenderPartial("WorkExperienceEditorRow", item); 
       } 
      </table> 
      <a href="#" id="addWorkExperience">Lägg till</a> 
     </div> 
     <div> 
      <table id="competenceAreaEditorRows"> 
       <tr> 
        <th> 
         Kompetensområde 
        </th> 
        <th> 
         Nivå 
        </th> 
       </tr> 
       @foreach (var item in Model.CompetenceAreas) 
       { 
        Html.RenderPartial("CompetenceAreaEditorRow", item); 
       } 
      </table> 
      <a href="#" id="addCompetenceArea">Lägg till</a> 
     </div> 
     <div> 
      <input id="fileInput" name="FileInput" type="file" /> 
     </div> 
     <p> 
      <input type="submit" value="Spara" /> 
     </p> 
    </fieldset> 
} 
<div> 
    @Html.ActionLink("Back to List", "Index") 
</div> 

가 GET의를 편집 방법 :

public ActionResult Edit(int id) 
    { 
     Consultant consultant = _repository.GetConsultant(id); 
     ConsultantViewModel vm = Mapper.Map<Consultant, ConsultantViewModel>(consultant); 
     return View(vm); 
    } 

그리고 POST 편집 방법 AutoMapper는 뷰 모델을 만들 때

[HttpPost] 
    [ValidateInput(false)] //To allow HTML in description box 
    public ActionResult Edit(int id, ConsultantViewModel vm, FormCollection collection) 
    { 

      Consultant consultant = Mapper.Map<ConsultantViewModel, Consultant>(vm); 
      _repository.Save(); 
      return RedirectToAction("Index"); 

    } 

지금, 그것은 보인다 (가장 단순한 형식을 사용하거나, 해결자가 없거나, 컨설턴트를 ConsultantViewModel에 매핑하는 것). 또한 UserName 속성도 있습니다. 현재보기에서는 현재 사용자 (User.Identity.Name)가 항상 자동으로 채우기 때문에 UserName에 대한 필드가 없습니다. 그러나 VM을 다시 가져 오면 UserName 속성은 null입니다. 아마도 뷰에 필드가 없기 때문일 것입니다.

컨설턴트가 언어를 채우는 것이 선택 사항이기 때문에 UserName에 숨겨진 필드를 넣더라도 컬렉션 중 일부가 동일한 오류를 일으킬 것으로 의심됩니다. 따라서 ViewModel에 인스턴스가있는 경우에도 View에있는 자식 콜렉션 각각에 대한 목록 (카운트가 0 인)은 null 값으로 대신 반환됩니다.

어떻게 해결할 수 있습니까? 사용자가 모든 하위 컬렉션에 값을 채우고 싶지는 않습니다. 내 말은, 항상 Name 속성에 빈 문자열을 사용하여 Language 객체를 만들 수는 있지만 불필요한 추가 코드가 필요하다는 것을 의미합니다. 그리고 실제로 원하는 것은 모두 자식 컬렉션 (및 UserName)을 얻는 것입니다. 보기 - 사용자 이름이 채워지고 자식 콜렉션이 인스턴스화되지만 사용자가 항목을 추가하지 않은 경우 개수가 0입니다.

UPDATE :

내가 모르는

, 나는 내가 실제로 아이 컬렉션은 괜찮 았는데 그 매핑과 관련하여 정말 문제가 were'nt 발견 ... 어떻게 든 AutoMapper 오해 것 같아요 이를 다시 Consultant 오브젝트로 맵핑합니다. 그러나 ... ViewModel에는 아무 것도 없었기 때문에이 ID를 Consultant 객체에 다시 넣어야했습니다. 하지만 그렇더라도 저장소에 저장하면 저장되지 않습니다. 이것은 내가 AutoMapper를 오해 한 것이라고 생각합니다. 저는 ViewModel의 값으로 Consultant 객체를 채울 것이라고 생각했지만, Consultant 변수가 Map() 문의 다른 객체를 참조하게 만들었을 것입니다.

[HttpPost] 
    [ValidateInput(false)] //To allow HTML in description box 
    public ActionResult Edit(int id, ConsultantViewModel vm, FormCollection collection) 
    { 

     vm.UserName = User.Identity.Name; 
     Consultant consultant = _repository.GetConsultant(id); 
     consultant = Mapper.Map<ConsultantViewModel, Consultant>(vm); 
     consultant.Id = id; 
     _repository.Save(); 
     return RedirectToAction("Index"); 

    } 

내가 잘못 뭐하는 거지 : 그것의 어느 것도 지속되지 않기 때문에 ...

여기

수정 된 POST 방법은 (작동하지 않는)입니까? ViewModel에 채워진 값을 Consultant 객체로 다시 가져 와서 데이터베이스에 유지 하시겠습니까 ???

업데이트 2 :

좋아, 대체로 혼란 스럽다.조금 이상 시작 :

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

편집 방법 : 다음 위해 Application_Start에서지도 제작의

// GET: /Consultant/Edit/5 

    public ActionResult Edit(int id) 
    { 
     Consultant consultant = _repository.GetConsultant(id); 
     ConsultantViewModel vm = Mapper.Map<Consultant, ConsultantViewModel>(consultant); 
     return View(vm); 
    } 

    // 
    // POST: /Consultant/Edit/5 

    [HttpPost] 
    [ValidateInput(false)] //To allow HTML in description box 
    public ActionResult Edit(int id, ConsultantViewModel vm, FormCollection collection) 
    { 
     vm.UserName = User.Identity.Name; 
     Consultant consultant = _repository.GetConsultant(id); 
     consultant = Mapper.Map<ConsultantViewModel, Consultant>(vm, consultant); 
     _repository.Save(); 
     return RedirectToAction("Index"); 
    } 

이 중 하나가 작동,하지만 분명히 적어도 개체 모델을 업데이트하려고하지 않습니다가, 지금 때문에 I 예외가 발생했습니다.

EntityCollection이 속한 개체의 관계 관리자가 이미 ObjectContext에 연결되어 있으므로 EntityCollection을 초기화 할 수 없습니다. InitializeRelatedCollection 메서드는 개체 그래프의 deserialize 중에 새 EntityCollection을 초기화하기 위해서만 호출해야합니다.

그리고 YSOD 오류 코드 샘플 :

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

이 거의 오히려 AutoMapper는 뷰 모델을 만들 필요없이, 모델로 직접 엔티티 객체를 사용하려고 할 때 내가 가진 같은 오류입니다. 그래서 내가 뭘 잘못하고 있니? 이

UPDATE 3 ... 날 미치게됩니다 : 글쎄, 이야기를 네버 앤딩 ... 내가 AutoMapper에서 CreateMap 방법에 UseDestinationValue를 사용하는 방법에 대한 몇 가지 정보를 발견

. 그래서 나는 그것을 시도했다. 그리고 그런데, 그것은 실제로 나를 조금 더 붙 잡았다. 하지만 ... 이제 SaveChanges() (EF 모델)에 대한 새로운 예외가 생겼습니다. 이제 예외가 있습니다 : "작업이 실패했습니다 : 하나 이상의 외래 키 속성이 nullable이 아니기 때문에 관계를 변경할 수 없습니다." 이것은 캐스케이드 삭제 집합이 없는데도 일대 다 관계에있는 자식 객체를 삭제하려고 할 때도 예외로 보이지만 여기서는 내가하려고하는 것이 아닙니다 ...

다음은 업데이트 된 CreateMap 메소드입니다.

Mapper.CreateMap<ConsultantViewModel, Consultant>().ForMember("Id", opts => opts.Ignore()).ForMember(
       x => x.Programs, opts => opts.UseDestinationValue()); 
      Mapper.CreateMap<Consultant, ConsultantViewModel>(); 

아이디어가 있으십니까?

답변

4

아직 답변을 얻지 못했지만 실제로 작동하도록하는 방법을 찾았습니다. 코드가 장황하기 때문에 여전히 기분이 좋지 않습니다 ... 누군가가 더 좋은 아이디어를 가지고 있다면, 가져 오십시오!

하위 컬렉션의 각 유형에 대해 DTO 개체를 갖도록 변경했습니다 (AutoMapper를 사용할 때 시작해야 함). 그래서 예를 들어 지금은 프로그램과 매핑 할 ProgramDTO 유형이 있습니다.

중첩 된 컬렉션이 작동하기를 원하지만 "EntityCollection이 이미 초기화되었습니다."라는 오류 만 다시 발생하여 Consultant 개체와의 매핑을 시도했습니다. 그래서 직감에 내가 대신이 방법을 시도 :

private Consultant CreateConsultant(ConsultantViewModel vm, Consultant consultant) //Parameter Consultant needed because an object may already exist from Edit method. 
    { 

     Mapper.Map(vm, consultant); 
//To do this I had to add an Ignore in the mapping configuration: 
//Mapper.CreateMap<ConsultantViewModel, Consultant>().ForMember(x => x.Programs, opts => opts.Ignore()); 

     //Delete items "marked for deletion" by removing with jQuery in the View: 
     var programs = consultant.Programs.Except(consultant.Programs.Join(vm.Programs, p => p.Id, d => d.Id, (p, d) => p)).ToList(); 
     Delete(programs); 

     foreach (var programDto in vm.Programs) 
     { 
      Program program = consultant.Programs.SingleOrDefault(x => x.Id == programDto.Id); 
      if (program == null) 
      { 
       program = new Program(); 
       consultant.Programs.Add(program); 
      } 
      program = Mapper.Map(programDto, program); 
     } 

     _repository.Save(); 

     return consultant; 
    } 

차이가 나는 UpdateModel()에 의해 컨설턴트의 간단한 속성을 채우되고, 다음 ProgramDTOs의 컬렉션을 반복하고 각 프로그램을 매핑합니다.

코드를 매우 좋아하지는 않지만 ...이 작업을 마친 후에도 사용자가 "삭제로 표시 한 항목을 삭제할 수 있어야합니다. "보기에서 말하기. 스티븐 샌더슨 (Steven Sanderson)의 튜토리얼을 따라 jQuery로 텍스트 상자 등을 추가하고 제거 할 수있는 뷰를 사용하고 있습니다. 그러나 이것은 코드를 더욱 복잡하게 만들었습니다 ... 어쨌든 이것은 제가 생각할 수있는 최선이었습니다. 그래서 이것을 개선 할 수 있다면 다른 아이디어도 다시 제공해주세요!나는 POST에서 컬렉션을 수동으로 반복 할 필요가없는 솔루션을 선호했지만 위에서 언급 한 오류없이 AutoMapper가 중첩 컬렉션 자체를 처리하도록했습니다!

+0

나는 해결책이 마음에 들지 않는다는 데 동의하지만 지금은 문제를 해결하기 위해 데이터 모델을 완전히 다시 할 필요가 없다. 나는이 해결책을 생각해 냈다. 더 좋은 방법이 있었으면 좋겠다. –