Tempo fa, quando ancora il Framework 2.0 era solo in beta avevo accennato all'uso di un nuovo tipo di espressioni per il recupero di risorse. Oggi, la pratica lavorativa di tutti i giorni ha portato alla luce le potenzialità di questo tipo di espressioni che nel gergo del framework si chiamano ExpressionBuilders. La potenza degli ExpressionBuilder è davvero notevole dato che con semplicità è possibile crearne di nuovi per soddisfare le esigenze di ogni progetto.

Poniamo ad esempio di voler estrarre le risorse di una pagina ASP.NET (le stringhe localizzate ad esempio) da una tabella di un database SqlServer anzichè da un assembly satellite. Penso siamo tutti daccordo che questa operazione in ASP.NET 1.1 non è per nulla semplice e richiede la scrittura di parecchio codice. Con la versione 2.0 del framework si sarebbe tentati di scrivere una cosa del genere:

<asp:Literal runat="server" Text="<%$ SqlResource: hallo_world %>" />

E così facendo ottenere Hallo World!, Ciao Mondo! o Hallo Welt!, rispettivamente se abbiamo scelto la lingua inglese, italiana o tedesca. La keyword SqlResources non esiste nel framework, ma con un po' di codice ben piazzato è possibile registrare un nuovo ExpressionBuilder che risponda a tale keyword ritornandoci la risorsa corrispondente alla chiave e alla cultura selezionate, recuperandole da un database sql server. Ecco come lo si può registrare:

<compilation>
    <expressionBuilders>
        <add 
            
expressionPrefix="SqlResource" 
            
type="Elite.Utilities.Resources.SqlResourceExpressionBuilder, Elite.Utilities.Resources"/>
    <
/expressionBuilders>
<
/compilation>
 

Per creare un expression builder è necessario estendere l'omonima classe ExpressionBuilder implementando almeno un metodo. Tale metodo ha il compito di restituire una porzione di codice che reperisca la risorsa a runtime. In realtà questa è l'unica parte che presenta una certa difficoltà perchè occorre conoscere il CodeDom per generare uno spezzone di codice. Ecco nel riquadro un esempio tratto dal mio codice:

public override System.CodeDom.CodeExpression GetCodeExpression(
    System.Web.UI.BoundPropertyEntry entry, 
    
object 
parsedData, 
    ExpressionBuilderContext context)
{
    CodeMethodInvokeExpression invokeMethod = 
        
new 
CodeMethodInvokeExpression();

    invokeMethod.Method.TargetObject = 
        
new CodeTypeReferenceExpression(typeof
(SqlResource));
    invokeMethod.Method.MethodName = 
        "GetObject";
    invokeMethod.Parameters.Add(
        
new 
CodePrimitiveExpression(entry.Expression));

    
return 
invokeMethod;
}

Il codice così generato verrà inserito dal runtime di ASP.NET nella parte nascosta della partial class che rappresenta la pagina. Questo codice genererà una riga di codice come la seguente:

SqlResource.GetObject("hallo_world")

A questo punto è sufficiente scrivere questa classe con un metodo statico GetObject() che accetta la chiave della risorsa e lo preleva dal database. Il compilatore genererà il codice suddetto e lo inserirà nel flusso della pagina asp.net. Personalmente ho scelto di leggere in Cache una pagina intera di risorse sotto form di un DataTable, e poi se la cache è già presente usare la Select() per trovare il record che mi serve: Ecco come:

public static object GetObject(string key)
{
    
string 
path = HttpContext.Current.Request.Path;
    
string 
culture = Thread.CurrentThread.CurrentUICulture.Name;

    
// in particolare questo metodo preleva le risorse dal db
    
DataTable resources = GetCachedResources(path, culture);

    
if (resources != null
)
    {
        DataRow[] rows = 
            resources.Select(
                
string
.Format("ResourceKey='{0}'", key));

        
if 
(rows.Length > 0)
            
return 
rows[0]["ResourceValue"].ToString();
    }

    
return 
"resource not found";
}
 

In particolare il metodo GetCachedResources() non fa altro che cercare in cache la presenza del DataTable e nel caso in cui non lo reperisca in Cache ottiene i dati dal database e li memorizza nella medesima cache.

powered by IMHO 1.3