2009-09-24 35 views
17

Ahora he visto algunas preguntas como esta, pero no es exactamente lo que quiero preguntar, así que para todos esos gritos duplicados, me disculpo :).ASP.NET MVC y ViewState

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 al ASP de la vieja escuela donde podemos simular lo que ASP.NET ViewState/ControlState hace al crear entradas de formularios ocultos con el estado del control, o con MVC, simplemente asumimos AJAX siempre y conservamos todos los estados del lado del cliente y hacemos AJAX llamadas para actualizar?

Esta pregunta tiene algunas respuestas, Maintaining viewstate in Asp.net mvc?, pero no es exactamente lo que estoy buscando en una respuesta.

ACTUALIZACIÓN: gracias por todas las respuestas hasta el momento. Sólo para aclarar lo que no estoy buscando y lo que estoy buscando:

No necesito:

  • solución Sesión
  • solución Cookies
  • Sin mirar a imitar WebForms en MVC

Lo que soy/ha buscado:

  • Un método que solo conserva el estado en la devolución de datos si los datos no se recuperan a un control. Piense en WebForms con el escenario de solo enlazar 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 a WebForms, solo me pregunto qué mecanismos ofrece MVC.
+1

Te das cuenta de que ViewState solo usa entradas ocultas, ¿no? HTTP es sin estado, y ASP.NET MVC lo abarca en lugar de abstraerlo. –

+0

@mgroves - Sí, me doy cuenta de que es apátrida. Es por eso que mencioné la simulación de ViewState usando entradas ocultas. – nickytonline

+2

Solo estoy diciendo que no estarías 'simulando' ViewState, harías básicamente lo mismo. Por supuesto, con el patrón MVC, (idealmente) necesitarías hacer mucho menos. –

Respuesta

16

La convención ya está disponible sin saltar demasiados aros. El truco consiste en cablear los valores TextBox basados ​​en el modelo que pasas 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 el ViewEngine toma la FormCollection y coincide con las llaves dentro de la colección con los nombres de ID/valor que tiene en su punto de vista, el uso de los ayudantes 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> 

¿Observe que el cuadro de texto y el área de texto tienen los ID de Título y Cuerpo? Ahora, observe cómo estoy configurando los valores del objeto Modelo de Vista? Ya que aprobó en un FormCollection (y debe configurar la vista para que esté fuertemente tipeado con un FormCollection), ahora puede acceder a él. O bien, sin un fuerte tipado, simplemente puede usar ViewData ["Title"] (creo).

POOF Tu mágico ViewState.Este concepto se llama convención sobre configuración.

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

Sugeriría utilizar un PostFormViewModel en lugar del objeto Post, pero para cada uno. De cualquier manera, al requerir un objeto en el método de acción, ahora obtiene un método IsValid() al que puede 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 su punto de vista inflexible de tipos tendría que ser ajustado:

<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> 

que puede tomar un paso más allá y mostrar los errores, así como en la vista, directamente desde el ModelState que configure 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 que es interesante con este enfoque es que se dará cuenta de que no estoy estableciendo el resumen de validación, ni los mensajes de validación individuales en la vista. Me gusta practicar conceptos 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 existe algún error) y la agrego a la colección ModelState.AddErrors actual. El resto es automático cuando devuelve View (post).

Muchos de los congresos se agotaron. Algunos libros le recomiendo que la cobertura de estos patrones en mucho más detalle son:

Y en ese orden la primera abarca los frutos secos crudos y los pernos de todo el marco MVC . Este último cubre técnicas avanzadas fuera de la versión oficial de Microsoft, con varias herramientas externas para facilitarle la vida (Castle Windsor, Moq, etc.).

+2

Es bueno ver que vinieron y aceptó su respuesta –

4

Se supone que la Vista es tonta en el patrón MVC, simplemente muestra lo que el Controlador le da (obviamente, a veces terminamos con cierta lógica pero la premisa es que no) como resultado, los controles no son responsables de su estado, siempre vendrá del controlador.

No puedo recomendar el libro de Steven Sanderson Pro ASP.NET MVC de Apress lo suficiente para entender este patrón y su implementación.

+1

Entonces, ¿qué estás diciendo es que debo lidiar con el estado de un control, MVC no tiene ningún mecanismo para proporcionar el estado de control? Leyendo las respuestas a continuación, parece que tiene un mecanismo (s). – nickytonline

+3

