2011-08-30 19 views
7

Recientemente me encontré con un problema al implementar una solución JSON para un cliente. Si el nombre del parámetro de acción coincide con un nombre de propiedad del modelo, el enlace no puede enlazar CUALQUIER propiedad.MVC3 Json Bind Bug?

Un ejemplo:

Controlador personas

public ActionResult SetEmails(Person emails){ 
    ... 
    return Content(""); 
} 

Debido a que el nombre del parámetro se denomina "e-mails" y que coincide con un nombre de propiedad del modelo de persona llamada "correos electrónicos" .. el ligante falla, pero no da ninguna indicación en cuanto a por qué ..

tenían un modelo llamado Persona

public class Person { 
    public string Name { get; set; } 
    public List<string> Emails { get; set; } 

    public Person() { 
     Emails = new List<string>(); 
    } 
} 

¿Es esto un error o una 'característica'?

Como una nota al margen, mi pregunta es más acerca de cómo un nombre de argumento podría causar un conflicto a una carpeta? El nombre no debería importar, ya que es el tipo de clase que define su esquema para coincidir con los datos json. ¿Por qué debería importarle al encuadernador el nombre del argumento o si coincide con un nombre de propiedad dentro del tipo de clase del argumento en sí?

+4

¿puede usted publicar el código js llamante, o al menos el JSON que está enviando? –

+0

json sería: {"Nombre": "Juan", "Correos electrónicos": ["[email protected]", "test2 @ test".com "]} El json estaba bien, como indiqué, una vez que cambié el nombre del argumento Acciones de los correos electrónicos (coincide con el nombre de la propiedad de los modelos) a cualquier cosa que no coincidía, funcionó ... pensé que era interesante y no puedo ver por qué esto no debería funcionar, o al menos arrojar una excepción en lugar de una clase instanciada con valores de propiedad nulos!? – Mike

Respuesta

2

El problema es que tiene una ambigüedad en el diccionario, con dos elementos denominados "correos electrónicos" (aunque con una carcasa diferente). DefaultModelBinder no puede resolver esta ambigüedad.

Dos posibles soluciones: (1) si está transfiriendo un modelo de persona completa, cambie el nombre de su elemento de nivel superior a "persona" (que tiene más sentido, dado el contexto), lo que eliminaría la ambigüedad y permita que el enlace se realice como se espera, o (2) si solo está devolviendo una lista de correos electrónicos, cambie su firma de acción al public ActionResult SetEmails(List<Emails> emails).

No llamaría a lo que ha experimentado, ya sea un error o una característica, sino un problema con su diseño.

+0

Lo cambié a persona, lo que resolvió el problema. El nombre no es un requisito, Solo estaba adoptando el código heredado y mi último pensamiento sobre por qué fallaría el encuadernador modelo fue que el nombre del argumento de Acción coincidía con un nombre de propiedad del tipo de clase de argumentos. ¿No entiendo por qué esto causaría correos electrónicos de Ambigüedad? .GetType(). GetProperties(), etc. los unen contra el json entrante ... si un tipo de clase de argumentos (esquema supongo) coincide con el de json, genial ... ¿qué significa el argumento nam? e importa? ¿Cómo entra el nombre en juego durante el enlace? ¡Su nombre no dicta su clase! – Mike

+0

JSON está vinculado a través del 'DictionaryValueProvider', por lo que los valores están asociados a través de nombres de clave. Si hay dos claves del mismo nombre, el Diccionario no será válido. Marque como respondido si esto fue útil. – counsellorben

+1

Y esas claves deberían provenir de las propiedades de las clases, no del nombre que le dio al argumento que se utiliza para vincular su modelo. ¿Cómo tiene relevancia el nombre del parámetro al iterar sobre sus propiedades? var pi = controller.GetMethod ("acción"). GetParameters(); ¿Cómo se necesita el nombre del parámetro o cómo influye en el resultado de recuperar los parámetros? Entonces, puede obtener su tipo de clase e iterar sobre sus propiedades para vincular los datos. No se trata de dos propiedades con el mismo nombre (inválidas y no compiladas) .. – Mike

0

No estoy diciendo que como esto por una respuesta, pero puede evitar el cambio de su nombre de propiedad.

Envuelva su objeto JSON con el nombre de la variable del parámetro (en este caso, es "correos electrónicos"). Por lo que este funciona:

de datos: JSON.stringify ({correos electrónicos: {name: "mi nombre", correos electrónicos: [ "[email protected]", "[email protected]"]}}) ;

pero esto no lo hace - los rendimientos modelbinder nulo en lugar:

datos: JSON.stringify ({nombre: "mi nombre", correos electrónicos: [ "[email protected]", " [email protected] "]});

Aún así, esto es bastante malo. Sin embargo, las cosas funcionan en profundidad, no hay razón para que mi cliente web tenga que saber cuál es el nombre del parámetro en el controlador para poder funcionar correctamente. En lo que a mí respecta, es un error en el encuadernador de MVC.