문제

여러 모델이 제출되는 양식에 바인딩하는 데 문제가 있습니다.불만사항 정보와 일대다 불만사항이 포함된 불만사항 양식이 있습니다.양식을 제출하려고 하는데 바인딩 시 오류가 발생합니다.ModelState.IsValid는 항상 false를 반환합니다.

ModelState 오류를 디버깅하고 보면 다음과 같은 메시지가 나타납니다."EntityCollection이 이미 초기화되었습니다.InitializeRelatedCollection 메서드는 개체 그래프의 역직렬화 중에 새 EntityCollection을 초기화하기 위해서만 호출되어야 합니다."

또한 디버깅할 때 양식 제출에서 불만 사항 모델이 불만 사항으로 채워지는 것을 볼 수 있으므로 해당 부분이 작동하는 것 같습니다.

내가 하고 있는 일이 기본 ModelBinder로는 가능하지 않은지, 아니면 단순히 올바른 방식으로 진행하고 있지 않은지 잘 모르겠습니다.이에 대한 구체적인 예나 문서를 찾을 수 없는 것 같습니다.stackoverflow에서 매우 유사한 문제를 찾을 수 있습니다. 여기 하지만 강력한 형식의 뷰를 처리하지 않는 것 같습니다.

컨트롤러 코드:

    public ActionResult Edit(int id)
    {
        var complaint = (from c in _entities.ComplaintSet.Include("Complainants")
                     where c.Id == id
                     select c).FirstOrDefault();

        return View(complaint);
    }

    //
    // POST: /Home/Edit/5
    [AcceptVerbs(HttpVerbs.Post)]
    public ActionResult Edit(Complaint complaint)
    {
        if (!ModelState.IsValid)
        {
            return View();
        }
        try
        {
            var originalComplaint = (from c in _entities.ComplaintSet.Include("Complainants")
                                     where c.Id == complaint.Id
                                     select c).FirstOrDefault();
            _entities.ApplyPropertyChanges(originalComplaint.EntityKey.EntitySetName, complaint);
            _entities.SaveChanges();
            return RedirectToAction("Index");
        }
        catch
        {
            return View();
        }
    }

코드 보기(이것은 불만사항으로 강력하게 입력된 보기 만들기/편집에 의해 호출되는 부분 보기입니다.):

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<ProStand.Models.Complaint>" %>

<%= Html.ValidationSummary() %>
<% using (Html.BeginForm()) {%>

<table cellpadding="0" cellspacing="0" class="table">
    <tr>
        <td>
        <label for="DateReceived">Date Received:</label>
            <%= Html.TextBox("DateReceived") %>
            <%= Html.ValidationMessage("DateReceived", "*") %>    
        </td>
        <td>
            <label for="DateEntered">Date Entered:</label>
            <%= Html.TextBox("DateEntered")%>
            <%= Html.ValidationMessage("DateEntered", "*") %>
        </td>
    </tr>
    <tr>
        <td>
            <label for="Concluded">Concluded:</label>
            <%= Html.CheckBox("Concluded")%>
            <%= Html.ValidationMessage("Concluded", "*") %>
            </td>
        <td>
            <label for="IncidentDate">Incident Date:</label>
            <%= Html.TextBox("IncidentDate")%>
            <%= Html.ValidationMessage("IncidentDate", "*") %></td>
    </tr>
</table>
    <hr />
    <table>
    <% if (Model != null) {
           int i = 0;
       foreach (var complainant in Model.Complainants){ %>
       <%= Html.Hidden("Complainants[" + i + "].Id", complainant.Id)%>
    <tr>
        <td>
            <label for="Surname">Surname:</label>

            <%= Html.TextBox("Complainants[" + i + "].Surname", complainant.Surname)%>
            <%= Html.ValidationMessage("Surname", "*")%>
        </td>
        <td>
            <label for="GivenName1">GivenName1:</label>
            <%= Html.TextBox("Complainants[" + i + "].GivenName1", complainant.GivenName1)%>
            <%= Html.ValidationMessage("GivenName1", "*")%>
        </td>
    </tr>
    <% i++; %>
    <% }} %>
    <tr>
        <td colspan=2>
            <input type="submit" value="Submit" />
        </td>
    </tr>
</table>
<% } %>
<div>
    <%=Html.ActionLink("Back to List", "Index") %>
</div>
도움이 되었습니까?

해결책

맹목적인 추측 :

변화:

<%= Html.TextBox("Complainants[" + i + "].Surname", complainant.Surname)%>

와 함께:

<%= Html.TextBox("Complaint.Complainants[" + i + "].Surname",  
complainant.Surname)%>

각각 - "불만"을 추가하십시오. "고소인 [..."이전

편집하다:

이것은 정답이 아닙니다. 적절한 답변이 나타날 때까지 약간의 가치가 추가 될 수 있기 때문에 배정되지 않았습니다.

edit2 :

나는 틀릴 수도 있지만, 나에게는 엔티티 프레임 워크에 문제가있는 것 같습니다 (또는 당신이 그것을 사용하는 방식으로). ASP.NET MVC는 요청에서 값을 읽을 수 있지만 불만 제기자 수집을 초기화 할 수는 없습니다.

여기 작성되었습니다 :

InitializerElatedCollection (ttargetentity) 메소드는 기본 생성자를 사용하여 생성 된 기존 EntityCollection (Tentity)을 초기화합니다. EntityCollection (Tentity)은 제공된 관계 및 대상 역할 이름을 사용하여 초기화됩니다.

