2009-05-20 15 views
8

Tengo una aplicación ASP.Net MVC con un modelo de varias capas que contiene una colección.ASP.Net MVC - modelo con colección que no se encuentra en la postback

Creo que la vista para crear los objetos está configurada correctamente, pero simplemente no llena la colección dentro del modelo cuando publico el formulario en el servidor.

I tienen un fragmento de datos que se encuentra en la jerarquía de clases de este modo:

person.PersonDetails.ContactInformation[0].Data; 

Esta estructura de clases es creado por LinqToSql, y ContactInformation es de tipo EntitySet<ContactData>. Para crear la vista que paso por el siguiente:

return View(person); 

y dentro de la vista I tener una forma que contiene un único cuadro de texto con un nombre asociado con el campo mencionado anteriormente:

<%= Html.TextBox("person.PersonDetails.ContactInformation[0].Data")%> 

el método POST dentro de mi controlador es entonces el siguiente:

[AcceptVerbs(HttpVerbs.Post)] 
public ActionResult Create (Person person) 
{ 
    //Do stuff to validate and add to the database 
} 

es en este punto donde me pierdo como person.PersonDetails.ContactInformation.Count() == 0. Entonces, ModelBinder creó un objeto ContactInformation pero no lo llenó con el objeto que debería contener (es decir, ContactData) en el índice 0.

Mi pregunta es doble: 1. He tomado el enfoque correcto ... es decir, debería ¿este trabajo? 2. ¿Alguna idea de por qué podría fallar el llenado del objeto ContactInformation?

Muchas gracias, Richard

Respuesta

20

Creo que su modelo es demasiado complejo para el modelo por defecto para trabajar con ligante. Usted podría tratar de usar múltiples parámetros y atándolos con prefijos:

public ActionResult Create( 
    Person person, 
    [Bind(Prefix="Person.PersonDetails")] 
    PersonDetails details, 
    [Bind(Prefix="Person.PersonDetails.ContactInformation")] 
    ContactInformation[] info) 
{ 
     person.PersonDetails = details; 
     person.PersonDetails.ContactInformation = info; 

     ... 
} 

O usted podría desarrollar su propio modelo personalizado ligante que entender cómo derivar su modelo complejo a partir de las entradas de formulario.

+1

Gracias, eso funciona a la perfección. He probado un ejemplo con una jerarquía mucho más profunda que funcionó perfectamente, pero probablemente tengas razón de que se está perdiendo en la complejidad. Sin embargo, dos comentarios, para mayor claridad: 1. Solo necesitaba la [Bind (Prefix = "Person.PersonDetails.ContactInformation"]] ContactInformation [] info) y la configuración correspondiente del objeto del modelo. 2. ContactInformation [] necesitaba ser EntitySet para corresponderse con el tipo correcto. Ahora para buscar más detalles sobre el prefijo ... Gracias por su ayuda. Richard – Richbits

+0

Bien, todavía no había leído sobre 'prefijos'. – Ropstah

+0

bueno. O podría desarrollar su propio encuadernador de modelo personalizado que comprendería cómo derivar su modelo complejo de las entradas de formulario, ¿puede compartir el enlace o impl. si ya lo has hecho? –

0

Tal vez la falta de atributo BIND es el caso:

[AcceptVerbs(HttpVerbs.Post)] 
public ActionResult Create ([Bind] Person person) 
{ 
// Do stuff to validate and add to the database 
} 
+0

Gracias, lo he probado sin suerte. El hecho de que sea vinculante para la persona y la creación de objetos para el objeto ContactInformation me indica que es vinculante, pero no para el nivel ContactData. – Richbits

+0

No, Bind solo es necesario en un argumento si necesita cambiar las reglas de enlace predeterminadas. –

0

El primer argumento de Html.TextBox es el nombre de la caja de texto, el segundo sería el valor.

"incorrecto":

<%= Html.TextBox("person.PersonDetails.ContactInformation[0].Data")%> 

"derecha":

<%= Html.TextBox("nameoftextbox", person.PersonDetails.ContactInformation[0].Data)%> 
+2

Gracias, pero tengo entendido que el segundo parámetro es el valor utilizado para rellenar el cuadro de texto en, p. Ej. escenario de edición. El primer valor es utilizado por defaultmodelbinder para vincular el formulario a un objeto creado al publicar de nuevo. – Richbits

+0

Mi mal ........... – Ropstah

5

