tutorial pages page net example aspnet asp asp.net-mvc ajax asp.net-mvc-2 viewstate

pages - ASP.NET MVC y ViewState



css asp net (7)

Ahora he visto algunas preguntas como esta, pero no es exactamente lo que quiero preguntar, así que para todos esos duplicados que gritan, me disculpo :).

Apenas he tocado ASP.NET MVC, pero por lo que entiendo no hay ViewState / ControlState ... bien. Entonces, mi pregunta es ¿cuál es la alternativa para conservar el estado de un control? Regresamos a la vieja escuela ASP donde podríamos simular lo que hace ASP.NET ViewState / ControlState creando entradas de formulario ocultas con el estado del control, o con MVC, asumimos siempre AJAX y retenemos todo el estado del lado del cliente y hacemos AJAX llamadas para actualizar?

Esta pregunta tiene algunas respuestas, ¿ Mantener el estado de visualización en Asp.net mvc? , pero no es exactamente lo que estoy buscando en una respuesta.

ACTUALIZACIÓN: Gracias por todas las respuestas hasta ahora. Solo para aclarar lo que no estoy buscando y lo que estoy buscando:

No busco:

  • Solución de sesión
  • Solución de galletas
  • No busca imitar a los formularios web en MVC

Lo que estoy buscando

  • Un método que solo retiene el estado en devolución de datos si los datos no se recuperan en un control. Piense en los formularios web con el escenario de solo vincular una cuadrícula en la carga de la página inicial, es decir, solo volver a vincular los datos cuando sea necesario. Como mencioné, no estoy tratando de imitar los formularios web, solo me pregunto qué mecanismos ofrece MVC.

AJAX llama es lo que hacemos. Si está hablando de grids en general, consulte JQGrid y cómo recomiendan la implementación de AJAX.


En los formularios web, los valores de control se mantienen en el estado de visualización, por lo que (teóricamente) no es necesario reinicializar y demás con cada devolución de datos. Los valores son (de nuevo teóricamente) mantenidos por el marco.

En ASP.NET MVC, si sigue el paradigma, no necesita mantener el estado en los elementos del formulario. Los valores de los elementos de formulario están disponibles en la publicación donde su controlador puede actuar sobre ellos (validación, actualizaciones de la base de datos, etc.). Para cualquier elemento del formulario que se muestra una vez que se procesa la publicación, usted (el desarrollador) es responsable de inicializarlos, el marco no lo hace automáticamente por usted.

Dicho esto, he leído acerca de un mecanismo llamado TempData que le permite a su controlador pasar datos a otro controlador luego de una redirección. En realidad, es una variable de sesión (o cookie si la configura como tal) pero se limpia automáticamente después de la siguiente solicitud.


La convención ya está disponible sin saltar a través de demasiados aros. El truco consiste en conectar los valores de TextBox basados ​​en el modelo que se pasa a la vista.