InitializerelatedCollection (ttargetentity) 방법은 사막화 중에 만 사용됩니다.

더 많은 정보 :

예외:

  • InvalidoPerationException

정황:

  • 제공된 EntityCollection (Tentity)이 이미 초기화 된 경우.
  • 관계 관리자가 이미 ObjectContext에 첨부 된 경우.
  • 관계 관리자가 이미이 이름과 대상 역할과 관계가 포함 된 경우.

다소 초기에 elateCollection이 두 번 발사됩니다. 운이 좋지 않아 - 나는 왜 정확히 밝은 아이디어를 얻지 못했습니다. 어쩌면이 작은 조사는 다른 사람에게 도움이 될 것입니다. EF에 더 많은 경험이 있습니다. :)

edit3 :
이것은이 특정 문제에 대한 해결책이 아니며, 해결 방법과 마찬가지로 MVC의 모델 부분을 처리하는 적절한 방법입니다.

프리젠 테이션 목적으로 만 뷰 모델을 만듭니다. Pure Pocos에서도 새로운 도메인 모델을 만듭니다 (EF는 다음 버전에서만 지원하기 때문에). 사용 automapper efdatacontext <=> model <=> viewModel을 맵핑하려면.

그것은 약간의 노력이 필요하지만 그것이 처리되어야하는 방법입니다. 이 접근법은 모델에서 프리젠 테이션 책임을 제거하고 도메인 모델을 정리하고 (모델에서 EF 물건을 제거) 바인딩 문제를 해결합니다.

다른 팁

public ActionResult Edit([Bind(Exclude = "Complainants")]Complaint model)
{
  TryUpdateModel(model.Complainants, "Complainants");
  if (!ModelState.IsValid)
  {
      // return the pre populated model
      return View(model);
  }

}

이것은 나를 위해 작동합니다!

불만 객체가 생성되면 '불만 제기'컬렉션이 초기화된다고 생각합니다 (엔티티 프레임 워크 자동 로직으로 인해) 모델 바인더도 컬렉션 자체를 만들려고 시도하여 오류가 발생합니다. 그러나 모델을 수동으로 업데이트하려고하면 컬렉션이 이미 초기화되었지만 모델 바인더는 다시 초기화하도록 요청받지 않습니다.

케이스 별 해결 방법 없이이 작업을 수행하려면 고유 한 모델 바인더를 만들고 Method SetProperty를 재정의해야합니다.

public class MyDefaultModelBinder : DefaultModelBinder
{
    protected override void SetProperty(ControllerContext controllerContext, ModelBindingContext bindingContext, PropertyDescriptor propertyDescriptor, object value)
    { 
        ModelMetadata propertyMetadata = bindingContext.PropertyMetadata[propertyDescriptor.Name]; 
        propertyMetadata.Model = value;
        string modelStateKey = CreateSubPropertyName(bindingContext.ModelName, propertyMetadata.PropertyName);

        // Try to set a value into the property unless we know it will fail (read-only 
        // properties and null values with non-nullable types)
        if (!propertyDescriptor.IsReadOnly) { 
        try {
            if (value == null)
            {
            propertyDescriptor.SetValue(bindingContext.Model, value);
            }
            else
            {
            Type valueType = value.GetType();

            if (valueType.IsGenericType && valueType.GetGenericTypeDefinition() == typeof(EntityCollection<>))
            {
                IListSource ls = (IListSource)propertyDescriptor.GetValue(bindingContext.Model);
                IList list = ls.GetList();

                foreach (var item in (IEnumerable)value)
                {
                list.Add(item);
                }
            }
            else
            {
                propertyDescriptor.SetValue(bindingContext.Model, value);
            }
            }

        }
        catch (Exception ex) {
            // Only add if we're not already invalid
            if (bindingContext.ModelState.IsValidField(modelStateKey)) { 
            bindingContext.ModelState.AddModelError(modelStateKey, ex); 
            }
        } 
        }
    }
}

Global.asax에 바인더를 등록하는 것을 잊지 마십시오 :

ModelBinders.Binders.DefaultBinder = new MyDefaultModelBinder();

다음을 수행하여 ModelBinding 예외를 중심으로 작업했습니다.

// Remove the error from ModelState which will have the same name as the collection.
ModelState.Remove("Complaints"/*EntityCollection*/); 
if (ModelState.IsValid) // Still catches other errors.
{
    entities.SaveChanges(); // Your ObjectContext
}

주요 단점은 예외가 여전히 발생하고 런타임에 비싸다는 것입니다. 우아한 작업은 기존 기본 바인더 주위에 래퍼를 만들고 EntityCollection을 다시 인스턴스화하는 것을 방지하는 것일 수 있습니다. 그런 다음 해당 수집을 양식 값 (FormCollection)에 바인딩합니다.

둘 이상의 컬렉션을 바인딩하는 경우 각 컬렉션의 오류를 제거해야합니다.

내 실험에서 컬렉션은 컬렉션의 일부인 그래프의 루트 객체뿐만 아니라 성공적으로 저장되었습니다.

그것이 다른 사람을 돕기를 바랍니다.

나는 동일한 문제를 겪었습니다!결국 프레임워크가 복잡한 모델을 처리할 수 없다는 것을 알게 될 것입니다.

나는 게시물의 복잡한 바인딩을 초기화할 수 있는 작은 바인딩 구성 요소를 작성했습니다.

하지만 기본적으로 해야 할 일은 Arnis L.말하고 있다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top