Si una propiedad es nulo, entonces el modelo ligante otro no podría encontrar o no podría encontrar valores en el formulario enviado necesario hacer una instancia del tipo de la propiedad. Por ejemplo, si la propiedad tiene un ID que no admite nulos y su formulario no contiene ningún dato para ese ID, el archivador del modelo dejará la propiedad como nula ya que no puede hacer una nueva instancia del tipo sin conocer el ID.

En otras palabras, para diagnosticar este problema, debe comparar cuidadosamente los datos en el formulario enviado (esto es fácil de ver con Firebug o Fiddler) con la estructura del objeto que espera que se llene la carpeta del modelo. Si falta algún campo obligatorio, o si los valores se envían de tal manera que no se pueden convertir al tipo de campo requerido, entonces el objeto completo se dejará nulo.

+2

Esto suena bien. –

+0

Gracias. Sin duda, si lo que dice aquí es correcto, ¿la solución de tvanfossen también fallará? – Richbits

+0

No necesariamente.Describo un posible problema. Él describe a otro. –

1

He estado luchando con este mismo tipo de escenario y con el tiempo se dio cuenta de que el problema de fondo es que no parece que el aglutinante de modelo por defecto MVC para trabajar en EntitySet <T> campos, sólo Lista <T> campos. Sin embargo, encontré una solución simple que parece aceptable. En mi caso, tengo una entidad de la Compañía que tiene una relación de uno a muchos con Contactos (mi Linq-to-Sql EntitySet).

ya que parece que cuando cambio mi código de EntitySet <Contacto> a la lista <Contacto>, el aglutinante modelo por defecto MVC comienza a funcionar como se esperaba (aunque el LTS no es ahora), pensé que podría proporcionar una alternativa, propiedad "aliased" a MVC que es del tipo List <Contact>, y por supuesto, esto parece funcionar.

En mi clase de entidad de la empresa:


// This is what LINQ-to-SQL will use: 
private EntitySet<Contact> _Contacts = new EntitySet<Contact>(); 
[Association(Storage="_Contacts", OtherKey="CompanyID", ThisKey="ID")] 
public EntitySet<Contact> Contacts 
{ 
    get { return _Contacts; } 
    set { _Contacts.Assign(value); } 
} 

// This is what MVC default model binder (and my View) will use: 
public List<Contact> MvcContacts 
{ 
    get { return _Contacts.ToList<Contact>(); } 
    set { _Contacts.AddRange(value); } 
} 

Así que ahora, en mi opinión, que tienen la siguiente:

 
<label>First Name* 
    <%= Html.TextBox("Company.MvcContacts[" + i + "].FirstName") %> 
</label> 
<label>Last Name* 
    <%= Html.TextBox("Company.MvcContacts[" + i + "].LastName") %> 
</label> 

parece funcionar como un encanto!

¡La mejor de las suertes! -Mike

+0

Gracias, lo siento ha estado fuera por un tiempo. Espero que hayas visto la respuesta a esto en MSDN (supongo que fuiste tú quien comentó allí). También registré un error en el sitio de conexión, y se ha confirmado que se corrigió en .Net 4.0. En mi caso, creo que su solución se vuelve demasiado complicada ya que el conjunto de entidades es profundo dentro de mi estructura de clase. También encontré que la edición de EntitySets es un problema real ya que se crea una nueva fila db en lugar de una actualización. Afortunadamente esto se resolverá también para 4.0. – Richbits

+0

Es bueno escuchar que esto se abordará. Y sí, publiqué esta misma solución para el grupo de noticias de microsoft prácticamente al mismo tiempo que hice aquí: ¡hasta ahora no me había dado cuenta de que tú eres el que comenzó! Obtuviste una respuesta de Allen que muestra cómo arreglar el generador de la propiedad que parece más ideal, pero en mi caso estoy usando entidades generadas por LTS y no estoy seguro de cómo solucionarlo fácilmente (sin por supuesto más soluciones gigantes). P.S., el otro hilo está aquí: http://groups.google.com/group/microsoft.public.dotnet.framework.aspnet/browse_thread/thread/a2869970f33c712a/188c0a9e5c00dc2a – Funka

0

Asegúrate de que tus modelos (y todos los modelos anidados) estén usando propiedades (getters/setters) en lugar de campos. Aparentemente, el encuadernador predeterminado necesita propiedades para funcionar correctamente. Tuve una situación muy similar que se solucionó al cambiar los campos necesarios por las propiedades.

Cuestiones relacionadas