Domanda

Ho problemi vincolanti su un modulo con l'essere presentate più modelli. Ho un modulo di reclamo che comprende informazioni denuncia, nonché uno-a-molti denuncianti. Sto cercando di inviare il modulo, ma sto ottenendo gli errori sul bind. ModelState.IsValid restituisce sempre false.

Se il debug e visualizzare gli errori ModelState, ottengo un detto: "L'EntityCollection è già stato inizializzato. Il metodo InitializeRelatedCollection deve essere chiamato solo per inizializzare un nuovo EntityCollection durante la deserializzazione di un oggetto grafico".

Inoltre, quando il debug, posso vedere che il modello della denuncia venga popolato con denuncianti dalla presentazione modulo, così sembra che parte sta lavorando.

Non sono sicuro se quello che sto facendo non è possibile con il ModelBinder di default, o se sto semplicemente non andando su di esso nel modo giusto. Io non riesco a trovare nessuna esempi concreti o la documentazione su questo. Un problema molto simile si trova su StackOverflow qui ma non sembra a che fare con una vista fortemente tipizzati.

Codice di controllo:

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

Visualizza codice (Questa è una vista parziale che viene chiamato da creare / modificare Vista, che sono anche fortemente tipizzati con Reclamo):

<%@ 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>
È stato utile?

Soluzione

indovinare cieco:

cambiamento:

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

con:

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

Rispettivamente - aggiungere "Reclamo". prima di "I denuncianti [..."

Modifica :

Questa non è una risposta giusta. Lasciato non eliminato solo perché questo potrebbe aggiungere un po 'di valore fino a quando la risposta corretta si apre.

EDIT2:

Potrei sbagliarmi, ma per me sembra che ci sia un problema con Entity Framework (o - con il modo in cui lo si utilizza). Voglio dire - asp.net mvc riesce a leggere i valori di richiesta, ma non è possibile inizializzare la raccolta denuncianti.

qui è scritto:

  

Il metodo InitializeRelatedCollection (TTargetEntity) inizializza un EntityCollection esistente (TEntity) che è stato creato utilizzando il costruttore predefinito. L'EntityCollection (TEntity) viene inizializzato utilizzando i nomi dei ruoli di relazione e di destinazione previsti.

     

Il metodo InitializeRelatedCollection (TTargetEntity) viene utilizzato solo durante deserializzazione.

Alcuni ulteriori informazioni:

  

Eccezione:

     
      
  • InvalidOperationException
  •   
     

Condizioni:

     
      
  • Quando la condizione EntityCollection (TEntity) è già inizializzato.
  •   
  • Quando il consulente è già collegato a un ObjectContext.
  •   
  • Quando il gestore rapporto contiene già un rapporto con questo nome e la destinazione di ruolo.
  •   

Somewhy InitializeRelatedCollection viene licenziato due volte. Purtroppo - ho ottenuto senza idee brillanti perché esattamente. Forse questa piccola indagine contribuirà per qualcun altro - più esperti con EF. :)

Edit3:
Questa non è una soluzione per questo problema particolare, più simile a una soluzione, un modo corretto di gestire una parte del modello di MVC.

Creare un ViewModel solo a scopo di presentazione. Creare un nuovo modello di dominio da puro pocos troppo (perché EF li sosterrà nella prossima versione solo). Utilizzare automapper per map EFDataContext <=> Modello <=> ViewModel.

Ciò richiederà un certo sforzo, ma questo è come dovrebbe essere gestita. Questo approccio elimina presentazione responsabilità dal modello, pulisce il modello di dominio (rimuove roba EF dal modello) e risolverebbe il problema con il legame.

Altri suggerimenti

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

}

Questo funziona per me!

Credo che quando oggetto di reclamo viene creato allora la sua collezione 'denuncianti viene inizializzata (a causa della logica automatica Entity Framework) e poi il modello legante tenta di creare la raccolta stessa pure, che causa l'errore. Ma quando cerchiamo di aggiornare il modello manualmente allora collezione è già inizializzato, ma modello di legante non viene richiesto per inizializzare di nuovo.

Per ottenere questo al lavoro senza caso per caso, soluzioni alternative è necessario creare il proprio modello di legante e metodo di sostituzione 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); 
            }
        } 
        }
    }
}

Non dimenticare di registrare il legante in Global.asax:

ModelBinders.Binders.DefaultBinder = new MyDefaultModelBinder();

Ho lavorato intorno eccezione ModelBinding nel modo seguente:

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

Lo svantaggio principale è che l'eccezione è ancora gettato e che può essere costoso in fase di esecuzione. L'elegante lavoro intorno potrebbe essere quella di creare un involucro attorno al DefaultBinder esistente e impedire che un'istanza di nuovo l'EntityCollection, che è già stato fatto da EF. Poi legame che insieme ai valori di modulo (FormCollection).

Tenere a mente se si sta vincolante più di una raccolta, è necessario rimuovere l'errore per ogni collezione.

Nel mio esperimento, la collezione salvato con successo così come l'oggetto principale nel grafico, che la collezione era parte.

La speranza che aiuta a qualcun altro.

ho avuto il problema identico! Alla fine si scoprirà che il quadro non è in grado di gestire modelli complessi.

ho scritto un po 'di componente vincolante in grado di inizializzare le associazioni complesse su un post.

Ma in fondo quello che devi fare è quello Arnis L. sta dicendo.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top