2012-05-25 21 views
46

Como experimentado desarrollador de ASP.Net que recientemente comencé a utilizar MVC, me cuesta un poco cambiar mi forma de pensar de un controlador de eventos y controlador de servidor tradicional. forma de hacer las cosas, en la forma de cosas MVC más dinámica. Creo que estoy llegando poco a poco, pero a veces la "magia" de MVC me saca de quicio.ASP.Net MVC y estado: cómo mantener el estado entre las solicitudes

Mi situación actual es crear una página web que permita al usuario buscar un archivo local, cargarlo en el servidor y repetirlo hasta que tenga una lista de archivos para trabajar. Cuando esté contento con la lista de archivos (que se mostrará en una cuadrícula en la página), hará clic en un botón para procesar los archivos y extraer algunos datos que se almacenarán en una base de datos.

La última parte no es tan importante, en este momento estoy luchando con algo tan trivial como crear una lista de archivos, y persistir esa lista entre las solicitudes. En el enfoque tradicional, esto sería extremadamente simple: los datos se conservarían en ViewState. Pero en MVC necesito pasar los datos entre el controlador y las vistas, y no entiendo completamente cómo se supone que funciona.

Creo que será mejor que publique mi intento bastante incompleto de codificar esto para explicar el problema.

Con el fin de mantener los datos de la lista de archivos, que han creado un modelo de vista que es básicamente una lista mecanografiada de archivos, junto con algunos metadatos adicionales:

public class ImportDataViewModel 
{ 
    public ImportDataViewModel() 
    { 
     Files = new List<ImportDataFile>(); 
    } 

    public List<ImportDataFile> Files { get; set; } 
... 

En la vista, tengo una forma para la navegación y cargar el archivo:

<form action="AddImportFile" method="post" enctype="multipart/form-data"> 
    <label for="file"> 
    Filename:</label> 
    <input type="file" name="file" id="file" /> 
    <input type="submit" /> 
    </form> 

la vista está utilizando el modelo de vista como modelo:

@model MHP.ViewModels.ImportDataViewModel 

Esto enviará el archivo a mi acción:

public ActionResult AddImportFile(HttpPostedFileBase file, ImportDataViewModel importData) 
    { 

     if (file.ContentLength > 0) 
     { 
      ImportDataFile idFile = new ImportDataFile { File = file }; 
      importData.Files.Add(idFile); 
     } 

     return View("DataImport", importData); 
    } 

Esta acción devuelve la vista de la página DataImport junto con la instancia de modelo de vista que contiene la lista de archivos.

Esto funciona muy bien hasta cierto punto, puedo buscar un archivo y subirlo, y puedo ver los datos del modelo de vista dentro de la acción, y luego también si pongo un punto de interrupción dentro de la vista y depuro "this.Model ", todo esta bien.

Pero luego, si intento cargar otro archivo, al poner un punto de interrupción dentro de la acción AddImportFile, el parámetro importData está vacío. Entonces la vista obviamente no está pasando la instancia actual de su modelo a la acción.

En los ejemplos de MVC que he visto, la instancia del modelo se transfiere "mágicamente" al método de acción como parámetro, entonces ¿por qué está vacía ahora?

Supongo que el verdadero problema es mi comprensión limitada de MVC, y que probablemente exista una solución muy simple para esto. De todos modos, estaría extremadamente agradecido si alguien pudiera señalarme en la dirección correcta.

+0

No está almacenando los archivos cargados en ningún lugar del servidor. ¿Cómo espera que se persistan entre las devoluciones? Todo lo que tienes dentro de tu '

' es un campo de entrada de un solo archivo. Entonces, todo lo que puede esperar obtener en la acción del controlador es este archivo. Nada mas. No hay magia No hay ViewState en ASP.NET MVC. –

+0

Darin, gracias por tu comentario. Es correcto que no estoy almacenando el archivo en el servidor. Podría almacenarlo en la base de datos, pero eso sería un desperdicio de espacio en la base de datos y la paciencia del usuario, ya que llevaría mucho tiempo y no es realmente necesario. Lo que me gustaría hacer es simplemente mantenerlo en la memoria del servidor como una variable de alguna manera hasta que el usuario decida procesarlo. Luego almacenaré la salida de ese procesamiento en la base de datos. Creo que o bien estoy tratando de usar MVC de la manera incorrecta, o simplemente no sé cómo hacerlo. Simplemente no sé cuál es todavía ... – TMan

+2

Mantener archivos en la memoria del servidor es una de las peores cosas. Evita esto a cualquier precio. No solo eso consumirá mucha memoria en su servidor, pero no olvide que IIS podría reciclar el dominio de la aplicación en cualquier momento, perdiendo todos los datos almacenados en la memoria. ¿Qué sucede si se ejecuta en una granja de servidores?Si almacena en la memoria del servidor, esto significa que solo 1 servidor de la granja tiene esta información y si el equilibrador de carga envía las solicitudes subsiguientes a otro servidor de la granja de servidores, perderá la información. –

Respuesta

46

Ha pasado un tiempo desde que publiqué la pregunta, que fue bastante matizada por mi poca experiencia y conocimiento de MVC. Aún así, recibí algunas aportaciones bastante útiles, que finalmente me llevaron a encontrar una solución y también a obtener una idea de MVC.

Lo que me confundió, en primer lugar, era que usted podría tener un controlador con un objeto fuertemente tipado como un parámetro, como esto:

public ActionResult DoSomething(MyClass myObject)... 

Este objeto se originó desde el mismo controlador:

... 
return View(myObject); 
... 

Esto me llevó a creer que el objeto vivió a lo largo de estos dos pasos, y que de alguna manera podía esperar que pudiera enviarlo a la vista, hacer algo y luego "mágicamente" regresarlo nuevamente al controlador.

Después de leer sobre el enlace del modelo, entendí que este no es el caso. La vista está completamente muerta y estática, y a menos que almacene la información en alguna parte, desaparecerá.

Volviendo al problema, que consistía en seleccionar y cargar archivos desde el cliente, y crear una lista de estos archivos para mostrar, me di cuenta de que, en general, hay tres maneras de almacenar información entre solicitudes en MVC:

