2011-01-17 31 views
10

Acabo de pasar mucho tiempo exorcizando el viewstate grande (pero comprensiblemente útil) de asp.net desde una aplicación, y creo que vale la pena compartir cómo se hace.Cómo eliminar o comprimir su asp.net viewstate

Básicamente, quiero que esta pregunta esté abierta a todas las soluciones para contraer/comprimir/eliminar viewstate.

Respuesta

5

Otra opción mejor, ejecute su propia PageStatePersister. Esta es la mía, fuertemente inspirado por http://aspalliance.com/72:

using System.Web.UI; 

... in your page class: 

PageStatePersister pageStatePersister; 
protected override PageStatePersister PageStatePersister 
{ 
    get 
    { 
    // Unlike as exemplified in the MSDN docs, we cannot simply return a new PageStatePersister 
    // every call to this property, as it causes problems 
    return pageStatePersister ?? (pageStatePersister = new BetterSessionPageStatePersister(this)); 
    } 
} 

... in your BetterSessionPageStatePersister.cs: 

/// <summary> 
/// This class allows the viewstate to be kept server-side, so that postbacks are as small as possible. 
/// It is similar to the built-in 'SessionPageStatePersister', but it yields smaller postbacks, 
/// because the SessionPageStatePersister still leaves some viewstate (possibly it leaves the controlstate) 
/// in the postback. 
/// </summary> 
class BetterSessionPageStatePersister : PageStatePersister 
{ 
    public BetterSessionPageStatePersister(Page page) 
    : base(page) 
    { } 

    const string ViewStateFieldName = "__VIEWSTATEKEY"; 
    const string ViewStateKeyPrefix = "ViewState_"; 
    const string RecentViewStateQueue = "ViewStateQueue"; 
    const int RecentViewStateQueueMaxLength = 5; 

    public override void Load() 
    { 
    // The cache key for this viewstate is stored in a hidden field, so grab it 
    string viewStateKey = Page.Request.Form[ViewStateFieldName] as string; 

    // Grab the viewstate data using the key to look it up 
    if (viewStateKey != null) 
    { 
     Pair p = (Pair)Page.Session[viewStateKey]; 
     ViewState = p.First; 
     ControlState = p.Second; 
    } 
    } 

    public override void Save() 
    { 
    // Give this viewstate a random key 
    string viewStateKey = ViewStateKeyPrefix + Guid.NewGuid().ToString(); 

    // Store the view and control state 
    Page.Session[viewStateKey] = new Pair(ViewState, ControlState); 

    // Store the viewstate's key in a hidden field, so on postback we can grab it from the cache 
    Page.ClientScript.RegisterHiddenField(ViewStateFieldName, viewStateKey); 

    // Some tidying up: keep track of the X most recent viewstates for this user, and remove old ones 
    var recent = Page.Session[RecentViewStateQueue] as Queue<string>; 
    if (recent == null) Page.Session[RecentViewStateQueue] = recent = new Queue<string>(); 
    recent.Enqueue(viewStateKey); // Add this new one so it'll get removed later 
    while (recent.Count > RecentViewStateQueueMaxLength) // If we've got lots in the queue, remove the old ones 
     Page.Session.Remove(recent.Dequeue()); 
    } 
} 
+0

Este método redujo mis devoluciones de 101k a 49k, así que estoy contento con eso :) – Chris

+0

En Guardar(), ¿podría reutilizar la misma clave en PostBack? – yanta

+1

Este código solo permite que las pestañas X se abran al mismo tiempo, en este ejemplo, abrir 6 pestañas eliminará el estado de visualización de la primera pestaña. – Andreas

6
+0

¡¡Lo haría si pudiera !!! – Chris

+0

Esa es una sugerencia horrible. (EDITAR: está bien, hay una razón válida para la sugerencia, pero no es una ventana tan grande, y él dijo que lo está eliminando de un sitio existente) – jcolebrand

+4

Probablemente no sea útil para personas con sitios existentes que usan viewstate, pero : No puedo evitar estar de acuerdo contigo. –

7

Primera opción fácil, use la clase integrada SessionPageStatePersister. Lo que hace es mantener el viewstate en la sesión en el servidor, en lugar de enviarlo al cliente. Sin embargo, todavía envía un estado de vista inicial menor por lo que no es todas las rosas:

using System.Web.UI; 
... the following goes in your Page class (eg your .aspx.cs) ... 
PageStatePersister pageStatePersister; 
protected override PageStatePersister PageStatePersister 
{ 
    get 
    { 
    // Unlike as exemplified in the MSDN docs, we cannot simply return a new PageStatePersister 
    // every call to this property, as it causes problems 
    return pageStatePersister ?? (pageStatePersister = new SessionPageStatePersister(this)); 
    } 
} 

