34 messaggi dal 13 gennaio 2006
Quante volte è capitato che clienti ci constringessero a scrivere pagine "terribili", zeppe di controlli ficcati tutti insieme perché il cliente vuole così e non vuole sentire parlare di pesantezza della pagina e sciocchezze del genere?
Beh, è in questi casi che compare il classico errore di viewstate, dato che ce n'é talmente tanto che il browser non fa in tempo a caricarlo tutto prima del successivo postback, così fallisce l'event validation.

Ho letto nel libro di Esposito che è possibile salvare il viewstate su server, derivando da Page una classe che contiene l'override dei metodi LoadPageStateFromPersistenceMedium e SavePageStateToPersistenceMedium, ma bisogna gestire autonomamente il file (non ho capito tra l'altro se è un file per utente o un file per utente e per pagina) e mi pare abbastanza scomodo.

Mi chiedevo allora se rendere enabled il form solo nell'evento PreRenderComplete poteva essere una soluzione semplice ed efficace, come suggerisce l'articolo che ho trovato e riporto più sotto, se ho interpretato correttamente.
In teoria con l'evento PreRenderComplete, il viewstate dovrebbe essere già caricato tutto, o mi sbaglio ?

Grazie per qualsiasi delucidazione in merito.

Nick


Hi everyone,

The error that's happening (as has been mentioned earlier) is caused by an ASP.net 2.0 feature called Event Validation. This is a security feature that ensures that postback actions only come from events allowed and created by the server to help prevent spoofed postbacks. This feature is implemented by having controls register valid events when they render (as in, during their actual Render() methods). The end result is that at the bottom of your rendered <form> tag, you'll see something like this:

<input type="hidden" name="__EVENTVALIDATION" id="__EVENTVALIDATION" value="AEBnx7v.........tS" />

When a postback occurs, ASP.net uses the values stored in this hidden field to ensure that the button you clicked invokes a valid event. If it's not valid, you get the exception that you've been seeing.

The problem you're seeing happens specifically when you postback before the EventValidation field has been rendered. If EventValidation is enabled (which it is, by default), but ASP.net doesn't see the hidden field when you postback, you also get the exception. If you submit a form before it has been entirely rendered, then chances are the EventValidation field has not yet been rendered, and thus ASP.net cannot validate your click.

One work around is of course to just disable event validation, but you have to be aware of the security implications. Alternatively, just never post back before the form has finished rendering. Of course, that's hard to tell your users, but perhaps you could disable the UI until the form has rendered?

Unfortunately there's no "perfect" solution for this just yet.

Thanks,

Eilon
probabilmente ti riferisci al libro di Dino Esposito su .net 1.1.

nel due cis ono cambiamenti radicali.


per poter adesempio salvare il ViewState su sql adesso i passi sono più o meno questi:

- creare una classe che erediti da PageAdapter
- creare una classe che erediti da PageStatePersister

il nuovo PageAdapter deve ritornare eseguendo l'eoverride GetStatePersister un istanza del tuo PageStatePersister, infine tramite un bel file nella cartella Browsers associ un l'adpter a tutte le tue pagina.

ciao marco
Modificato da nostromo il 04 aprile 2006 11.30 -

Chi parla senza modestia troverà difficile rendere buone le proprie parole.
Confucio

http://nostromo.spaces.live.com/default.aspx
34 messaggi dal 13 gennaio 2006
Ciao Marco,
in realtà mi riferivo a ASP.NET 2.0 Cap. 13 pag. 586 della versione in italiano.
Comunque mi interessava di più il discorso il discorso del PreRenderComplete. Secondo te non risolvo il problema abilitando il form in quell'evento ?

Ciao e grazie.

Nick
ho riletto la tua domanda, OnPreRenderComplete non risolverebbe il problema.

se ho capito bene dovrebbe invece fare al caso tuo imposatre nella direttiva page EnableEventValidation="false".

strano comunque che dino non suggerisca PageStatePersister, mi ricordo che LoadPageStateFromPersistenceMedium lo suggeriva nel suo libro su asp.net 1.1 ed era l'unico sistema

ciao marco

Chi parla senza modestia troverà difficile rendere buone le proprie parole.
Confucio

http://nostromo.spaces.live.com/default.aspx
34 messaggi dal 13 gennaio 2006
Ho controllato, il PageAdapter è appena accennato nel suo libro, comunque anche nel suo blog porta avanti lo stesso discorso e qualcuno gli fa presente del PageAdapter... d'altra parte ho visto che in giro non c'è molta documentazione a proposito.
Intanto grazie delle dritte, vedo se provarlo o no.

Ciao

Nick
se ti interessa posso postarti del codice che ho scritto pempo fa, dovrebbe diventare uno script per la rubrica uno script al giorno, ma visto i templi biblici che ho ultimamente ti posso postare un "anteprima"

ciao marco

Chi parla senza modestia troverà difficile rendere buone le proprie parole.
Confucio

http://nostromo.spaces.live.com/default.aspx
34 messaggi dal 13 gennaio 2006
Se ce l'hai già pronto, gli darei un'occhiata volentieri, grazie.

Ciao

Nick
chiedo venia in anticipo per la lunghezza del post.

considera che è uno script in beta devo rifinirlo testarlo a dovere e analizzare alcuni rischi di sicurezza che potrebbe comportare questo meccanismo.

insomma declino ogni responsabilità

using System;
using System.Data;
using System.Data.SqlClient;
using System.Data.SqlTypes;
using System.Configuration;
using System.Web;
using System.Web.Configuration;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using System.Web.UI.Adapters;
using System.Security.Permissions;


/// <summary>
/// Summary description for SqlPageStatePersister
/// </summary>

