2009-07-03 21 views
11

Estoy usando jQuery para hacer una llamada Ajax usando una publicación Http en ASP.NET MVC. Me gustaría poder aprobar un Diccionario de valores.¿Cómo paso un diccionario como parámetro a un método ActionResult de jQuery/Ajax?

Lo más parecido que podía pensar era pasar una matriz multidimensional de cadenas, pero el resultado que realmente pasa al método ActionResult es una matriz de cadenas dimensionales única que contiene una concatenación de cadenas de la "clave/valor" " par.

Por ejemplo, el primer elemento en el siguiente array "valores" contiene el valor a continuación:

"id,200" 

He aquí un ejemplo de mi método ActionResult:

public ActionResult AddItems(string[] values) 
{ 
    // do something 
} 

Aquí hay un ejemplo de cómo' m llamando al método desde jQuery:

$.post("/Controller/AddItems", 
    { 
     values: [ 
      ["id", "200"], 
      ["FirstName", "Chris"], 
      ["DynamicItem1", "Some Value"], 
      ["DynamicItem2", "Some Other Value"] 
     ] 
    }, 
    function(data) { }, 
    "json"); 

¿Alguien sabe cómo pasar un diccionario o ¿Qué ocurre con jQuery al método ActionResult en lugar de una matriz?

Realmente me gustaría definir mi ActionResult así:

public ActionResult AddItems(Dictionary<string, object> values) 
{ 
    // do something 
} 

¿Alguna sugerencia?

ACTUALIZACIÓN: Intenté pasar una coma dentro del valor y básicamente hace que sea imposible analizar realmente el par de clave/valor mediante el análisis de cadenas.

Pass esto:

values: [ 
    ["id", "200,300"], 
    ["FirstName", "Chris"] 
] 

resultados en esta:

values[0] = "id,200,300"; 
values[1] = "FirstName,Chris"; 
+0

No creo que hay una manera de hacerlo that.I puede estar equivocado, pero va a ser trivial para analizar los datos que se pasan en una string array, y crea el diccionario tú mismo dentro del método AddItems. –

+0

No estoy seguro de qué problemas de análisis causarían las comas dentro de los valores. –

+0

Por fin me di cuenta, gracias a todos los que hicieron sugerencias! Agregué mi solución final como respuesta a continuación. Lo marcaré como la Respuesta correcta tan pronto como SO me lo permita. ¡Gracias a todos! –

Respuesta

11

Por fin lo he descubierto !! ¡Gracias a todos por las sugerencias! Finalmente descubrí que la mejor solución es pasar JSON a través de Http Post y usar un ModelBinder personalizado para convertir el JSON en un diccionario. Una cosa que hice en mi solución es crear un objeto JsonDictionary que herede Dictionary para poder adjuntar el ModelBinder personalizado al tipo JsonDictionary, y no causará ningún conflicto en el futuro si utilizo el diccionario como un parámetro ActionResult más adelante para un propósito diferente de JSON.

Aquí está el método ActionResult definitiva:

public ActionResult AddItems([Bind(Include="values")] JsonDictionary values) 
{ 
    // do something 
} 

Y el jQuery "$ .post" llamada:

$.post("/Controller/AddItems", 
{ 
    values: Sys.Serialization.JavaScriptSerializer.serialize(
      { 
       id: 200, 
       "name": "Chris" 
      } 
     ) 
}, 
function(data) { }, 
"json"); 

Entonces el JsonDictionaryModelBinder necesita ser registrado, añadí esto con el método Application_Start dentro Global.asax.cs:

protected void Application_Start() 
{ 
    ModelBinders.Binders.Add(typeof(JsonDictionary), new JsonDictionaryModelBinder()); 
} 

Y, finalmente, aquí está el JsonDictionaryMode lBinder objeto y el objeto JsonDictionary que crean:

public class JsonDictionary : Dictionary<string, object> 
{ 
    public JsonDictionary() { } 

    public void Add(JsonDictionary jsonDictionary) 
    { 
     if (jsonDictionary != null) 
     { 
      foreach (var k in jsonDictionary.Keys) 
      { 
       this.Add(k, jsonDictionary[k]); 
      } 
     } 
    } 
} 

public class JsonDictionaryModelBinder : IModelBinder 
{ 
    #region IModelBinder Members 

    public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) 
    { 
     if (bindingContext.Model == null) { bindingContext.Model = new JsonDictionary(); } 
     var model = bindingContext.Model as JsonDictionary; 

     if (bindingContext.ModelType == typeof(JsonDictionary)) 
     { 
      // Deserialize each form/querystring item specified in the "includeProperties" 
      // parameter that was passed to the "UpdateModel" method call 

      // Check/Add Form Collection 
      this.addRequestValues(
       model, 
       controllerContext.RequestContext.HttpContext.Request.Form, 
       controllerContext, bindingContext); 

      // Check/Add QueryString Collection 
      this.addRequestValues(
       model, 
       controllerContext.RequestContext.HttpContext.Request.QueryString, 
       controllerContext, bindingContext); 
     } 

     return model; 
    } 

    #endregion 

    private void addRequestValues(JsonDictionary model, NameValueCollection nameValueCollection, ControllerContext controllerContext, ModelBindingContext bindingContext) 
    { 
     foreach (string key in nameValueCollection.Keys) 
     { 
      if (bindingContext.PropertyFilter(key)) 
      { 
       var jsonText = nameValueCollection[key]; 
       var newModel = deserializeJson(jsonText); 
       // Add the new JSON key/value pairs to the Model 
       model.Add(newModel); 
      } 
     } 
    } 

    private JsonDictionary deserializeJson(string json) 
    { 
     // Must Reference "System.Web.Extensions" in order to use the JavaScriptSerializer 
     var serializer = new System.Web.Script.Serialization.JavaScriptSerializer(); 
     return serializer.Deserialize<JsonDictionary>(json); 
    } 
} 
1