Lo que estoy diciendo es que el control HTML no debería tener ningún estado, es solo una representación de un elemento del Modelo. El Modelo tiene el 'estado', el Controlador proporciona los datos del Modelo a la vista para mostrar. Si el usuario actualiza algo y hace clic en 'Guardar' por ejemplo, entonces el Controlador procesa la 'entrada' entrante y actualiza el Modelo. El Controlador entonces determinará a dónde va el siguiente usuario, ya sea la misma vista o una nueva vista. De cualquier forma, los datos para la vista se recuperan del Modelo y se presentan a la Vista. Puede ser difícil entender, pero vale la pena el esfuerzo. – Lazarus

+0

Sin embargo, donde puedo ver la necesidad de algo como ViewState es si tiene una KendoWindow cargando una Vista parcial, y los datos grabados en la Vista parcial son necesarios en la ventana principal. Debería haber una manera de registrar esos datos en la memoria sin tener que tener un modelo involucrado, excepto el que recuperó los datos en primer lugar de PartialView y el modelo para la vista de los padres. El problema está en transferir datos de uno a otro. – vapcguy

0

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

3

En Web Forms, los valores de control se mantienen en viewstate por lo que (teóricamente) no es necesario reinicializar y tal 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 del elemento de formulario están disponibles en la publicación donde su controlador puede actuar sobre ellos (validación, actualizaciones de bases de datos, etc.). Para cualquier elemento de formulario que se muestre 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 después 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 próxima solicitud.

3
  • campos ocultos, como:

    <% using (Html.BeginForm<SomeController>(c=>c.SomeAction(null))) {%> 
        <%= Html.Hidden("SomeField", Model.SomeField)%> 
        <%= Html.Hidden("AnotherField", Model.AnotherField)%> 
    
  • ajuste del modelo específico & no tener campos explícitos (da u campos ocultos).En el siguiente ejemplo, el modelo se llena por el controlador con los valores recibidos del último mensaje, así que esto permite una opción sin js en la página que puede filtrar basándose en 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" /> 
          <% } %> 
    
  • métodos de extensión utilizar para crear campos, si solo desea rellenar los campos con los valores publicados cuando muestra 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) firmó
  • TempData si todo lo anterior no lo hace (pasa por la sesión - limpiado en la próxima solicitud)
  • como mencionaste, cuando se usa ajax, el estado ya está en los campos previamente cargados en el sitio del cliente. Si necesita hacer una publicación completa, actualice cualquier campo que pueda necesitar con su js.

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

3

La respuesta realmente depende de los tipos de controles para los que intenta mantener el estado. Para los controles Html básicos, es muy fácil mantener el estado con sus Modelos, para ello necesita crear una vista fuertemente tipada.

Así que si teníamos un modelo de usuario con las propiedades: nombre de usuario, NombreCompleto, 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> 
<% } %> 

tendríamos entonces dos acciones del controlador que muestran este punto de vista, uno para obtener y otro para la publicación:

[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) 
    } 
} 

¿Esto es lo que estás buscando? ¿O desea mantener el estado de los Modelos que no se muestran en un formulario entre las solicitudes?

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

Como han mencionado otras personas, recomiendo altamente el Pro ASP.NET MVC Framework de Steve Sandersan para una comprensión completa de cómo trabajar con MVC Framework.

+0

Entonces, esto es exactamente lo que respondí a continuación ... – eduncan911

+0

ahh sí, lo siento. Estaba aburrido en el trabajo y simplemente saltó directamente en –

+0

Np. Parece que no estamos obteniendo los votos, a pesar de que ambos sabemos que esta es la forma de persistir en los datos, sin que el controlador se preocupe, ya que no es responsabilidad del controlador configurar un cuadro de texto, el controlador solo debe generar el modelo. y pasarlo a la vista para renderizar. Deberíamos haber obtenido los votos, pero bueno. – eduncan911

2

La mejor manera de hacerlo, 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 viewstate, solo usted debe implementarlo usted mismo. Yo uso este:

primero necesito algunos métodos que hacen las cosas más fáciles:

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 siguiente formato:

<% 
     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 puedes ver, un saludo El campo dden (Modelo) se agrega 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 contenido se deserializa mediante el enlazador de modelo personalizado al 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 necesita tener un TypeConverter que pueda convertir el objeto en una cadena.

El ViewState en formularios web utiliza LosFormatter (Serialización de Objetos Limitados). También ofrece cifrado de los datos de serialización.

saluda ...