namespace AspItalia
{
    public class SqlStatePersister : PageStatePersister
    {
        #region query
        public const string saveQuery =
    @"
IF @GUID IS  NULL
  BEGIN
  SET @GUID = NEWID()
  INSERT INTO SqlViewState
                        (ID,Value, FirstAccess, LastAccess)
  VALUES     (@GUID ,@VIEWSTATE, GETDATE(), GETDATE())
  END
ELSE
  BEGIN
  UPDATE    SqlViewState
  SET              Value = @VIEWSTATE, LastAccess = GETDATE()
  WHERE     (ID = @GUID)
  END";

        public const string ReadQuery = "SELECT Value FROM SqlViewState WHERE ID = @GUID";
        #endregion

        public SqlStatePersister(Page page): base(page){ }

        public override void Load()
        {
            //recupero il guid
            string s = this.Page.Request.Form["__HiddenGuid"];
            //se esiste
            if (!string.IsNullOrEmpty(s))
            {
                //creo un nuovo guid
                Guid id = new Guid(s);
                //recupero il formatter
                IStateFormatter formatter = this.StateFormatter;
                //recupero la connessione
                SqlConnection connection = new SqlConnection(WebConfigurationManager.ConnectionStrings[0].ConnectionString);
                try
                {
                    //inizializzo una nuova istanza di SqlCommand
                    SqlCommand command = new SqlCommand(ReadQuery, connection);
                    //inizializzo il parametro @GUID e lo aggiungo al comando
                    SqlParameter guidParameter = new SqlParameter("@GUID", SqlDbType.UniqueIdentifier);
                    guidParameter.Value = id == Guid.Empty ? SqlGuid.Null : id;
                    guidParameter.Direction = ParameterDirection.Input;
                    command.Parameters.Add(guidParameter);
                    //apro la connessione
                    connection.Open();
                    //recupero il viewstate
                    object ob = command.ExecuteScalar();
                    //chiudo la connessione
                    connection.Close();
                    //deserializzo lo stao dei controlli
                    Pair pair = (Pair)formatter.Deserialize((string)ob);
                    //imposto il ViewState
                    ViewState = pair.First;
                    //imposto il ControlState
                    ControlState = pair.Second;
                }
                finally 
                {
                    if (connection.State != ConnectionState.Closed)
                    {
                        connection.Close();
                    }
                }
            }
        }

        public override void Save()
        {
            //se il ViewState o il ControlState non sono nulli
            if (this.ViewState != null || this.ControlState != null)
            {
                Pair statePair = new Pair(ViewState, ControlState);
                //recupero il guid precedentemente salvato dal campo nascosto
                string s = this.Page.Request.Form["__HiddenGuid"];
                Guid guid;
                try
                {
                    //se è stato salvato 
                    if (!string.IsNullOrEmpty(s))
                    {
                        //creoil guid
                        guid = new Guid(s);
                    }
                    else
                    {
                        guid = Guid.Empty;
                    }
                }
                catch
                {
                    guid = Guid.Empty;
                }
                //salvo lo stato dei controlli sul DataBase
                SaveOrUpdateViewStateToSqlServer(this.StateFormatter.Serialize(statePair), ref guid);
                //instanzio una nuvo campo hidden per contenere il guid
                HiddenField hiddenGuid = new HiddenField();
                //li assegno un id
                hiddenGuid.ID = "__HiddenGuid";
                //salvo il guid
                hiddenGuid.Value = guid.ToString();
                //aggiungo HiddenField alla collezione del controllo HtmlForm
                this.Page.Controls[3].Controls.Add(hiddenGuid);
            }
        }

        private void SaveOrUpdateViewStateToSqlServer(string stringToSave, ref Guid id)
        {
            //inizializzo una nuova istanza di SqlConnection con la stringa di connessione
            SqlConnection connection = new SqlConnection(WebConfigurationManager.ConnectionStrings[0].ConnectionString);
            try
            {
                //inizializzo SqlCommand
                SqlCommand command = new SqlCommand(saveQuery, connection);
                //istanzio e aggiungo il parametro @GUID
                SqlParameter guidParameter = new SqlParameter("@GUID", SqlDbType.UniqueIdentifier);
                guidParameter.Value = id == Guid.Empty ? SqlGuid.Null : id;
                guidParameter.Direction = ParameterDirection.InputOutput;
                command.Parameters.Add(guidParameter);
                //istanzio e aggiungo il parametro @ViewState
                SqlParameter viewStateParameter = new SqlParameter("@ViewState", SqlDbType.Text);
                viewStateParameter.Direction = ParameterDirection.Input;
                viewStateParameter.Value = stringToSave;
                command.Parameters.Add(viewStateParameter);
                //apro la connesione
                connection.Open();
                //eseguo l'inser o l'update
                command.ExecuteNonQuery();
                //chiudo la connessione
                connection.Close();
                //recupero il parametro di ritorno
                id = ((SqlGuid)guidParameter.Value).Value;
            }
            finally
            {
                if (connection.State != ConnectionState.Closed)
                {
                    connection.Close();
                }
            }
        }
    }

    [AspNetHostingPermission(SecurityAction.Demand, Level = AspNetHostingPermissionLevel.Minimal)]
    public class MyPageAdapter : System.Web.UI.Adapters.PageAdapter
    {

        public override PageStatePersister GetStatePersister()
        {
            return new SessionPageStatePersister(this.Page);
        }
    }
}


ciao marco

Chi parla senza modestia troverà difficile rendere buone le proprie parole.
Confucio

http://nostromo.spaces.live.com/default.aspx

Torna al forum | Feed RSS

ASPItalia.com non è responsabile per il contenuto dei messaggi presenti su questo servizio, non avendo nessun controllo sui messaggi postati nei propri forum, che rappresentano l'espressione del pensiero degli autori.