Torno ancora sull'uso delle risorse in ASP.NET 2.0 perchè ho scoperto un comportamento che di primo acchito mi ha creato qualche problema. Il problema derivava dal fatto di aver implementato l'ExpressionBuilder di ho parlato in un precedente post ed aver inserito le espressioni all'interno di alcuni Literal nella pagina. Chi avesse provato questo codice si sarà reso conto che la Culture restituita dal Thread corrente è sempre quella di default del sistema e che ogni tentativo per modificarla non ha l'esito sperato. In sostanza quello che accade è che il momento in cui vengono valutate le espressioni nella pagina è precedente a qualunque altro evento della pagina stessa, precedente perfino al PreInit e quindi non è possibile impostare la cultura corrente prelevandola ad esempio da una Session oppure dal profilo utente.

Questo comportamento deriva dal fatto che le espressioni vengono valutare nel momento in cui la pagina viene costruita e non durante il normale flusso di esecuzione. Per capire meglio quanto sto dicendo, e la conseguente soluzione che proporrò, è interessante provare a fare un semplice test. Preso il codice del post precedente e creata una pagina funzionante che usa una espressione si può provare ad introdurre un errore nel codice generato dal CodeDom. Io ho scelto ad esempio di restituire il nome di un metodo che non esiste. La cosa curiosa è che Visual Studio genera immediatamente un errore, a differenza di quello che ci si potrebbe attendere, e in seguito a tale errore ci mostra il sorgente della pagina aspx generata. Ecco riportato il punto relativo la espression:

@__ctrl.Text = 
    System.Convert.ToString(
        SqlResource.GetObject1("hallo_world"), 
        System.Globalization.CultureInfo.CurrentCulture);

 

Riportato in rosso il codice generato da CodeDom, cui ho aggiunto l'1 finale per causare l'errore. Ora, se con un po' di pazienza si risale il flusso di esecuzione si arriverà a notare che il punto di partenza dello stesso è nel metodo override FrameworkInitialize(). FrameworkInitialize è un metodo che in .NET 1.1 non era documentato e il cui uso era sconsigliato. Risalendo ancora il flusso di esecuzione, stavolta affidandosi a Reflector in breve si vede che FrameWorkInitialize è il primo metodo che viene chiamato all'interno della pagina direttamente dalla ProcessRequest quindi molto prima che gli eventi PreInit e Init si verifichino. Ne deriva che l'unico punto possibile nella pagina per impostare la CultureInfo è il costruttore.

Per risolvere alla radice questo problema, dato che non mi piaceva l'idea di usare il costruttore per questo genere di attività, ho deciso di scrivere un piccolo HttpModule di poche righe che si incarichi di questa attività. Il vantaggio di usare l'HttpModule è che estende facilmente il comportamento a tutte le pagine/webservices sotto il controllo del runtime senza che sia necessario scrivere una classe base per ognuna di esse. Ecco riportato il codice del Modulo:

public class GlobalizationModule : IHttpModule
{
    
public void Dispose()
    {}

    
public void Init(HttpApplication context)
    {
        context.PreRequestHandlerExecute += 
            
new EventHandler(context_PreRequestHandlerExecute);
    }

    
void context_PreRequestHandlerExecute(object sender, EventArgs e)
    {
        Thread.CurrentThread.CurrentUICulture = 
            
this.CurrentCulture;
    }

    
private CultureInfo CurrentCulture
    {
        
get
        
{
            
return CultureInfo.CreateSpecificCulture(
                HttpContext.Current.Profile.GetPropertyValue("Culture") 
as string);
        }
    }
}

Il modulo fa uso dell'evento PreRequestHandlerExecute che viene eseguito subito prima che il runtime chiami la ProcessRequest della pagina. In questo modo l'impostazione della cultura avverrà dopo che il costruttore della pagina è stato eseguito, ma subito prima che si entri nel normale flusso di esecuzione. Personalmente ho scelto di prelevare la Culture corrente dal Profile dell'utente collegato. In questo modo si rende persistente la cultura utilizzata attraverso diverse sessioni oltre che su diverse chiamate.

Occorre infine segnalare un particolare rilevante. Visto quello che è il flusso di esecuzione della chiamata, non ho ancora trovato un metodo valido per cambiare la Culture allo scattare di un evento di un controllo. Mi spiego meglio: se ad esempio nella pagina disponiamo della classica DropDownList con tutte le lingue e volessimo intercettare l'evento SelectedIndexChanged per modificare la lingua della pagina corrente scopriremmo ben presto che questo ha un comportamento anomalo. Infatti a ben pensarci quando si entra nell'handler dell'evento e si imposta la nuova culture, la pagina è stata già generata nella lingua precedente. Perciò l'unica cosa da fare è di usare un Response.Redirect(Request.Path) dopo aver impostato la nuova cuture che otterrà l'effetto di ricaricare completamente la pagina. In questo modo però si perde completamente lo stato della pagina che poteva essere memorizzato nella ViewState. La morale di tutto ciò è quindi: Create sempre una pagina di "profilo utente" che permetta di impostare la Culture prescelta, e togliete di mezzo bandierine e tendine della lingua... non è una gran perdita.

powered by IMHO 1.3


per leggere il post originale o inviare un commento visita il seguente indirizzo: ASP.NET 2.0: Propagare la CultureIfo a tutte le pagine della WebApplication