2011-12-20 21 views
6

¿Cuál es el mejor método para conservar los resultados de una publicación de formulario (modelo de vista) en la página de resultados de búsqueda?ASP.NET MVC RouteValueDictionary y objeto complejo

Tengo un formulario de búsqueda que contiene casillas de verificación. Esta forma es construir utilizando un modelo de vista como la

public class SearchViewModel 
{ 
    public string Name { get; set; } 
    public string[] Colors { get; set; } 
} 

Cuando este modelo de vista que se envía de vuelta utilizo los valores para construir una consulta (utilizando EF). Los resultados se convierten en una PagedList.

public class SearchController : Controller 
    { 
    public ActionResult Index() 
    { 
     //this displays the search form. 
     return View(); 
    } 

    public ActionResult Results(string game, SearchViewModel vm) 
    { 
     //this displays the results 
     ViewBag.SearchViewModel = vm; 
     var matches = _repository.AsQueryable() 
      .ColorOr(vm.Colors) 
      .WhereIf(vm.Name.IsNotEmpty(), x => x.Name.Contains(vm.Name.Trim())); 

      return View(matches.ToPagedList(1, 10)); 
    } 
} 

Ahora que los resultados se muestran quisiera utilizar Html.PagedListPager y RouteValueDictionary para crear paginación.

@Html.PagedListPager((IPagedList)Model, page => Url.Action("Results", new RouteValueDictionary(ViewBag.SearchViewModel))) 

Sin embargo; la URL creada se ve así:

http://localhost:5139/search?Name=test&Colors=System.String[]&PageIndex=0 

Los valores para los colores termina siendo no el tipo de los valores. Tenía la esperanza de la URL se parece más a:

http://localhost:5139/search?Name=test&Colors=[Blue,Pink,Yellow]&PageIndex=0 
  1. ¿Cuál es el mejor método para conservar los resultados de un formulario de envío (vista del modelo) a través de la página de resultados de búsqueda?
  2. ¿Puede RouteValueDictionary soportar objetos complejos?
  3. Debo usar algo como unbinder
  4. ¿Sería mejor utilizar ViewData o Session?
+0

Wow. Esta es mi pregunta exacta. +1 –

Respuesta

2

Lo que he hecho para casos como este, que me parece simple, pero potente, se serializa mi objeto vista del modelo a JSON (en su caso SearchViewModel), usando algo como NewtonSoft JSON.net luego con la cadena JSON resultante, Haga una simple compresión de la cadena a través de la clase zlib.netZlib.DeflateStream (también podría usar algo como AES Rijndael, pero sin duda será más lento y desea velocidad en primer lugar) y luego pasar la cadena Base64 resultante a través de su QueryString.

Luego, cuando esté listo para usarlo nuevamente (es efectivamente un estado de vista), simplemente descomprima la cadena JSON y deserialícela de JSON en el objeto .NET correspondiente (nuevamente en su caso SearchViewModel).

Ha funcionado para mí, y no se termina con una URL inmanejable o cualquier impacto de rendimiento medible real que he visto con solo un puñado de campos de formulario que se serializan.

Elaboraré con un código de muestra pronto.

ACTUALIZACIÓN: Ejemplos de código ...

Esto es lo que yo haría en su escenario particular:

En Results(string, SearchViewModel) acción:

public ActionResult Results(string encryptedUrlViewModel, string game, SearchViewModel vm) 
{ 
    SearchViewModel searchUrlViewModel = null; 
    if (!string.IsNullOrEmpty(searchUrl)) { 
     // only first submission, no url view model set yet, so compress it and store.. 
     encryptedUrlViewModel = Convert.ToBase64String(
     DeflateStream.CompressString(JsonConvert.SerializeObject(vm))); 
     ViewBag.EncryptedUrlViewModel = encryptedUrlViewModel; 
    } 
    else { 
     var jsonUrlViewModel = DeflateStream.UncompressString(Convert.FromBase64String(encryptedUrlViewModel)); 
     searchUrlViewModel = JsonConvert.DeserializeObject(jsonUrlViewModel, typeof(SearchViewModel)) as SearchViewModel; 
     // at this point you should have a serialized 'SearchViewModel' object 
     // ready to use which you can then tweak your query below with. 
    } 
    var matches = _repository.AsQueryable() 
     .ColorOr(vm.Colors) 
     .WhereIf(vm.Name.IsNotEmpty(), x => x.Name.Contains(vm.Name.Trim())); 

    return View(matches.ToPagedList(1, 10)); 
} 

En vista:

@Html.PagedListPager((IPagedList)Model, page => Url.Action("Results", new { encryptedUrlViewModel = ViewBag.EncryptedUrlViewModel })) 

El código puede requerir algunos ajustes, no probado en su escenario, pero será algo así, la mejor de las suertes :)

Debería considerar, sin embargo, que si desea llevar a cabo la solicitud del usuario en la URL en buscapersonas, entonces uno consideraría por qué el formulario no se realizó como una solicitud GET en lugar de una solicitud POST en primer lugar. ¿Alguna razón por la que particularmente la quería POST? Creo que GET llevará su matriz Colors correctamente, pero asegúrese de que su modelo de vista esté configurado correctamente. See this Haacked article for model binding to lists.

+0

Solo para ser claro. La solución más simple, como se indicó anteriormente, fue cambiar el método de formulario de POST a GET. – detroitpro

+1

Haha .... doh. Oh mi solución codificada a mano para desperdiciar. Oh, bueno :) Tal vez necesites algo así un día ... =) – GONeale

+0

entrando en esto un poco tarde, pero enfrentando un desafío similar. Entonces, ¿envolvería todo el formulario de búsqueda + lista de resultados en el formulario Html para obtener el modelo correcto? ¿Es la paginación parte del modelo de vista de búsqueda si toda la vista tiene el formato o es un argumento separado para la acción del controlador? –

1

Tuve el mismo problema pero con los parámetros de búsqueda. Teníamos un parámetro de color que era una lista de nombres de colores que el motor de búsqueda estaba usando. Por lo tanto, puede marcar el negro y el azul, y el resultado puede incluir tanto productos negros como azules.

Terminé usando Unbound.

using Unbound; 
Unbinder u = new Unbinder(); 

@Url.Action("Index", new RouteValueDictionary(u.Unbind(SearchParams))) 

lo que resulta en un enlace como:

/MyRoute?color[0]=black&color[1]=blue