Question

Je vais avoir des problèmes de fixation sur un formulaire avec plusieurs modèles soumis. J'ai un formulaire de plainte qui comporte des informations de plainte ainsi que l'un-à-plusieurs plaignants. Je suis en train de soumettre le formulaire, mais je reçois des erreurs sur la liaison. ModelState.IsValid renvoie toujours false.

Si debugger et afficher les erreurs de ModelState, je reçois un dicton: « Le EntityCollection a déjà été initialisé. La méthode InitializeRelatedCollection ne doit être appelée pour initialiser une nouvelle EntityCollection lors de la désérialisation d'un graphe d'objet ».

En outre, lors du débogage, je peux voir que le modèle de plainte ne soit rempli avec les plaignants de la soumission du formulaire, il semble qu'une partie travaille.

Je ne sais pas si ce que je fais est pas possible avec le ModelBinder par défaut, ou si je suis tout simplement pas aller à ce sujet dans le bon sens. Je ne peux pas sembler trouver des exemples concrets ou de la documentation à ce sujet. Un problème similaire se trouve sur stackoverflow mais il ne semble pas traiter avec des vues fortement typées.

Code contrôleur:

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

Afficher le code (Ceci est une vue partielle qui est appelé par Créer / Modifier vues, qui sont fortement typés avec plainte):

<%@ 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>
Était-ce utile?

La solution

guess aveugle:

Changement:

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

avec:

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

Respectivement - ajouter "plainte." avant "Plaignants [..."

EDIT :

Ce n'est pas une bonne réponse. A laissé juste parce que annulation de la suppression pourrait ajouter une certaine valeur jusqu'à ce que pops réponse correcte vers le haut.

EDIT2:

Je peux me tromper, mais pour moi, il semble qu'il y ait un problème de cadre entité (ou - avec la façon dont vous l'utilisez). Je veux dire - asp.net mvc parvient à lire les valeurs de demande, mais ne peut pas initialiser la collecte des plaignants.

il est écrit:

  

La méthode InitializeRelatedCollection (TTargetEntity) initialise une EntityCollection existante (TEntity) qui a été créé en utilisant le constructeur par défaut. Le EntityCollection (TEntity) est initialisé à l'aide des relations fournis et noms de rôle cibles.

     

La méthode InitializeRelatedCollection (TTargetEntity) est utilisé au cours de désérialisation seulement.

Un peu plus d'info:

  

Exception:

     
      
  • InvalidOperationException
  •   
     

Conditions:

     
      
  • Quand est déjà initialisé la condition EntityCollection (TEntity).
  •   
  • Lorsque le gestionnaire de relation est déjà attaché à un ObjectContext.
  •   
  • Lorsque le gestionnaire de relation contient déjà une relation avec ce nom et le rôle cible.
  •   

InitializeRelatedCollection est congédié Pour certaines raisons à deux reprises. Serve - je suis pas d'idées brillantes exactement pourquoi. Peut-être que cette petite enquête aidera pour quelqu'un d'autre - plus expérimenté avec EF. :)

EDIT3: Ce n'est pas une solution à ce problème particulier, plus comme une solution de contournement, une bonne façon de traiter une partie modèle de la lutte antivectorielle.

Créer un viewmodel à des fins de présentation seulement. Créer un nouveau modèle de domaine de Poços pur aussi (parce que EF leur apportera son soutien dans la prochaine version seulement). Utilisez AutoMapper Carte EFDataContext Modèle <=> ViewModel <=>.

Cela prendra un certain effort, mais comment il doit être manipulé. Cette approche supprime la responsabilité de la présentation de votre modèle, nettoie votre modèle de domaine (enlève des choses EF de votre modèle) et permettrait de résoudre votre problème avec la liaison.

Autres conseils

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

}

Cela fonctionne pour moi!

Je pense que lorsque l'objet de la plainte est créé alors sa collection « plaignants » s'initialisée (à cause de la logique automatique cadre de l'entité) et liant modèle tente de créer la collection elle-même aussi bien, ce qui provoque l'erreur. Mais quand nous essayons de mettre à jour le modèle puis manuellement collection est déjà initialisé, mais liant modèle n'est pas demandé de l'initialiser à nouveau.

Pour que cela fonctionne sans cas par cas, vous devez créer solutions de contournement de votre propre modèle de liaison et la méthode de remplacement 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); 
            }
        } 
        }
    }
}

Ne pas oublier d'enregistrer votre classeur dans Global.asax:

ModelBinders.Binders.DefaultBinder = new MyDefaultModelBinder();

Je CONTOURNÉS l'exception ModelBinding en procédant comme suit:

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

Le principal inconvénient est que l'exception est toujours lancée et qui peut être coûteux à l'exécution. Le travail élégant autour peut être de créer une enveloppe autour de la DefaultBinder existante et l'empêcher de l'instanciation EntityCollection à nouveau, ce qui est déjà fait par EF. Ensuite, liant cette collection aux valeurs de forme (FormCollection).

Gardez à l'esprit si vous lier plus d'une collection, vous devrez supprimer l'erreur pour chaque collection.

Dans mon expérience, la collection sauvé avec succès ainsi que l'objet racine dans le graphique de la collection qui faisait partie.

L'espoir qui aide quelqu'un d'autre.

J'ai eu le même problème! En fin de compte, vous trouverez que le cadre ne peut pas gérer des modèles complexes.

J'ai écrit un petit élément de liaison qui peut initialiser les liaisons complexes sur un poteau.

Mais au fond ce que vous avez à faire est ce que Arnis L. est dit.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top