  1. puede almacenar información en los campos de la vista, y que lo ponga de nuevo al controlador tarde
  2. puede persistir en algún tipo de almacenamiento, por ejemplo, un archivo o una base de datos
  3. Puede almacenarlo en la memoria del servidor mediante el acceso a los objetos que se encuentran en las solicitudes, p. las variables de sesión

En mi caso, yo tenía básicamente dos tipos de información: a persistir 1. El archivo de metadatos (nombre de archivo, tamaño de archivo, etc.) 2. El contenido del archivo

El "por "el libro" sería probablemente almacenar los metadatos en los campos del formulario y los contenidos del archivo en un archivo o en db. Pero también hay otra manera. Como sé que mis archivos son bastante pequeños y solo habrá unos pocos, y esta solución nunca se implementará en una granja de servidores o similar, quería explorar la opción n. ° 3 de variables de sesión. Los archivos tampoco son interesantes para persistir más allá de la sesión; se procesan y descartan, por lo que no quería almacenarlos en mi db.

Después de leer este excelente artículo: Accessing ASP.NET Session Data Using Dynamics

yo estaba convencido. Simplemente creado una clase sessionbag como se describe en el artículo, y entonces yo podría hacer lo siguiente en mi controlador:

[HttpPost] 
    public ActionResult AddImportFile(HttpPostedFileBase file) 
    { 

     ImportDataViewModel importData = SessionBag.Current.ImportData; 
     if (importData == null) importData = new ImportDataViewModel(); 

     if (file == null) 
      return RedirectToAction("DataImport"); 

     if (file.ContentLength > 0) 
     { 
      ImportDataFile idFile = new ImportDataFile { File = file }; 
      importData.Files.Add(idFile); 
     } 

     SessionBag.Current.ImportData = importData; 

     return RedirectToAction("DataImport"); 
    } 

Soy plenamente consciente de que en la mayoría de los casos, esto sería una mala solución. Pero para los pocos kb de memoria del servidor que ocupan los archivos y con la simplicidad de todo, creo que funcionó muy bien para mí.

La ventaja adicional de usar SessionBag es que si el usuario ingresó un elemento de menú diferente y luego regresó, la lista de archivos aún estaría allí. Este no sería el caso, p. al elegir la opción de campos de formulario/almacenamiento de archivos.

Como observación final, me doy cuenta de que SessionBag es muy fácil de abusar, dada la simplicidad de uso. Pero si lo usa para lo que significa, es decir, datos de sesión, creo que puede ser una herramienta poderosa.

+0

Buen resumen y explicación. – Lester

+1

¿qué ocurre con la sesión que vence? – garik

+0

@garik una solución para hacer frente a las sesiones que se reciclan es utilizar un servidor de estado de sesión fuera de proceso o un servidor sql según https://technet.microsoft.com/en-us/library/cc754032(v=ws. 10) .aspx –

10

En cuanto Carga

1) Tal vez considere un cargador AJAX con HTML para permitir que su usuario seleccionar varios archivos antes de ser enviados al servidor. Este cargador de archivos BlueImp Jquery AJAX es bastante sorprendente con una muy buena API: Blueimp Jquery File Upload.Permitirá a los usuarios arrastrar y soltar o seleccionar múltiples archivos y editar el orden de los archivos, incluir/excluir, etc. Luego, cuando estén contentos, pueden presionar cargar para enviar a su controlador o cargar el controlador para procesarlo en el servidor.