Es posible con enlazadores de modelos personalizados o filtros. Entre bastidores: tendrás que hacerlo manualmente de todos modos (Request.Form, analizar cadenas, crear diccionario tralala), pero al menos: tu controlador estará limpio y el código será reutilizable para otras acciones.

1

No creo que sea posible pasar un diccionario de jQuery/Ajax a un método ActionResult a través de una publicación de Http. Una cosa que descubrí que parece ser la más fácil de trabajar es pasar un objeto JSON y luego analizarlo en un diccionario.

Aquí está la versión modificada de la llamada anterior "$ .post" de jQuery que envía JSON como un pseudo-diccionario:

$.post("/Controller/AddItems", 
    { 
     values: Sys.Serialization.JavaScriptSerializer.serialize(
       { 
        id: 200, 
        "name": "Chris" 
       } 
      ) 
    }, 
    function(data) { }, 
    "json"); 

La función "Sys.Serialization.JavaScriptSerializer.serialize" es un método de la biblioteca ASP.NET AJAX JavaScript.

Aquí está la versión modificada del método ActionResult arriba:

public ActionResult AddItems(Dictionary<string, object> values) 
{ 
    // Must Reference "System.Web.Extensions" in order to use the JavaScriptSerializer 
    var json = new System.Web.Script.Serialization.JavaScriptSerializer(); 
    var data = json.Deserialize<Dictionary<string, string>>(routeValues); 

    // do something 
} 

Creo que esto hace que sea mucho más fácil de probar la unidad al pasar JSON, en lugar de utilizar la colección de formularios para enviar/recuperar la colección de clave/pares de valoresAdemás, es más fácil trabajar que descubrir cómo crear un IModelBinder personalizado, y un IModelBinder personalizado puede causar problemas con otros métodos ActionResult cuando este es el único que necesito para hacer esto.

+0

Chris, mira mi comentario anterior. Sigo pensando que vas a hacer esto de la manera difícil. No estoy 100% seguro, pero tengo la sensación de que lo eres. – griegs

0

DefaultModelBinder puede enlazar su POST a una matriz o diccionario. Por ejemplo:

para las matrices:

public ActionResult AddItems(string[] values) 

$.post("/Controller/AddItems", { values: "values[0]=200&values[1]=300" }, 
    function(data) { }, "json"); 

o:

$.post("/Controller/AddItems", { values: "values=200&values=300" }, 
    function(data) { }, "json"); 

para los diccionarios:

public ActionResult AddItems(Dictionary<string, object> values) 

$.post("/Controller/AddItems", { 
    values: "values[0].Key=value0&values[0].Value=200&values[1].Key=value1&values[1].Value=300" }, function(data) { }, "json"); 

Actualizado:

Si sus valores están en las entradas HTML a continuación, en jQuery se puede hacer algo como esto:

var postData = $('input#id1, input#id2, ..., input#idN").serialize(); 
// or 
var postData = $('input.classOfYourInputs").serialize(); 

$.post("/Controller/AddItems", { values: postData }, function(data) { }, "json"); 

Actualizado:

También marque esta: Scott Hanselman's ComputerZen.com - ASP.NET Wire Format for Model Binding to Arrays, Lists, Collections, Dictionaries

+0

Probé su ejemplo de diccionario, pero no termina llenando el parámetro "valores"; simplemente termina siendo un diccionario vacío. –

+0

¿Ha intentado Dictionary values ​​en lugar de Dictionary . También verifique si su índice de valores [n] es cero e ininterrumpido. Compruebe http://stackoverflow.com/questions/1031416/asp-net-mvc-model-binding-a-set-of-dynamically-generated-checkboxes-how-to/1031694#1031694 también –

0

Esta es una entrada antigua, pero no puedo evitar tener algunas observaciones de todos modos.

@ eu-ge-ne: "DefaultModelBinder puede vincular su POST a una matriz o diccionario". Es cierto, pero al menos para los diccionarios, encuentro que la notación de forma requerida es contraintuitiva.

@Chris: Ayer tuve exactamente el mismo problema al intentar publicar un diccionario JavaScript (JSON) en un método de acción de controlador. Desarrollé una carpeta de modelos personalizada totalmente diferente que procesa diccionarios genéricos con diferentes tipos de argumentos. Solo lo probé en MVC 3 y probablemente tenga la ventaja de un marco mejorado.

Para los detalles de mis experiencias y el código fuente de la carpeta de modelo personalizado, por favor ver mi post blog en http://buildingwebapps.blogspot.com/2012/01/passing-javascript-json-dictionary-to.html

2

Esto es lo que he intentado. Ahorra mucho trabajo Javascript:

var dict = {};  
     dict["id"] = "200"; 
     dict["FirstName"] = "Chris"; 
     dict["DynamicItem1"] = "Some Value"; 
     dict["DynamicItem2"] = "Some Other Value"; 

     var theObject = {}; 
     theObject.dict = dict; 
     $.post(URL, theObject, function (data, textStatus, XMLHttpRequest) { 
      console.log("success"); 
     }, "json"); 

Acción Método:

public ActionResult MethodName(DictionaryModel obj) 
    { 
     //Action method logic 
    } 

public class DictionaryModel 
{ 
    public Dictionary<string, string> dict { get; set; } 

} 
+0

No es exactamente lo que quería, porque no tiene sentido pasar un objeto, si pudiera pasar el diccionario, pero funciona bien. Upvoted. :) –

Cuestiones relacionadas