Frage

Ich habe Probleme auf einem Formular mit mehreren Modellen verbindlich vorgelegt. Ich habe eine Beschwerde Form, die Beschwerde Informationen sowie eine Eins-zu-viele Beschwerdeführer umfasst. Ich versuche, das Formular zu senden, aber ich bin Fehler auf dem bind bekommen. ModelState.IsValid immer false zurück.

Wenn ich das Model Fehler debuggen und sehen, bekomme ich ein Sprichwort: .

„Die EntityCollection bereits initialisiert wurde. Die InitializeRelatedCollection Methode sollte nur ein neues EntityCollection während der Deserialisierung eines Objekts Graph zu initialisieren aufgerufen werden“

Auch beim Debuggen, kann ich sehen, dass sie die die Beschwerde Modelle mit Klägern aus dem Formular Vorlage bevölkert bekommen, so ist es, dass ein Teil scheint funktioniert.

Ich bin nicht sicher, ob das, was ich tue, nicht möglich mit den Standard-Modelbindern ist, oder wenn ich einfach nicht darüber auf dem richtigen Weg. Ich kann nicht scheinen keine konkreten Beispiele oder Dokumentation zu diesem Thema zu finden. Ein sehr ähnliches Problem kann auf Stackoverflow hier werden aber es scheint nicht mit stark typisierte Ansichten zu beschäftigen.

Controller-Code:

    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();
        }
    }

Code anzeigen (Dies ist eine Teilansicht, die von Create / Edit Ansichten aufgerufen wird, die mit Beschwerde auch stark typisiert werden):

<%@ 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>
War es hilfreich?

Lösung

Blind Vermutung:

Änderung:

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

mit:

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

Beziehungsweise - add "Beschwerde". vor "Klägern [..."

Bearbeiten :

Dies ist nicht eine richtige Antwort. Beließ es undeleted nur, weil das könnte einen Wert hinzufügen, bis die richtige Antwort erscheint.

EDIT2:

Ich könnte falsch sein, aber für mich ist es scheint, gibt es Problem mit Entity Framework (oder - mit der Art und Weise Sie es verwenden). Ich meine - asp.net Mvc schafft Werte von Anfrage zu lesen, aber nicht zu den Antragstellern Sammlung initialisieren.

Hier es geschrieben:

  

Die InitializeRelatedCollection (TTargetEntity) -Methode initialisiert eine bestehende EntityCollection (TEntity), die mit dem Standard-Konstruktor erstellt wurde. Die EntityCollection (TEntity) initialisiert durch die zur Verfügung gestellten Beziehung und Zielrollennamen verwendet wird.

     

Die InitializeRelatedCollection (TTargetEntity) Methode wird nur bei der Deserialisierung verwendet wird.

Einige weitere Informationen:

  

Ausnahme:

     
      
  • InvalidOperationException
  •   
     

Bedingungen:

     
      
  • Wenn die mitgelieferte EntityCollection (TEntity) bereits initialisiert.
  •   
  • Wenn die Beziehung Manager bereits auf einen Object angebracht ist.
  •   
  • Wenn die Beziehung Manager enthält bereits eine Beziehung mit diesem Namen und Zielrolle.
  •   

Somewhy InitializeRelatedCollection wird zweimal gebrannt. Unglücklicherweise - ich erhielt keine gute Ideen, warum genau. Vielleicht wird diese kleine Untersuchung für jemanden anderen helfen - mehr mit EF erfahren. :)

EDIT3:
Dies ist keine Lösung für dieses spezielle Problem, eher wie dieses Problem zu umgehen, eine richtige Art und Weise Modellteil von Mvc zu handhaben.

Erstellen Sie ein Ansichtsmodell für nur Präsentationszwecke. Erstellen Sie ein neues Domain-Modell aus reinem POCOs auch (weil EF wird sie in der nächsten Version unterstützt nur). Verwenden Sie AutoMapper EFDataContext <=> Modell <=> Ansichtsmodell abzubilden.

Das würde einige Mühe, aber das ist, wie es behandelt werden soll. Dieser Ansatz Präsentation Verantwortung aus dem Modell entfernt, reinigt Ihr Domain-Modell (EF Sachen aus dem Modell entfernt) und würde löst Ihr Problem mit Bindung.

Andere Tipps

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

}

Das funktioniert für mich!

Ich denke, dass, wenn Beschwerde Objekt dann seine Sammlung initialisiert wird ‚Kläger‘ erstellt wird (wegen der Auto Logik Entity Framework) und dann Binder Modell der Sammlung selbst als auch zu schaffen versucht, die den Fehler verursacht. Aber wenn wir versuchen, das Modell zu aktualisieren manuell dann Sammlung bereits initialisiert aber Binder Modell wird gebeten, es nicht zu initialisieren wieder.

Um dies zu erhalten, ohne zu arbeiten, von Fall zu Fall Umgehungen müssen Sie Ihre eigene Modell Binder und Überschreibung Methode SetProperty erstellen:

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); 
            }
        } 
        }
    }
}

Vergessen Sie nicht, Ihre Mappe in Global.asax zu registrieren:

ModelBinders.Binders.DefaultBinder = new MyDefaultModelBinder();

Ich arbeitete rund um die ModelBinding Ausnahme der folgenden Aktionen ausführen:

// 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
}

Der größte Nachteil ist, dass die Ausnahme noch ausgelöst wird, und das kann zur Laufzeit teuer sein. Die elegante Arbeit um möglicherweise einen Wrapper um die bestehenden DefaultBinder zu schaffen und verhindern, dass es Instanziieren des EntityCollection wieder, die bereits von EF erfolgt. Dann Bindung, die Sammlung an die Form Werte (Formcollection).

Beachten Sie, wenn Sie mehr als eine Sammlung sind verbindlich, müssen Sie den Fehler für jede Sammlung entfernen.

In meinem Experiment wird die Sammlung erfolgreich sowie das Stammobjekt in der Grafik, die die Sammlung war Teil gespeichert.

Hoffnung, dass jemand anderes hilft.

Ich hatte das gleiche Problem! Am Ende werden Sie feststellen, dass der Rahmen nicht komplexe Modelle verarbeiten kann.

Ich schrieb eine wenig Bindungskomponente, die die komplexen Bindungen an einem Pfosten initialisieren kann.

Aber im Grunde, was Sie tun müssen, ist das, was Arnis L. sagt.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top