2) Podrías hacer que cada carga persista en la base de datos, aunque estarías recargando toda la página y escribiendo un modelo de vista extra y un código de afeitar para lograr el efecto de listado. Esto probablemente no va a ser sensible ...

En cuanto Mantener WebForms Estado/MVC

Mantener estado entre solicitudes es algo mágico negro y el vudú. Al ingresar a ASP.NET MVC, entiéndalo entendiendo que las aplicaciones web se comunican mediante solicitudes y respuestas. ¡Así que ve a abrazar la red como sin estado y desarrolla desde allí! Cuando su modelo se publica a través de su controlador, es ido junto con las variables en el controlador! Sin embargo, antes de que desaparezca, puede almacenar sus contenidos en una base de datos para recuperarlos más tarde.

Las aplicaciones web no pueden mantener el estado verdadero como las aplicaciones de escritorio. Hay muchas maneras en que los marcos ajax y algunas herramientas de vudú emplean personas para simular el estado en el entorno HTTP. Y una simulación de estado es realmente solo una falsa mímica de estado. ASP.NET Web Forms intenta simular el estado de la mejor manera posible ocultando la naturaleza sin estado de HTTP del desarrollador. Puede experimentar muchos dolores de cabeza cuando intente utilizar su propio código AJAX junto con el código de marcado de Formularios Web y su propio marco Ajax.

Estoy muy contento de que estés aprendiendo MVC

Todas las bromas a un lado, si usted consigue el/HTTP/MVC mentalidad de Apatridia, va a ser muy fácil de aplicar los patrones de otros marcos súper populares como Ruby on Rails, SpringMVC (java), Django (python), CakePHP, etc ... Esta transferencia de conocimiento le ayudará a convertirse en un desarrollador mucho mejor y obtener REALMENTE bien en Ajax.

Estoy muy contento de que estés aprendiendo MVC 3, he estado haciendo unas prácticas en algunas grandes empresas que tenían estos grandes y locos proyectos ASP.NET Web Forms con código volando en todas partes solo para cambiar unos pocos numéricos valores en la base de datos (-_- ') Me sentí como si estuviera usando unas tijeras para tejer el calcetín de un bebé. Un movimiento incorrecto fácil, y todo se rompió. Parecía que se desarrollaba en PHP, sales sudando y no estás muy seguro de qué pasó y en qué línea. Era casi imposible depurar y actualizar.

+0

aunque algo obvio, agregaría 2) a) arriba para usar los campos ocultos para mantener la lista de archivos entre las solicitudes. En última instancia, la abstracción viewstate en el formulario web utiliza un campo oculto, por lo que no es una idea tan mala después de todo y será más fácil de implementar que la opción de base de datos. – Kiran

+0

Max, gracias por una respuesta completa. En cuanto a 1): Sí, probablemente intentaré implementar una funcionalidad de carga de archivos múltiples en una etapa posterior para que sea más fácil de usar, pero ese no es realmente el tema central. Creo que el tema central aquí es más sobre stateful vs stateless. Me he encontrado con esta discusión antes, pero no entiendo completamente todo esto de "abrazar la naturaleza sin estado de MVC". ¿Estás diciendo que no deberíamos implementar el estado en nuestras aplicaciones web? No todo debe ser almacenado en el db. Tome el carrito de compras, por ejemplo. Los humanos son por naturaleza con estado, entonces ¿por qué no deberían ser las aplicaciones web? – TMan

+0

Kiran, creo que tienes razón. Ahora estoy leyendo sobre el enlace de modelo, que parece representar una gran parte de la "magia" percibida en mi caso: cómo los campos de formulario en la vista se transforman repentinamente en instancias de clase fuertemente tipadas en el controlador. Al tratar de aplicar la "magia" en un escenario diferente sin entender los principios subyacentes, por supuesto, irá mal. Creo que estoy en el camino correcto ahora. – TMan

Cuestiones relacionadas