[AcceptVerbs(HttpVerbs.Get)] public ActionResult CreatePost() { return View(); } [AcceptVerbs(HttpVerbs.Post)] public ActionResult CreatePost(FormCollection formCollection) { try { // do your logic here // maybe u want to stop and return the form return View(formCollection); } catch { // this will pass the collection back to the ViewEngine return View(formCollection); } }

Lo que sucede a continuación es que ViewEngine toma formCollection y combina las claves dentro de la colección con los nombres / valores de ID que tiene a su vista, usando los ayudantes de HTML. Por ejemplo:

<div id="content"> <% using (Html.BeginForm()) { %> Enter the Post Title: <%= Html.TextBox("Title", Model["Title"], 50) %><br /> Enter the Post Body: <%= Html.TextArea("Body", Model["Body"]) %><br /> <%= Html.SubmitButton() %> <% } %> </div>

¿Nota que el cuadro de texto y el área de texto tienen los ID de Título y Cuerpo? Ahora, ¿observa cómo estoy configurando los valores del objeto Modelo de la vista? Ya que pasó en un FormCollection (y debe configurar la vista para que esté fuertemente tipada con un FormCollection), ahora puede acceder a ella. O, sin escribir con fuerza, simplemente puede usar ViewData ["Título"] (creo).

POOF Tu estado de vista mágico. Este concepto se llama convención sobre configuración.

Ahora, el código anterior se encuentra en su forma más simple, sin formato, utilizando FormCollection. Las cosas se ponen interesantes cuando empiezas a usar ViewModels, en lugar de FormCollection. Puede comenzar a agregar su propia validación de sus Modelos / Modelos de Vista y hacer que el controlador aumente automáticamente los errores de validación personalizados. Esa es una respuesta para otro día sin embargo.

Yo sugeriría usar un modelo PostFormViewModel en lugar del objeto Post, pero para cada uno de los suyos. De cualquier manera, al requerir un objeto en el método de acción, ahora obtienes un método IsValid () al que puedes llamar.

[AcceptVerbs(HttpVerbs.Post)] public ActionResult CreatePost(Post post) { // errors should already be in the collection here if (false == ModelState.IsValid()) return View(post); try { // do your logic here // maybe u want to stop and return the form return View(post); } catch { // this will pass the collection back to the ViewEngine return View(post); } }

Y tu vista de tipo fuerte debería ser modificada:

<div id="content"> <% using (Html.BeginForm()) { %> Enter the Post Title: <%= Html.TextBox("Title", Model.Title, 50) %><br /> Enter the Post Body: <%= Html.TextArea("Body", Model.Body) %><br /> <%= Html.SubmitButton() %> <% } %> </div>

Puede ir un paso más allá y mostrar los errores también en la vista, directamente desde el ModelState que configuró en el controlador.

<div id="content"> <%= Html.ValidationSummary() %> <% using (Html.BeginForm()) { %> Enter the Post Title: <%= Html.TextBox("Title", Model.Title, 50) %> <%= Html.ValidationMessage("Title") %><br /> Enter the Post Body: <%= Html.TextArea("Body", Model.Body) %> <%= Html.ValidationMessage("Body") %><br /> <%= Html.SubmitButton() %> <% } %> </div>

Lo interesante de este enfoque es que notará que no estoy configurando el resumen de validación, ni los mensajes de validación individuales en la Vista. Me gusta practicar los conceptos de DDD, lo que significa que mis mensajes de validación (y resúmenes) están controlados en mi dominio y se pasan en forma de colección. Luego, recorro la colección (si existen errores) y los agrego a la colección actual de ModelState.AddErrors. El resto es automático cuando regresas Ver (publicar).

Mucha convención está fuera. Algunos libros que recomiendo ampliamente que cubren estos patrones con mucho más detalle son:

Y en ese orden, el primero cubre las tuercas y tornillos en bruto de todo el marco MVC. Este último cubre técnicas avanzadas fuera de la relama oficial de Microsoft, con varias herramientas externas para hacer su vida mucho más fácil (Castle Windsor, Moq, etc.).


La mejor manera de hacer esto, creo, es serializar su modelo original a un campo oculto, luego deserializarlo y actualizar el modelo en la publicación. Esto es algo similar al enfoque del estado de vista, solo usted tiene que implementarlo usted mismo. Yo uso esto:

Primero necesito algunos métodos que faciliten las cosas:

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Web.Mvc; using LuvDaSun.Extensions; using System.Web.UI; namespace LuvDaSun.Web.Mvc { public static class HtmlHelperExtensions { static LosFormatter _losFormatter = new LosFormatter(); public static string Serialize(this HtmlHelper helper, object objectInstance) { var sb = new StringBuilder(); using (var writer = new System.IO.StringWriter(sb)) { _losFormatter.Serialize(writer, objectInstance); } return sb.ToString(); } } [AttributeUsage(AttributeTargets.Parameter)] public class DeserializeAttribute : CustomModelBinderAttribute { public override IModelBinder GetBinder() { return new DeserializeModelBinder(); } } public class DeserializeModelBinder : IModelBinder { static LosFormatter _losFormatter = new LosFormatter(); public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) { if (bindingContext.ModelType.IsArray) { var type = bindingContext.ModelType.GetElementType(); var serializedObjects = (string[])bindingContext.ValueProvider.GetValue(bindingContext.ModelName).ConvertTo(typeof(string[])); var deserializedObjects = Array.CreateInstance(bindingContext.ModelType.GetElementType(), serializedObjects.Length); for (var index = 0; index < serializedObjects.Length; index++) { var serializedObject = serializedObjects[index]; var deserializedObject = _losFormatter.Deserialize(serializedObject); deserializedObjects.SetValue(deserializedObject, index); } return deserializedObjects; } else { var serializedObject = (string)bindingContext.ValueProvider.GetValue(bindingContext.ModelName).ConvertTo(typeof(string)); var deserializedObject = _losFormatter.Deserialize(serializedObject); return deserializedObject; } } } }

entonces en mi controlador tengo algo como esto (para actualizar un producto)

public ActionResult Update(string productKey) { var model = _shopping.RetrieveProduct(productKey); return View(model); } [AcceptVerbs(HttpVerbs.Post)] public ActionResult Update([Deserialize]Shopping.IProduct _model, FormCollection collection) { UpdateModel(model); model.Save(); return RedirectAfterPost(); }

y necesito un campo oculto que contiene el objeto serializado en el formulario:

<% using (Html.BeginRouteForm("Product", FormMethod.Post, new { id = UniqueID, })) { %> <%= Html.Hidden("Model", Html.Serialize(Model)) %> <h1> Product bewerken</h1> <p> <label for="<%=UniqueID %>_Name"> Naam:</label> <input id="<%=UniqueID %>_Name" name="Name" type="text" value="<%= Html.AttributeEncode(Model.Name) %>" class="required" /> <br /> </p> <p> Omschrijving:<br /> <textarea id="<%= UniqueID %>_Description" name="Description" cols="40" rows="8"><%= Html.Encode(Model.Description) %></textarea> <br /> </p> <p> <label for="<%=UniqueID %>_Price"> Prijs:</label> <input id="<%= UniqueID %>_Price" name="Price" type="text" value="<%= Model.Price.ToString("0.00") %>" class="required" /> <br /> </p> <ul class="Commands"> <li><a href="" class="ClosePopup">Annuleren</a></li> <li> <input type="submit" value="Opslaan" /></li> </ul> <% } %> <script type="text/javascript"> jQuery(''#<%= UniqueID %>'').validate(); </script>

Como puede ver, se agrega un campo oculto (Modelo) al formulario. Contiene la información de serialización para el objeto original. Cuando se publica el formulario, el campo oculto también se publica (por supuesto) y el encuadernador de modelos personalizado deserializa los contenidos en el objeto original, que luego el controlador actualiza y guarda.

Tenga en cuenta que el objeto que está serializando debe decorarse con el atributo Serializable o debe tener un TypeConverter que pueda convertir el objeto en una cadena.

El estado de vista utiliza LosFormatter (serialización de objetos limitada) en formularios web. También ofrece encriptación de los datos de serialización.

saluda ...


La respuesta realmente depende de los tipos de controles para los que intenta mantener el estado. Para los controles básicos de HTML, es muy fácil mantener el estado con sus modelos. Para ello, debe crear una vista de tipo fuerte.

Entonces, si tuviéramos un modelo de usuario con las propiedades: Nombre de usuario, Nombre completo, Correo electrónico, podemos hacer lo siguiente en la vista:

<%= Html.ValidationSummary() %> <% using (Html.BeginForm()) { %> <fieldset> <legend>User details</legend> <%= Html.AntiForgeryToken() %> <p> <label for="Username">Username:</label> <%= Html.Textbox("Username", Model.Username, "*") %> </p> <p> <label for="FullName">FullName:</label> <%= Html.Textbox("FullName", Model.FullName, "*") %> </p> <p> <label for="Email">Email:</label> <%= Html.Textbox("Email", Model.Email, "*") %> </p> <p> <input type+"submit" value="Save user" /> </p> </fieldset> <% } %>

Entonces tendríamos dos acciones de controlador que muestran esta vista, una para obtener y otra para publicar:

[AcceptVerbs(HttpVerbs.Get)] public ActionResult User() { return View(new User()) } [AcceptVerbs(HttpVerbs.Post)] [ValidateAntiForgeryToken] public ActionResult User([Bind(Include = "Username,FullName,Email")]User user) { if (!ModelState.IsValid()) return View(user); try { user.save() // return the view again or redirect the user to another page } catch(Exception e) { ViewData["Message"] = e.Message; return View(user) } }

¿Es esto lo que estás buscando? ¿O desea mantener el estado de los modelos que no se muestran en una forma entre las solicitudes?

La clave a recordar es que su código se ejecuta en el servidor durante la duración de la solicitud y finaliza, la única información que puede pasar entre sus solicitudes es datos de formularios html básicos, parámetros de url e información de sesión.

Como han mencionado otras personas, recomendaría encarecidamente el marco MVC Pro ASP.NET de Steve Sandersan para una comprensión completa de cómo trabajar con el marco MVC.


Se supone que la vista es estúpida en el patrón MVC, solo muestra lo que el Controlador le da (obviamente, a menudo terminamos con algo de lógica, pero la premisa es que no debe ser) como resultado, los controles no son responsables de Su estado, vendrá desde el controlador cada vez.

No puedo recomendar el libro de Steven Sanderson, Pro ASP.NET MVC by Apress, lo suficiente como para familiarizarse con este patrón y esta implementación.


  • campos ocultos, como

    <% using (Html.BeginForm<SomeController>(c=>c.SomeAction(null))) {%> <%= Html.Hidden("SomeField", Model.SomeField)%> <%= Html.Hidden("AnotherField", Model.AnotherField)%>

  • establecer el modelo específico y no tener ningún campo explícito (da u campos ocultos). En el ejemplo a continuación, el controlador llena el Modelo con los valores recibidos desde la última publicación, por lo que esto habilita una opción no js en la página que puede filtrar según un estado:

    Some Filter: <% using( Html.BeginForm<SomeController>( c => c.SomeAction(model.SomeField, model.AnotherField, model.YetAnotherField, null, model.SomeOtherField) )) { %> <%= Html.DropDownList("status", Model.StatusSelectList)%> <input type="submit" value="Filter" class="button" /> <% } %>

  • use métodos de extensión para crear campos, si solo quiere que los campos se llenen con valores publicados cuando muestre mensajes de validación fallidos en el formulario enviado
  • en asp.net mvc 2 introdujeron una forma de guardar una instancia en un campo oculto ... codificado + (creo) firmado
  • TempData si todo lo anterior no lo hace (pasa por sesión, se limpia en la siguiente solicitud)
  • Como mencionó, cuando se usa ajax, el estado ya se encuentra en los campos cargados previamente en el sitio del cliente. Si necesita hacer una publicación completa, actualice cualquier campo que pueda necesitar con sus js.

Las anteriores son todas diferentes opciones independientes para lograrlo que se pueden usar en diferentes escenarios. Hay más opciones que no mencioné, es decir, cookies, sesión, almacenar cosas en db (como para un asistente de pasos múltiples reanudables), parámetros pasados ​​a una acción. No hay un solo mecanismo para gobernarlos a todos, y no debería existir.