Pregunta

Tengo problemas de enlace en un formulario con múltiples modelos que se presentaron. Tengo un formulario de queja que incluye información sobre la queja, así como demandantes de uno a muchos. Estoy tratando de enviar el formulario, pero estoy recibiendo errores en el enlace. ModelState.IsValid siempre devuelve falso.

Si elimino errores y ver los errores ModelState, me sale uno diciendo: "El EntityCollection ya se ha inicializado. El método InitializeRelatedCollection sólo debe ser llamada para inicializar un nuevo EntityCollection durante la deserialización de un gráfico de objeto".

Además, al depurar, puedo ver que el modelo de la demanda no son rellenados con los denunciantes del envío del formulario, por lo que parece que una parte está trabajando.

No estoy seguro de si lo que estoy haciendo no es posible con la ModelBinder por defecto, o si estoy simplemente no va sobre la manera correcta. Parece que no puedo encontrar ningún ejemplo concreto o documentación sobre esto. Un problema muy similar se puede encontrar en stackoverflow aquí pero no parece que lidiar con vistas al establecimiento inflexible.

Código de control:

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

Ver código (Esta es una vista parcial que es llamada por crear / editar Vistas, que también están fuertemente tipado con queja):

<%@ 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>
¿Fue útil?

Solución

conjetura Ciegos:

Cambio:

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

por:

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

Respectivamente - añada "Queja". antes de "Los demandantes [..."

Editar

Esto no es una respuesta correcta. Dejó revirtió la eliminación simplemente porque eso podría añadir algo de valor hasta que la respuesta correcta aparece.

Edit2:

Puedo estar equivocado, pero para mí Parece que hay un problema con el marco de la entidad (o - con la forma en que lo utilice). Me refiero - asp.net mvc las arregla para leer los valores de petición, pero no puede inicializar colección demandantes.

aquí está escrito:

  

El método InitializeRelatedCollection (TTargetEntity) inicializa un EntityCollection existente (TEntity) que se creó utilizando el constructor predeterminado. El EntityCollection (TEntity) se inicializa mediante el uso de la relación y de destino nombres de función proporcionados.

     

El método InitializeRelatedCollection (TTargetEntity) se utiliza sólo durante la deserialización.

Algunos más información:

  

Excepción:

     
      
  • InvalidOperationException
  •   
     

Condiciones:

     
      
  • Cuando ya se inicializa el proporcionado EntityCollection (TEntity).
  •   
  • Cuando el gerente de relaciones ya está conectado a un ObjectContext.
  •   
  • Cuando el gerente de relaciones ya contiene una relación con este nombre y función objetivo.
  •   

Somewhy InitializeRelatedCollection es despedido dos veces. Por desgracia - no tengo ideas brillantes por qué exactamente. Tal vez esta pequeña investigación le ayudará a alguien más - más experimentado con EF. :)

Edit3:
Esto no es una solución para este problema en particular, más como una solución, una manera apropiada de manejar parte del modelo MVC.

Crear un modelo de vista sólo para fines de presentación. Crear un nuevo modelo de dominio de pura POCOs también (debido a EF les apoyará en la versión siguiente solamente). Utilice AutoMapper para asignar EFDataContext <=> Modelo <=> modelo de vista.

Eso sería tomar un poco de esfuerzo, pero así es como se debe manejar. Este enfoque elimina la responsabilidad de presentación de su modelo, limpia su modelo de dominio (EF quita cosas de su modelo) y resolvería su problema con la unión.

Otros consejos

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

}

Esto funciona para mí!

Creo que cuando el objeto de quejas se crea entonces su colección 'reclamantes se inicializa (debido a la lógica de auto marco de la entidad) y luego ligante modelo trata de crear la colección en sí, así, lo que provoca el error. Pero cuando tratamos de actualizar el modelo de forma manual y luego colección está ya inicializado pero no se le pide ligante modelo para inicializar de nuevo.

Para conseguir esto para trabajar sin el caso por caso temporales de los que necesita para crear su propio modelo de aglutinante y método de reemplazo 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); 
            }
        } 
        }
    }
}

No se olvide de registrar su aglutinante en Global.asax:

ModelBinders.Binders.DefaultBinder = new MyDefaultModelBinder();

He trabajado en torno a excepción ModelBinding haciendo lo siguiente:

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

El principal inconveniente es que la excepción todavía se lanza y que puede ser costoso en tiempo de ejecución. El trabajo en torno elegante puede ser la creación de una envoltura alrededor de la DefaultBinder existente y evitar que una instancia del EntityCollection nuevo, lo que ya se ha hecho por EF. Luego de unión que la recogida de los valores de la forma (FormCollection).

Tenga en cuenta que si estás vinculante más de una colección, usted tendrá que quitar el error para cada colección.

En mi experimento, la colección salvados éxito, así como el objeto raíz en el gráfico que la colección era parte de.

La esperanza que ayuda a otra persona.

tuve el mismo problema! Al final, usted se enterará de que el marco no puede manejar modelos complejos.

escribí un pequeño componente de unión que puede inicializar las fijaciones complejas en un poste.

Pero básicamente lo que tiene que hacer es lo que está diciendo L. Arnis.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top