Este método encogida una devolución de datos especialmente grande de 100k a 80k. No es genial, pero es un buen comienzo.

+0

100kB ViewState? ¿Estás seguro de que todo persistió en viewstate tiene que estar allí? –

+0

@ladislav Creo que esa sería la razón de esto – jcolebrand

+1

@Ladislav - estoy seguro de que la mayoría de ese viewstate no necesita estar allí. Pero es una aplicación heredada y no es "comercialmente viable" revisarla y arreglarla. – Chris

2

Al principio es importante entender qué es el estado de vista y por qué lo quiere en primer lugar. Después de eso, solo se trata de ser consciente de lo que la aplicación está haciendo por usted y de recordar adjuntar UseViewState = "false" a todos los elementos que normalmente usarían viewstate.

Ahora, para recordar por qué es útil, tendrá una necesidad definida de recuperar cosas con más frecuencia de forma manual.

Un tiempo y lugar para todas las herramientas, ¿sí?

+0

Por supuesto, viewstate debe usarse con prudencia. Todos estamos de acuerdo en eso. – Chris

+0

Bueno, sí, todos los que lo conocemos. Sin embargo, para los recién llegados no tendrán idea de qué es o si existe, ¿sí? ;) – jcolebrand

2

completamente deshacerse de él:

protected override object LoadPageStateFromPersistenceMedium() 
    { 
     return null; 
    } 

    protected override void SavePageStateToPersistenceMedium(object viewState) 
    { 
    } 
+0

¿Dónde lo declaras? – jcolebrand

+0

En su código detrás o entre una etiqueta

1

Puede, con un poco de engaño, secuestrar sólo la serialización del estado de la página mediante la derivación de System.Web.Page y reemplazar la propiedad PageStatePersister:

private PageStatePersister _pageStatePersister = null; 
    protected override PageStatePersister PageStatePersister 
    { 
     get { return _pageStatePersister ?? (_pageStatePersister = new PersistState(this)); } 
    } 

Una vez que haya hecho esto puede derivar una nueva instancia de HiddenFieldPageStatePersister y desde allí utilizar la reflexión para cambiar la implementación de persistencia:

class PersistState : HiddenFieldPageStatePersister, IStateFormatter 
    { 
     public PersistState(Page p) : base(p) 
     { 
      FieldInfo f = typeof(PageStatePersister).GetField("_stateFormatter", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.SetField); 
      f.SetValue(this, this); 
     } 

     object IStateFormatter.Deserialize(string serializedState) 
     { 
      BinaryFormatter f = new BinaryFormatter(); 
      using (GZipStream gz = new GZipStream(new MemoryStream(Convert.FromBase64String(serializedState)), CompressionMode.Decompress, false)) 
       return f.Deserialize(gz);      
     } 

     string IStateFormatter.Serialize(object state) 
     { 
      BinaryFormatter f = new BinaryFormatter(); 
      using (MemoryStream ms = new MemoryStream()) 
      { 
       using (GZipStream gz = new GZipStream(ms, CompressionMode.Compress, true)) 
        f.Serialize(gz, state); 
       return Convert.ToBase64String(ms.ToArray()); 
      } 
     } 
    } 

TEN CUIDADO

Este es un ejemplo solo para fines exploratorios. El código anterior ES UN RIESGO DE SEGURIDAD, ya que no firma ni encripta la carga y, por lo tanto, puede ser pirateado fácilmente por cualquiera que intente dañar el sitio.

De nuevo NO USE ESTE CÓDIGO sin un conocimiento completo y completo de la seguridad, la criptografía y la serialización .Net.

</warning> 

El problema real, como ya se ha dicho, es el uso del estado de la página para empezar. La solución más sencilla para una aplicación ASP.NET mal escrita que hace un uso intensivo del estado de la página es poner de pie un servidor de estado y utilizar la SessionPageStatePersister.

+0

LMAO, en realidad no esperaba un voto positivo, solo para demostrar cómo, no para sugerirlo realmente como una respuesta. Me pregunto si puedo votar por mi propia respuesta. –

+0

No. gorrón. Aparentemente tampoco puedes votar por ti mismo;) –

+0

He votado a favor: ¡es una solución que vale la pena! Si de alguna manera lo hizo almacenar y verificar un evento aleatorio (por ejemplo, un guid al azar) en la sesión del usuario, debería ser seguro. – Chris

Cuestiones relacionadas