Java meglio di .NET ?

Il titolo di questo post è volutamente ironico, ed affronta un argomento all’ordine del giorno in ogni ambiente di lavoro e/o community in cui si ha a che fare con le tecnologia legate al mondo del software.
In questi ambienti capita spesso infatti di sentire o di partecipare a discussioni circa il fatto che quella o quell’altra tecnologia prevale a parità di contesto di utilizzo, es. Java è meglio di .NET,  Oracle è meglio di Sql Server, o viceversa.
Questi dibattiti, per quello che è la mia esperienza sul campo, sono spesso e volentieri condizionati dalla unilaterale conoscenza di una particolare tecnologia, e raramente colui che l’appoggia ha una consolidata esperienza su entrambe le piattaforme.
Ho assistito molto spesso a discorsi tipo:

questo pezzo di codice se fosse scritto in Java non avrebbe quel bug…“,

oppure

se questa query fosse in PL-SQL le prestazioni sarebbero sen’altro migliori“.

In questo interessantissimo post di Scott Guthrie in merito all’ultimo dibattito tecnologico in essere tra gli sviluppatori web (meglio ASP .NET Web Forms o ASP .NET MVC ?), è sintetizzato il mio pensiero sull’argomento:

“Great developers using bad tools/frameworks can make great apps. Bad developers using great tools/frameworks can make bad apps. Be very careful about making broad assumptions (good or bad) about the quality of the app you are building based on the tools/frameworks used”.

Sono le persone che partecipano allo sviluppo a rendere vincente o perdente un software, a prescindere dalla particolare tecnologia applicata, ed è un grave errore pensare che solo perchè si è scelto di utilizzare l’ultima moda tecnologica un progetto software debba per forza essere vincente.

Entity Framework – Come impostare una relazione

Con Entity Framework è possibile referenziare tra loro entità  in modo molto semplice.

Supponendo di avere l’entità  Customer e l’entità Category, che rappresentano rispettivamente un cliente e la sua categoria di appartenenza, nel data model l’oggetto Customer conterrà  una proprietà  chiamata Category di tipo Category.

In fase di creazione di un nuovo oggetto Customer è necessario associare la Category di appartenenza scelta dall’utente, molto probabilmente mediante una dropdown list contenente la lista delle categorie (DataTextField), e l’Id delle stesse (DataValueField).

Istintivamente, verrebbe di fare una cosa di questo tipo:

che però non funziona in quanto solleva una eccezione del tipo “An entity object cannot be referenced by multiple instances of IEntityChangeTracker”.

Per poter funzionare la reference ha bisogno esclusivamente dell’Id della Categoria di appartenenza del Cliente, e non dell’intero oggetto Category, anche perchè per ricrearlo interamente potrebbe essere necessario accedere al database di memorizzazione.

Occorre semplicemente creare un oggetto EntityKey ed associarlo all’oggetto Customer corrispondente, in questo modo:

ASP .NET 4.0 #3 Ciò che non è cambiato

ASP .NET 4.0 è ormai alle porte, con la versione beta è possibile scoprire le novità  rispetto alla versione precedente, e non sono certamente poche, ma a livello di controlli lato server ce ne sono alcuni praticamente immutati rispetto alle precedenti versioni. Mi riferisco ad esempio al controllo Http File Upload, rimasto identico nelle varie versioni di ASP .NET che si sono succedute. Questo controllo soffre di qualche problema e non è certo il massimo in ottica web 2.0, ovvero su siti dove è richiesto una elevata user experience.

A meno di non utilizzare un controllo di terze parti probabilmente a pagamento, occorre fare i conti con il look del controllo rimasto identico nel tempo, con l’assenza di funzionalità  oggi richieste quali ad esempio la barra di progressione dell’upload in corso, e soprattutto con un fastidioso comportamento “by design”, ovvero la perdita del contenuto (il nome completo di percorso del file scelto) ad ogni postback della pagina; quest’ultima caratteristica è resa necessaria da motivi di sicurezza.

Se la pagina dispone di altri controlli che generano un postback, es. una dropdownlist, l’unico escamotage è quello di rendere il controllo File Upload l’ultimo controllo che genera postback in ordine di visualizzazione, in modo da invogliare l’utente ad interagire per ultimo con esso. In caso contrario, un postback della pagina provocherà  la perdita del suo contenuto, cioè del nome del file scelto, ed ovviamente obbligherà  l’utente a scegliere nuovamente il file da inviare.

L’evento page load è eseguito 2 volte

Convertire un progetto ASP .NET dalla versione 1.1 ad una versione successiva del .NET Framework nasconde un inconveniente a cui occorre porre rimedio manualmente.

L’inconveniente è dovuto alla introduzione delle partial class a partire dalla versione 2.0 del .NET Framework, in contrapposizione al codice generato dal designer nella versione 1.1.

Questo fa si che importando il codice sorgente nella nuova versione utilizzata ci si ritrovi, ad esempio, con un event handler come questo nel metodo InitializeComponent

 private void InitializeComponent() 
 { 
     this.Load += new System.EventHandler(this.Page_Load); 
 }

Questo innocente codice derivante dalla conversione fa in modo che l’evento Page Load sia generato 2 volte, una volta dall’handler presente nella partial class ed una volta da quello presente nel metodo InitalizeComponent.

Per eliminare questo fastidioso inconveniente è sufficiente rimuovere l’handler presente nel metodo InitializeComponent

</xplayn.org>

Questo blog cambia nome, veste grafica e motore di blogging. Causa perdurata inattività, ho deciso di chiudere il sito xplayn.org, da me aperto insieme all’amico Francesco Quaratino.

Il nuovo nome, coding4art.com è ovviamente un omaggio alla programmazione software vista come un’arte o più precisamente un’attività ingegneristica e non artigianale. Si parla tanto di metodologie applicate alla produzione software, Agile, waterfall o MSF che sia, ma spesso e volentieri questa viene vista come un’attività artigianale, espletata senza o quasi nessuna metodologia, da programmatori senza esperienza specifica nella particolare tecnologia scelta, senza nessuna cura per il codice sorgente, senza nessuna best practice o guidelines applicata o peggio ancora con varie best practice mischiate tra loro, senza nessuna pianificazione riguardante argomenti come sicurezza, performance, scalabilità, manutenibilità ma con un unico obiettivo in testa, ovvero creare nel più breve tempo possibile una applicazione in cui i requisiti utente siano soddisfatti, ovvero che funzioni, e basta, con le conseguenze che questo approccio comporta.

I miei articoli presenti su xplayn.org li ho inseriti nella apposita sezione articoli su questo blog, dove conto di scriverne altri, tempo permettendo ovviamente.

Come nuovo motore di blogging ho scelto SubText, ben fatto e molto veloce, parecchio più veloce di Community Server, a mio avviso, con una fantastica funzionalità già inclusa, ovvero la possibilità di importare un intero blog (commenti compresi) precedentemente esportato, il tutto mediante lo standard BlogML.

L’RSS feed è comunque invariato, poichè utilizzo il feed esterno fornito da FeedBurner e quindi il blog è accessibile negli RSS Aggregator senza nessuna modifica.

Questo post vuol essere anche una promessa a me stesso di curare di più il mio blog, strumento in cui credo moltissimo, e di arricchirlo anche con più articoli tecnici.

Utilizzo “sicuro” di stringhe in .NET 2.0

La classe System.String non rappresenta la soluzione più sicura quando è necessario memorizzare sotto forma di stringa contenuti confidenziali, ad esempio stringhe di connessione a database, password varie, numeri di carta di credito, ecc.. Informazioni di questo genere andrebbero mantenute in memoria solo il tempo strettamente necessario alla loro elaborazione, e la memoria occupata andrebbe rilasciata il più presto possibile. Questo accorgimento è necessario poichè il contenuto della memoria utilizzata da un processo in esecuzione potrebbe essere letta attraverso varie tecniche di hacking, e quindi il suo eventuale contenuto confidenziale correrebbe seri pericoli di manomissione. Inoltre, un processo in esecuzione potrebbe utilizzare un file di swap su disco che provocherebbe la scrittura anche di questo genere di informazioni.

[Perchè una stringa è insicura]
La classe System.String non si adatta affatto a queste caratteristiche. Infatti, una stringa è un oggetto immutabile, per cui ogni modifica ad essa comporta la creazione di una nuova stringa memorizzata in un’area di memoria differente, mentre la vecchia stringa continuerà  ad esistere fino alla successiva operazione di garbage collection, che, a causa della sua natura non deterministica, potrebbe avvenire ben più tardi rispetto al momento in cui tutti i riferimenti ad essa sono rilasciati.
L’immutabilità  delle stringhe costituisce un serio problema se una stringa contiene informazioni confidenziali per almeno 3 motivi:
– Ogni manipolazione effettuata su di essa lascia in memoria una copia della stessa, in attesa del garbage collector;
– Non esiste alcun modo per resettare il contenuto della stringa una volta utilizzato; il tentativo di assegnare un valore nullo provocherà  la creazione di un nuovo oggetto stringa con il nuovo contenuto lasciando invariato il vecchio;
– L’indirizzo di memoria in cui è memorizzata una stringa non è costante una volta creata;
– Non è possibile criptare il contenuto di una stringa senza provocare la creazione di un nuovo oggetto con il contenuto criptato;
[La soluzione in .NET 1.0/1.1]
Nella versione 1.0/1.1 del Microsoft .NET Framework non esiste una soluzione semplice a questo problema. Infatti l’unica strada percorribile è quella di criptare/decriptare manualmente il contenuto di una stringa confidenziale utilizzando un array di caratteri come area di lavoro. Non è invece possibile resettare una stringa senza attendere che il garbage collector agisca.

[La soluzione in .NET 2.0]
La versione 2.0 del .NET Framework pone rimedio a tutti i problemi di sicurezza precedentemente elencati attraverso la nuova classe SecureString presente nel namespace System.Security. Questa nuova classe permette di memorizzare in modo sicuro e criptato una stringa contenente contenuto confidenziale, utilizzando DPAPI (Data Protection API), ovvero una serie di API per il criptaggio ed il decriptaggio di informazioni mediante l’algoritmo Triple-DES.

Ecco un esempio di utilizzo:

public SecureString SetSecurePassword(string password)
{
   SecureString ss = new SecureString();
   foreach (char c in password.ToCharArray())
   {
         ss.AppendChar(c);
   }
        
   return ss;
}

Come si può; notare, l’utilizzo di SecureString è un pò più macchinoso rispetto alla creazione di un semplice oggetto stringa; infatti esso richiede che la stringa sicura sia costruita aggiungendo un carattere alla volta attraverso il metodo AppendChar(). Il motivo di questo utilizzo dovrebbe essere ovvio: non è possibile costruire una stringa sicura a partire da una stringa già in memoria in testo in chiaro, poichè quest’ultima sarebbe immutabile con gli identici problemi di sicurezza visti precedentemente. Inoltre, una istanza di SecureString è mutabile, il suo indirizzo di memoria non cambia una volta creata e può quindi facilmente essere azzerata attraverso il metodo Clear() senza attendere il garbage collector. Oltre al metodo AppendChar() esistono altri metodi per modificare il contenuto, ad es. InsertAt(), RemoveAt(), SetAt(), il cui nome è abbastanza intuitivo per comprendere il loro utilizzo. La proprietà MakeReadOnly blocca il contenuto dell’oggetto SecureString non permettendo più alcuna modifica. Questa caratteristica può essere letta attraverso la proprietà  IsReadOnly. Inoltre, la classe SecureString implementa l’interfaccia IDisposable, ovvero permette al suo utilizzatore di distruggere l’istanza in memoria senza attendere il garbage collector.

Dopo aver creato e popolato una istanza della classe SecureString occorre leggere il valore in essa contenuto. Questa operazione non può essere fatta richiamando semplicemente il metodo ToString() della classe, in quanto quest’ultimo non è stato ridefinito e ritorna semplicemente l’oggetto sotto forma di stringa così come ereditato da System.Object, vale a dire System.Security.SecureString, non di grande utilità , e non è semplicissima in quanto comporta una operazione di marshalling (attraverso l’utilizzo della classe Marshal presente nel namespace System.Runtime.InteropServices) che coinvolge l’utilizzo di un puntatore alla stringa, anche se il tutto si risolve con poche righe di codice, come in questo esempio:

 public string GetPassword(SecureString ss)
 {
     IntPtr bstr = Marshal.SecureStringToBSTR(ss);
   
     try
     {
        password = Marshal.PtrToStringUni(bstr);
     }
     finally
     {
         Marshal.ZeroFreeBSTR(bstr);
     }
   
     return password;
}

Una volta ottenuta la controparte immutabile della stringa, è necessario assicurarsi che l’area di memoria puntata dal puntatore sia azzerata attraverso l’utilizzo del metodo ZeroFreeBSTR della classe Marshal.
[Riferimenti]
[1] Maurizio Tammacco – Pinned object in .NET
[1] Maurizio Tammacco – Uso efficiente delle stringhe in .NET

Utilizzare codice unsafe in C#

Il linguaggio C# permette di scrivere codice cosiddetto unsafe, cioè eseguito al di fuori del controllo del CLR (Common Language Runtime).In questo ambito il programmatore può accedere, seppure con le dovute limitazioni rispetto a linguaggi più specifici quali C/C++, direttamente alla memoria attraverso l’uso dei puntatori. Un puntatore è una particolare variabile il cui contenuto è un indirizzo di memoria. In un sistema a 32 bit quindi un puntatore occupa 4 byte.

L’utilizzo diretto della memoria offre vantaggi e svantaggi. Da un lato è possibile superare i controlli imposti dal CLR e dal compilatore C#, e questo permette di scrivere routine altamente ottimizzate e generalmente più efficienti; dall’altro canto però la programmazione unsafe è senz’altro più ostica e complessa della stesura di normale codice gestito, sia per la particolare sintassi a cui bisogna attenersi, sia perchè è facilissimo commettere errori che a questo livello risultano fatali per l’applicazione e per l’intero sistema.

Come utilizzare il codice unsafe

Prima di eseguire codice unsafe è necessario utilizzare l’omonimo modificatore, il quale può essere associato ad una intera classe, ad uno o più metodi, ad una o più variabili membro, oppure ad un singolo blocco di codice all’interno di un metodo.

Nel seguente esempio vengono mostrati tutti i possibili usi:

// Qualsiasi metodo della classe può usare i puntatori
public unsafe class MyClass {}
// I membri (tipi valore) della classe possono essere puntatori, anche con
// diversi livelli di visibilità (private, public, protected)
public class MyClass2 
{
   private unsafe float* pFloat;
   public unsafe int* px2;
   protected unsafe int* px3;

  
// Il metodo può usare i puntatori in qualsiasi punto al suo interno
public unsafe void Method1() {}
 
public void Method2() 
{
   int a=0;
   unsafe
   {
      // E' possibile usare i puntatori all'interno di questo blocco
   }
}

Non è invece possible applicare la parola chiave unsafe ad una variabile privata a livello di metodo. Il seguente codice, infatti, non compilerà :

 public void Method3() 
 {
   // errore di compilazione
   unsafe int* a=0;
}

Tuttavia, pur definendo correttamente un contesto unsafe contenente l’opportuno modificatore, il compilatore C# genererà  comunque un errore di compilazione. Come passo ulteriore, è richiesto il parametro /unsafe da associare alla esecuzione della compilazione (comando csc.exe), oppure, se si utilizza l’ambiente Visual Studio 2003/2005, basta attivare l’opportuno check nella finestra delle proprietà  del progetto.

Un aspetto importantissimo da considerare è che in un contesto unsafe  è possibile utilizzare puntatori solo a tipi valore (tutti i tipi numerici primitivi, datetime e strutture), i quali sono memorizzati all’interno dello stack; non è ammesso quindi l’utilizzo di un puntatore ad un tipo riferimento (tutte le classi del .NET Framework più quelle definite dall’utente che derivano direttamente o indirettamente da System.Object, memorizzate invece nell’heap gestito). Questa limitazione è dovuta al fatto che i tipi riferimento sono appunto memorizzati in una area di memoria chiamata heap costantemente monitorata dal meccanismo del garbage collector e su cui esso agisce per liberare la memoria occupata da oggetti non più referenziati. Risulta evidente che se il programmatore avesse la possibilità  di manipolare l’indirizzo di memoria di un tipo riferimento, il garbage collector non avrebbe più la possibilità di tenerne traccia per poter rilasciare la memoria utilizzata quando il riferimento non è più puntato da nessuna variabile.

Utilizzo dei puntatori in pratica.

Le seguenti righe di codice dichiarano due puntatori rispettivamente ad un intero 32 bit ed ad un float:

int* pintValue;.
float* pfloatValue;

Il seguente snippet code evidenzia invece alcune delle operazioni possibili con i puntatori:

Si dichiarano 2 puntatori ad interi (righe 1 e 2).

Si dichiara una variabile intera assegnandole il valore 20 (riga 3).

Successivamente si assegna l’indirizzo di memoria di intValue al puntatore pintValue attraverso operatore “&” (riga 4). Questo operatore permette appunto di ricavare l’indirizzo di memoria di una variabile e di convertirlo in un puntatore.

Nella riga 5 al puntatore pintValue2 viene assegnato il contenuto del puntatore pintValue.

Successivamente si modifica il valore puntato dal puntatore pintValue2 assegnandogli il valore 50 (riga 6) mediante l’operatore “*”, il quale ha l’effetto opposto rispetto all’operatore “&”, ovvero converte un puntatore verso un tipo di dato a valore.

La riga 7 stampa su console lo spazio di memoria occupato da un intero a 32 bit. Questo valore si ottiene attraverso l’operatore sizeof che accetta un tipo valore come parametro e restituisce la sua occupazione in memoria. Le righe 8 e 10 stampano l’indirizzo ed il valore dei 2 puntatori utilizzati in questo esempio. Per poter stampare l’indirizzo di memoria memorizzato in un puntatore è necessario prima convertire quest’ultimo in un tipo di dato numerico abbastanza ampio da poterlo rappresentare (in questo caso un intero senza segno) attraverso una operazione di cast esplicito.

L’output mostrato a video evidenzia come il primo puntatore (int* pintValue) contenga il valore 50 (il valore assegnato al secondo puntatore int* pintValue2). Questo risultato dimostra l’utilizzo dei puntatori come semplici indirizzi di memoria; infatti, nella riga 6 è stato effettuato un assegnamento dell’indirizzo puntato da pintValue alla variabile pintValue2, e, poichè trattasi di puntatori, ciò che è stato effettivamente assegnato non è il valore di pintValue ma l’indirizzo di memoria. Ne consegue che dopo l’istruzione visualizzata alla riga 6 entrambi i puntatori puntano alla stessa area di memoria per cui qualsiasi modifica al contenuto puntato da uno dei due si riflette anche sull’altro.

Aritmetica dei puntatori

Ad un puntatore è possibile sommare o sottrarre un valore intero. Tuttavia il risultato di questa operazione non è immediatamente intuitivo. Il compilatore, infatti, applica sempre la seguente formula quando esegue una operazione di somma (o sottrazione) su un puntatore:

dove X rappresenta un indirizzo di memoria, ovvero un puntatore, n un valore intero da sommare, T il tipo valore a cui il puntatore si riferisce.

Esempio:

dato un puntatore ad un valore double:

che punta al seguente indirizzo di memoria (valore decimale): 1201550

se sommiamo 1 a tale puntatore attraverso questa istruzione:

il risultato sarà  che il puntatore pDouble conterrà  l’indirizzo di memoria 1201558, ovvero all’indirizzo iniziale sono stati aggiunti 8 byte, vale a dire l’ampiezza in memoria di una variabile di tipo double.

Se avessimo aggiunto il valore intero 3, in questo modo:

l’indirizzo di memoria sarebbe cambiato in 1201574, cioè sarebbero stati aggiunti 24 byte (l’equivalente di 3 valori double) al valore iniziale.

Analogamente, in una operazione di sottrazione i bytes sarebbero sottratti dal valore iniziale.

Da ciò si evince una regola importantissima: se si effettua una operazione di somma o sottrazione su un puntatore di un certo tipo valore, il puntatore risultante punterà  ad una area di memoria contigua in funzione del numero di byte che servono a rappresentare il suddetto tipo. Questa operazione andrebbe effettuata con molta cautela, in quanto non si ha nessuna informazione circa il contenuto dell’area di memoria puntata in seguito alla operazione matematica. Non è affatto garantito, infatti, che essa non contenga alcun dato; al contrario, potrebbe contenere informazioni fondamentali per il corretto funzionamento del processo in esecuzione, ad esempio l’indirizzo di ritorno del metodo corrente. Se queste informazioni venissero sovrascritte da un puntatore sicuramente si otterrebbe un crash del sistema.

Puntatori a strutture

Una struttura è un tipo valore in quanto è memorizzata nello stack esattamente come i tipi numerici primitivi. Quindi è possibile definire un puntatore ad una struttura, ma essa non potrà contenere alcun tipo riferimento, esempio una stringa, in quanto ciò provocherebbe, come già menzionato, un errato funzionamento del garbage collector.

Quindi, disponendo di una struttura come da esempio:

public struct Article
{
   public int code;
   public float price;
   public short foo;
}

è possibile utilizzare i puntatori in questo modo:

(Riga 1) E’ dichiarato un puntatore ad una struttura contenente un valore intero a 32 bit, un float ed un intero a 16 bit (short).

(Riga 2) E’ stampato l’indirizzo a cui punta il puntatore alla struttura e l’ampiezza in memoria della stessa. Quest’ultimo valore è pari a 12 byte, e non coincide con la somma delle ampiezze dei singoli campi della struttura. Infatti il tipo Int32 occupa 4 byte, il tipo float occupa 4 byte ed il tipo short 2, per un totale di 10 byte, e tuttavia lo spazio allocato per la struttura è pari a 12 byte. Questo comportamento è normale su un processore a 32 bit dove la memoria è suddivisa in blocchi da 4 byte poichè tale processore lavora in modo più efficiente quando opera su blocchi di memoria di 4 byte, meglio conosciuto nel sistema Windows come DWORD. Il .NET Framework, quindi, alloca memoria in blocchi di 4 byte anche se la memoria indispensabile per ogni tipo a valore è inferiore.

(Righe 3, 4 5) E’ creata una istanza della struttura Article con l’inizializzazione dei suoi campi

(Riga 6) Il puntatore pArticleStruct punta all’ istanza della struttura Article appena creata

(Righe 7, 8) Attraverso il puntatore pArticleStruct sono letti i valori dei singoli campi della struttura e memorizzati in variabili

(Righe 9, 10) Attraverso il puntatore pArticleStruct sono scritti nuovi valori dei singoli campi della scrittura

(Righe 11, 12) Sono creati puntatori ai singoli campi della struttura Article

(Righe 13, 14) Queste righe sono equivalenti alle righe 12, 13; mostrano quindi una sintassi alternativa per creare dei puntatori ai campi interni di una struttura

(Riga 15) E’ stampato il contenuto dei campi della struttura letti attraverso variabili di appoggio

(Riga 16) E’ stampato il contenuto dei campi della struttura letti attraverso i rispettivi puntatori

(Righe 17, 18, 19, 20) Sono stampati gli indirizzi memorizzati nelle variabili di tipo puntatore

Puntatori a campi di una classe

E’ stato detto che non è possibile creare un puntatore ad un tipo riferimento, almeno utilizzando il linguaggio C#, ma solo ad un tipo valore, in quanto si comprometterebbe il corretto funzionamento del garbage collector. Ad esempio, la compattazione della memoria effettuata dopo una operazione di pulizia non potrebbe più aver luogo se il codice avesse la capacità  di manipolare gli indirizzi di memoria. Tuttavia, una classe può contenere membri di tipo valore, ad esempio un campo pubblico di tipo double. Il compilatore C# permette di definire un puntatore ad un campo di una classe, ma, se si applicasse la stessa logica vista per le strutture, non si avrebbe il risultato desiderato.

Ad esempio, considerando l’esempio Article come una classe:

public class ArticleClass
{
   public int code;
   public float price;
}

compilando il seguente codice:

ArticleClass ArticleObj=new ArticleClass();
ArticleObj.code=5;
ArticleObj.price=50.78f;
int* pIntOfClass=&(ArticleObj.code);
float* pFloatOfClass=&(ArticleObj.price);

si otterrebbe il seguente errore di compilazione:

You can only take the address of an unfixed expression inside of a fixed statement initializer

Questo comportamento è dovuto al meccanismo del garbage collector che potrebbe spostare spostare il riferimento ArticleObj in una zona di memoria diversa da quella in cui è stato creato. Per evitare quindi che a seguito di ciò il riferimento non sia più valido il compilatore C# impedisce di utilizzare puntatori a tipi valore membri di una classe, a meno di non utilizzare la parola chiave fixed che, come facilmente intuibile, obbliga il garbage collector a non spostare il riferimento ad ArticleObj in un’altra zona di memoria perchè è molto probabile che ci siano puntatori ai suoi campi.

Il seguente esempio mostra il suo utilizzo:

 fixed (int* pIntOfClass=&(ArticleObj.code))
 {
   Console.WriteLine("Code: {0}", *pIntOfClass);
 }
   
 fixed (float* pFloatOfClass=&(ArticleObj.price))
 {
   Console.WriteLine("Price: {0}", *pFloatOfClass);
 }

Uso di un array basato sui puntatori

Attraverso l’utilizzo dei puntatori è possibile gestire array ad alte prestazioni basati sullo stack. Un array è rappresentato da una istanza della classe System.Array, e, per tale motivo, è memorizzato nell’heap gestito.

Quindi, se si prova a creare un array di tipi double (cioè un tipo valore) in un metodo, ciò che sarà  memorizzato nello stack è semplicemente il riferimento all’heap gestito che conterrà  i dati veri e propri. Se si ha la necessità  di manipolare un array nello stack in modo da guadagnare in termini di performance è necessario ricorrere ad un puntatore.

Nell’esempio che segue:

double[] doubleArray=new double[20];

E’ dichiarato un array di double di 20 elementi memorizzati nell’heap gestito.

In questo esempio, al contrario, è dichiarato un array di 20 elementi di tipo double memorizzati nello stack:

double* pDoubleArray=stackalloc double[20];

Come si può notare occorre utilizzare la parola chiave stackalloc per la dichiarazione dell’array, seguita dal tipo di dato e dall’ampiezza dell’area di memoria da allocare. Dopo tale dichiarazione la variabile pDoubleArray punterà  al primo elemento dello stesso, quindi la seguente istruzione assegna il valore 10.37 al primo elemento dell’array:

 *pDoubleArray=10.37;

Sfruttando l’aritmetica dei puntatori è possibile accedere ad uno qualsiasi degli elementi di un array in questo modo:

*(pDoubleArray+10)=20.45;

Tuttavia, è possibile utilizzare una sintassi più semplice:

pDoubleArray[10]=20.45;

Il compilatore C# riconosce che la variabile pDoubleArray è un puntatore ad un array di double e permette di accedere all’elemento con l’indice indicato tra parentesi quadre. Quindi le 2 righe di codice viste in precedenza sono equivalenti.

Sfruttando questo meccanismo il seguente blocco di codice crea un array di 20 elementi di tipo intero basato sullo stack, ed assegna ad ogni elemento un numero casuale compreso tra 1 e 100. Successivamente il contenuto dell’array è stampato a video:

Occorre però tenere in considerazione un aspetto importantissimo quando si utilizzano i puntatori per accedere agli elementi di un array. Nell'esempio precedente è stata allocata sufficiente memoria per memorizzare un array di venti interi a 32 bit utilizzando la keyword stackalloc. Se si tentasse di accedere ad un elemento fuori dal range ammesso utilizzando l'array creato in questo modo, ad esempio:

pintArray[50]=10;

non si otterrebbe alcun errore di run-time ma molto probabilmente si comprometterebbe la stabilità  del sistema in quanto l'applicazione avrebbe accesso ad una area di memoria esterna rispetto a quella occupata dall'array di 20 elementi. Se si facesse la stessa operazione su un normale array creato a partire da una istanza di System.Array, il CLR solleverebbe l'eccezione System.IndexOutOfRangeException, preservando in tal modo l'integrità del sistema.

Conclusioni

In questo articolo abbiamo visto come poter accedere direttamente alle memoria utilizzando il linguaggio C#, e come questa operazione possa incrementare le performance di routine in cui la velocità rappresenta un aspetto critico. Analogamente si è visto che questa possibilità  andrebbe usata con molta cautela e solo quando realmente necessario poichè il codice unsafe non è eseguito nel contesto di sicurezza imposto dal CLR, risultando potenzialmente pericoloso per l'integrità  dell'applicazione e dell'intero sistema.

Righe casuali in SQL Server 2000

Ordinare le righe di una tabella di Sql Server in modo casuale, cioè con ordinamento variabile di volta in volta, non è certamente una operazione di ogni giorno e, a prima vista, può anche sembrare una operazione complessa. In realtà è abbastanza semplice se ci si affida alla funzione NEWID() che ritorna un GUID univoco in assoluto; pertanto la seguente query:

T-SQL

select * from orders order by newid()

restituirà un resultset ordinato ogni volta in modo differente.

Basandoci su questo esempio è anche possibile estrarre una riga diversa ogni volta che si esegue lo statement:

T-SQL

select top 1 * from orders order by newid()

Quest’ultima istruzione andrebbe usata però con cautela su tabelle di grandi dimensioni, in quanto prima di restituire  la riga casuale l’engine SQL legge tutte le righe della tabella, per ognuna di esse calcola il GUID, ed infine ordina le stesse, come si evince dando uno sguardo al piano di esecuzione.

Manipolare i campi identity in T-SQL

Spesso può essere utile verificare il valore identity associato ad una colonna di una tabella di database ed eventualmente modificarlo, impostandolo chiaramente ad un valore almeno uguale al valore più alto del campo. Questa operazione è  utile soprattutto quando si popola una tabella con un campo di questo tipo con informazioni di test, che successivamente sono eliminate, ed è quindi utile azzerare il valore identity per farlo ripartire da 1.

Il comando T-SQL per manipolare il campo identity è DBCC CHECKIDENT.

Alcuni esempi:

T-SQL

-- visualizza il valore corrente del campo identity di <nome tabella
DBCC CHECKIDENT (<nome tabella>, NORESEED)  
  
-- verifica il valore corrente del campo identity di <nome tabella> e, 
-- se errato, lo corregge
DBCC CHECKIDENT (<nome tabella>) 
  
-- imposta a 50 il valore corrente del campo identity di <nome tabella>
DBCC CHECKIDENT (<nome tabella>, RESEED, 50) 

Uso efficiente delle stringhe in .NET

L’architettura Microsoft .NET Framework considera una stringa come un “oggetto immutabile”. Ciò significa che una volta assegnato un valore la stessa non potrà mai più mutare il suo contenuto. Questo comporta che qualsiasi manipolazione effettuata sul contenuto di una stringa una volta che è stata creata produrrà sempre una nuova stringa, ovvero la stringa originaria risulterà raggiungibile dal successivo garbage collector, e una nuova stringa sarà creata e conterrà il risultato della manipolazione. Il seguente pezzo di codice:

C#

string s="SONO UNA STRINGA"; s=s.ToLower(); 

a) crea una stringa; b) la manipola trasformando tutti i caratteri in essa contenuti in minuscolo; c) assegna il risultato alla medesima variabile. La variabile “s” dopo la manipolazione punterà ad un indirizzo diverso rispetto a quello puntato al momento della creazione e dell’inizializzazione del valore. In pratica conterrà un’altro oggetto rispetto a quello originario, appunto perchè l’immutabilità delle stringhe comporta che una volta creato ed inizializzato un oggetto, lo stesso non potrà più mutare per il restante tempo in cui rimarrà in vita. Questa caratteristica presenta vantaggi e svantaggi, ma nel complesso offre un accettabile compromesso per quanto concerne l’efficienza dell’utilizzo della memoria e le prestazioni che ne derivano. Il vantaggio principale dell’immutabilità di una stringa consiste nell’ottimizzazione di operazioni “time-consuming” come la copia, soprattutto nel caso di stringhe grandi. Infatti, poichè è certo che il contenuto di una stringa non muterà mai, una copia del suo contenuto si traduce in una semplice copia dell’indirizzo di memoria a cui essa punta, operazione molto più veloce ed efficiente della copia dell’intero contenuto. Tuttavia, un uso intenso di operazioni di manipolazione di stringhe comporta a lungo andare un degrado delle prestazioni, a causa delle continue operazioni di allocazione e deallocazione di aree di memoria dovuto appunto alla immutabilità della stringhe. Per attenuare questo possibile degrado di prestazioni il .NET Framework mette a disposizione il cosiddetto “intern pool”, ovvero un buffer di memoria in cui è possibile memorizzare stringhe di uso frequente, il cui ciclo di vita coincide con quello dell’intera applicazione. In questo modo le stringhe memorizzate, sono sempre disponibili e non sono mai raggiunte dal garbage collector. Il metodo statico Intern della classe System.String ricerca e memorizza una stringa nel pool in un’ unica operazione. Infatti esso accetta un parametro di tipo stringa che rappresenta la stringa da ricercare; se è presente nel pool il metodo restituisce un riferimento ad essa; se non presente viene aggiunta una nuova stringa e restituito il riferimento, come da esempio:

C#

string s=System.String.Intern("una stringa"); 

Inoltre, operazioni comunissime come la concatenazione producono sempre una stringa temporanea che contiene il risultato della elaborazione che sarà successivamente assegnato alla stringa di risultato. Il seguente semplicissimo codice:

C#

string str="sono una"; string str2=" stringa"; str=str+str2;

Genera una stringa temporanea che conterrà il risultato della concatenazione che sarà assegnato ad un oggetto diverso da quello puntato dalla variabile “str”. Questa operazione di concatenazione, che coinvolge valori non letterali è eseguita a run-time, a differenza della concatenazione di valori letterali eseguita invece durante la compilazione. Per ridurre l’impatto della immutabilità delle stringhe su applicazioni che effettuano parecchie concatenazioni, soprattutto con stringhe abbastanza grandi, è possibile utilizzare l’oggetto StringBuilder, una sorta di buffer preallocato che può essere usato per contenere stringhe concatenate senza la necessità di ulteriori allocazioni di memoria. Una versione di overload del costruttore dell’oggetto StringBuilder accetta un valore intero indicante l’ampiezza della memoria preallocata, il cui valore di default è pari a 16. Fino all’ampiezza massima del buffer preallocato è possibile concatenare stringhe senza ulteriori allocazioni. Oltre questo valore viene allocata nuova memoria, la cui capacità è il doppio dell’ampiezza del vecchio buffer; quest’ultimo è copiato nel nuovo buffer e risulta raggiungibile da una operazione di garbage collection. Infine, le seguenti tecniche possono ridurre la possibilità di scrivere codice inefficiente durante la concetenazione di stringhe: -usare il metodo String.Concat per concatenare una espressione stringa. L’utilizzo di questo metodo non genera una o più stringhe temporanee come invece accade se si usa l’operatore “+”; -usare l’operatore “+” per concatenare stringhe che contengono valori letterali; -usare l’oggetto StringBuilder per concatenare stringhe durante operazioni in cui il numero di concatenazioni non è noto a priori, esempio durante un loop.

Pinned object in .NET

E’ noto che il meccanismo del Garbage Collector del .NET Framework, a cui ho dedicato un articolo apparso sulla rivista Visual Basic Journal, compatta l’ heap dopo aver effettuato la pulizia ed il rilascio della memoria occupata dagli oggetti non più referenziati dall’applicazione. Questa compattazione dispone in modo contiguo gli oggetti ancora in vita per un più efficiente utilizzo della memoria, e chiaramente per tutti gli oggetti spostati vengono aggiornati i rispettivi puntatori a causa del nuovo indirizzo di memoria a cui punta l’oggetto; inoltre, per evitare lunghe operazioni di spostamento, gli oggetti troppo grandi (oltre 85K) non sono spostati. Quindi, a seguito di un GC, un oggetto ancora valido può puntare ad una locazione di memoria diversa rispetto alla locazione che lo stesso oggetto possedeva prima della operazione di GC. Questa situazione comporta problemi se l’oggetto in questione viene passato attraverso il suo indirizzo ad una DLL unmanaged attraverso P/Invoke. Infatti, se il GC cambiasse la locazione di memoria dell’oggetto a seguito di una compattazione dopo la chiamata alla DLL, l’indirizzo passato alla DLL non sarebbe più valido e la DLL non avrebbe alcun modo per accorgersene, con conseguenze facilmente immaginabili. Per ovviare a questo problema è possibile ricorrere alla struttura GCHandle, attraverso cui è possibile creare i cosiddetti oggetti “pinned”, ovvero oggetti non rilocabili in memoria e quindi passabili come parametro a DLL unmanaged. Tuttavia, sono gli oggetti “blittable” possono essere gestiti come pinned (un oggetto blittable è tale se la sua rappresentazione in memoria è la stessa nel codice gestito e nel codice non gestito, es. valori integer, double, ecc; non lo è in caso contrario). Questa struttura permette inoltre di conoscere l’indirizzo in cui un oggetto “pinned” è memorizzato. Se questo valore venisse passato ad un componente unmanaged, si andrebbe incontro al rischio di corruzione della memoria in quanto il componente non gestito avrebbe (teoricamente) la possibilità di scrivere nella memoria puntata dall’indirizzo passato. In questo esempio viene creato un array di interi, reso “pinned” l’oggetto corrispondente e letto il suo indirizzo di memoria.

C#

int[] arrInt = new int[10]; 
arrInt[0]=1; 
arrInt[1]=2; 
arrInt[2]=3; 
System.Runtime.InteropServices.GCHandle GCH = 
System.Runtime.InteropServices.GCHandle.Alloc(
        arrInt, System.Runtime.InteropServices.GCHandleType.Pinned); 
IntPtr a = GCH.AddrOfPinnedObject();

Accedere al registro di Windows utilizzando T-SQL

Sql Server prevede una serie di stored procedure di sistema per svolgere funzioni di amministrazione del server, gestione della posta elettronica, esecuzione di comandi di sistema, etc., alcune delle quali anche non documentate, e che possono tornare utili in diverse occasioni. Poichè si tratta di una applicazione basata su COM utilizza il registro di sistema per memorizzare informazioni vitali per il suo funzionamento. Attraverso una store procedure non documentata chiamata xp_regread è possibile leggere il valore di una qualsiasi chiave di registro, utilizzando il tal modo solo codice T-SQL. Lo script seguente legge il contenuto della chiave utilizzata da Sql Server per memorizzare il percorso fisico di installazione.

T-SQL

declare @sqlpath varchar(200)
exec master..xp_regread @root='HKEY_LOCAL_MACHINE', 
     @key='SOFTWARE\MICROSOFT\MICROSOFT SQL SERVER\80\TOOLS\CLIENTSETUP', 
     @value_name='SqlPath', @value=@sqlpath OUT 

Il parametro di output @value conterrà il contenuto della chiave chiamata SqlPath situata nel percorso ‘SOFTWARE\MICROSOFT\MICROSOFT SQL SERVER\80\TOOLS\CLIENTSETUP’

T-SQL

print @sqlpath 

Esecuzione di codice managed come script

La classe System.CodeDom.Compiler.CodeDomProvider è una classe astratta dalla quale derivano classi specifiche utilizzate dal .NET Framework per implementare i compilatori dei vari linguaggi da esso supportati. Il compilatore del linguaggio C#, ad esempio, è basato sulla classe Microsoft.CSharp.CSharpCodeProvider che eredita dalla classe astratta CodeDomProvider e permette di accedere ad una istanza del compilatore C# per la gestione del codice sorgente in detto linguaggio, così come il compilatore del linguaggio Visual Basic .NET è basato sulla classe Microsoft.VisualBasic.VBCodeProvider. Utilizzando tali classi è possibile compilare il codice sorgente, proveniente ad esempio da un file di script, per generare ed eseguire codice managed direttamente in memoria oppure creando un assembly. Ciò si rileva molto utile quando è necessario provare il funzionamento di porzioni di codice sorgente senza dover creare una soluzione da salvare su disco, come ad esempio accade se si utilizza l’ambiente Visual Studio .NET, oppure per fornire ad una applicazione la possibilità di integrazioni software da implementare a livello di scripting. Il primo passaggio consiste nel creare una istanza del compilatore C# oppure VB .NET rispettivamente attraverso la classe CSharpCodeProvider e VBCodeProvider, e di ottenere quindi un oggetto ICodeCompiler che rappresenta una interfaccia la cui implementazione da parte dello specifico compilatore permette di compilare dinamicamente il codice sorgente. E’ opportuno inoltre creare una istanza della classe CompilerParameters per impostare le opzioni di compilazione appropriate.

C#

VB .NET

Alcune di queste opzioni permettono ad esempio di generare un eseguibile oppure una dll, di includere i simboli di debug, di creare un assembly in memoria o di produrre un file su disco (in questo caso è possibile assegnare un nome specifico al file attraverso la proprietà OutputAssembly). Una proprietà importante della classe CompilersParameters è ReferencedAssemblies, ovvero un oggetto di tipo collezione contenente la lista degli assemblies impostati come riferimento dell’assembly in compilazione. Occorre prevedere almeno gli assembly di uso più comune, altrimenti l’utilizzo di una classe senza il riferimento all’assembly che la contiene produce un errore.

C#

VB .NET

A questo punto è possibile leggere il codice sorgente da compilare, tipicamente da un file di testo, e costruire l’entry point dell’assembly insieme al metodo Main che sarà invocato dinamicamente:

C#

VB .NET

Per compilare il codice è necessario invocare il metodo CompileAssemblyFromSource esposto dall'interfaccia ICodeCompiler passandogli l'oggetto CompParameters creato in precedenza e la stringa contenente il codice. Questo metodo restituisce un oggetto CompilerResults che permette di verificare se la compilazione ha prodotto errori o avvertimenti mediante la collection Errors, la quale espone proprietà per individuare la riga e la colonna contenente l'istruzione che ha prodotto l'errore, il numero dell'errore stesso e la sua descrizione, oltre alla proprietà HasErrors che consente immediatamente di verificare se è presente almeno un errore:

C#

VB .NET

Nel caso di compilazione terminata correttamente si effettua un ciclo sui tipi che appartengono all'assembly appena compilato e si ricava un riferimento al tipo "scripter" che costituisce l'entry point dell'assembly e, mediante l'uso di Reflection, si invoca dinamicamente il metodo Main:

C#

VB.NET

Infine, è possibile registrare nel sistema l'applicazione impostando una particolare estensione di file, ad esempio "cssc" per indicare uno script in linguaggio C#, oppure "vbsc" per uno script in Visual Basic .NET. In tal modo attraverso un semplice doppio click sul file con tale estensione sarà eseguito il codice in esso contenuto, in modo simile ad un file di script Visual Basic.

Usare matrici con base diversa da zero in .NET

I linguaggi .NET permettono di definire matrici con un limite inferiore, ovvero l’indice dell’elemento iniziale della matrice stessa, che è sempre uguale a zero. Infatti essi non permettono di gestire array con base personalizzata, ovvero diversa da zero. La seguente istruzione VB .NET dichiara una variabile matrice contenente 11 elementi di tipo stringa, il cui limite inferiore è sempre e solo zero ed il cui limite superiore è 10:

Dim s(10) As String 

Questa caratteristica è ben nota a chi sviluppa in C++/C#, poichè in questi linguaggi un array ha sempre base zero e non è permesso definire basi definite dal programmatore. Al contrario, coloro che hanno scritto programmi utilizzando precedenti versioni del linguaggio Visual Basic, ad esempio Visual Basic 6.0, hanno invece avuto la possibilità di utilizzare matrici con limite inferiore diverso da zero, impostando questo valore nella dichiarazione dell’array. Questa non è affatto una caratteristica del VB6 a cui non si può assolutamente rinunciare, ma è semplicemente una facilitazione per chi sviluppa. Come riportato nel seguente esempio di codice VB6, indicando una matrice di 10 elementi con limite inferiore uguale a 1 e limite superiore uguale a 10, il numero degli elementi della stessa è uguale al suo limite superiore presente nella dichiarazione. Viceversa, nei linguaggi .NET una matrice con limite superiore uguale a 10 contiene in realtà 11 elementi, poichè il limite inferiore parte da zero.

Dim s(1 To 10) As String 

Tuttavia, utilizzare matrici con base personalizzata è una tecnica che potrebbe compromettere l’affidabilità di un’applicazione generando errori anche piuttosto difficili da individuare, soprattutto quando alla stessa ci lavora un team di sviluppo composto da più programmatori. Il CLS (Common Language Specification), ovvero le specifiche tecniche a cui devono obbligatoriamente conformarsi tutti i linguaggi .NET, stabiliscono che una matrice può avere solo un limite inferiore pari a zero, e questo chiaramente per non compromettere l’interoperabilità tra i vari linguaggi. Nonostante le specifiche del CLS, la classe System.Array propria del .NET Framework permette al programmatore di definire un limite inferiore personalizzato per le matrici, e quindi di creare codice non compatibile con il CLS stesso. System.Array costituisce la classe di base utilizzata dai vari linguaggi del .NET Framework per fornire l’implementazione delle matrici utilizzando i costrutti che il linguaggio specifico mette a disposizione. Essa espone metodi shared che consentono di creare un array di elementi e di gestire l’assegnazione dei valori e la lettura degli stessi. Questo approccio richiede la scrittura di codice leggermente più prolisso, ma consente una maggiore flessibilità nell’uso degli array, come appunto creare una matrice con una base diversa da zero. Occorre sempre ricordare però che quest’approccio genera codice non compatibile con le specifiche CLS, e quindi un componente contenente codice di questo tipo potrebbe non essere utilizzato da codice che invece utilizza la sintassi dei linguaggi .NET per creare e gestire gli array. Per ottenere questo risultato è necessario innanzitutto dichiarare una variabile di tipo System.Array, come nel seguente esempio:

Dim Arr As System.Array 

A questo punto è possibile creare una istanza della classe System.Array utilizzando il metodo shared Createnstance. Questo metodo ha una versione di overloading che permette di creare array anche multidimensionali passando 3 parametri, e precisamente un riferimento al tipo di oggetto che dovrà essere contenuto in ogni elemento dell’array, un intero che specifica le dimensioni dell’array da creare, ed ancora un intero che indica il limite inferiore dell’array stesso. Gli ultimi 2 parametri sono array monodimensionali, e ciò significa in pratica che è possibile creare una istanza di un array multidimensionale, ed indicare per ognuna delle sue dimensioni sia la lunghezza che il limite inferiore. Il seguente esempio crea un array di stringhe monodimensionale la cui lunghezza è uguale a 10 ed il cui limite inferiore è uguale a 2:

Dim Lengths as Integer() = {10} 
Dim LowerBounds as Integer() = {2} 
Arr=Array.CreateInstance(GetType(String),Lengths,LowerBounds) 

Dopo aver creato una istanza di un array è possibile popolarlo con dati e in seguito leggerli. Per far questo occorre utilizzare rispettivamente i metodi di istanza SetValue e GetValue della classe System.Array. Il seguente esempio mostra come utilizzarli effettuando un ciclo sull’array appena creato dall’indice zero fino al suo limite superiore. Poichè l’array è stato creato con indice inferiore uguale a 2 e non zero, i primi due cicli generano una eccezione di tipo IndexOutOfRangeException, dimostrando di fatto che il .NET Framework supporta array con base personalizzata, al contrario dei suoi linguaggi di programmazione.

 Dim I as Integer 
 For I = 0 To Arr.GetUpperBound(0) 
 Try 
     Arr.SetValue("Stringa alla posizione " & CStr(I), I) 
     System.Console.WriteLine(Arr.GetValue(I)) 
     Catch ex As IndexOutOfRangeException 
         System.Console.WriteLine(ex.Message) 
End Try 
Next 

Inoltre, se si invoca il metodo System.Array.GetLowerBound(0), il quale ritorna il limite inferiore della dimensione dell’array specificata come parametro, si otterrà il valore 2, che corrisponde al limite inferiore personalizzato impostato come parametro del metodo CreateInstance, come nel seguente frammento di codice:

System.Console.Writeline(Arr.GetLowerBound(0)) 

La classe System.Array è dichiarata utilizzando la parola chiave MustInherit. Questo potrebbe far pensare che è possibile creare una propria classe che eredita da System.Array, e che gestisce funzionalità aggiuntive o ridefinisce i membri della classe base. Sfortunatamente se si tenta di ereditare una propria classe da System.Array si ottiene un errore di compilazione il cui messaggio è “Non è consentito ereditare da System.Array”, poiché solo i linguaggi di programmazione, e quindi i compilatori, possono ereditare da questa classe per fornire i vari costrutti di gestione.

Link utili della settimana #3

Doloto, time optimizers for Ajax applications

AutoMapper, framework per mappare automaticamente e quindi creare un oggetto semplice, es. un DTO, mediante un oggetto complesso come una entità di dominio

WPF tutorial;

Microsoft Ajax Library (Preview 6) e Microsoft Ajax Minifier ( Microsoft Ajax Javascript Library -> nuova e migliorata sintassi imperativa, miglior integrazione con JQuery, nuovo client script loader, Minifier Tool, per ridurre le dimensioni dei file javascript scaricati sul client mediante minification)

Link utili della settimana #2

 

  1. Descrizione di Unity, il framework di Inversion of Control e Dipendency Injection sviluppato dal team di P&P (qui è presente la versione 1.2 per Silverlight)
  2. 45 passi per diventare agili
  3. xVal, libreria per la validazione client side di applicazioni ASP .NET MVC
  4. Anti XSS Library ver. 3.1
  5. CodePad .NET 1.0 Beta 2, un editor leggero per provare spezzoni di codice
  6. Strongly typed session variables in ASP .NET MVC
  7. Visual Studio 2010 e .NET Framework 4.0 Training Kit – May preview

Link utili della settimana #1

MVVM Toolkit Light

Ajax Control Toolkit release 30930 (30 sett 2009);

thinktecture.WSCF.blue (Contract first WCF Tool)

– The ModelView-ViewModel design pattern for WPF

Code Run (questo è davvero incredibile, si evincono chiaramente le potenzialità del Cloud Computing).  Trattasi di un IDE Online, con cui è possibile sviluppare progetti .NET direttamente nel browser, potendo anche disporre di un ambiente di host on the cloud, su cui è possibile effettuare il deployment delle proprie applicazioni sviluppate on line.

Bonnie .NET API per sviluppare applicazioni che usano la crittografia della informazioni o certificati X.509

ASP .Net e i thread secondari

Interessantissimo post di Stefano Pronti del nuovo blog MSDN di Supporto Tecnico agli Sviluppatori, che spiega le disastrose conseguenze di non richiamare il metodo Dispose su risorse unmanaged, utilizzate all’interno di una web application.

Per farla breve, le risorse unmanaged utilizzavano un thread secondario rispetto a quello che prende in carico la web request, ed in questo thread secondario veniva sollevata una eccezione non gestita durante la fase di finalizzazione del garbage collector, che, come è noto, viene eseguito in un thread diverso.

In questo caso il comportamento di ASP .NET a partire dalla versione 2.0 è quello di interrompere immediatamente il processo in esecuzione, con conseguenze facilmente immaginabili.

Ho già parlato qui di questo comportamento di ASP .NET e di come sia possibile utilizzare la modalità pre-versione 2.0 di gestione delle eccezioni non gestite sollevate all’interno di thread diversi.

Anche a me è capitato di dover “impazzire” con una applicazione in produzione, abbastanza vasta, che soffriva di frequenti ed improvvise cadute della sessione corrente, con enorme disagio degli utenti.

Nel caso specifico non è stato indispensabile attaccare un debugger per ottenere il dump della memoria al momento dell’eccezione, è bastato debuggare il codice, che non conoscevo neanche bene, e scoprire che venivano creati thread aggiuntivi (!?) il cui codice, in particolari circostanze, sollevava l’eccezione fatale che provocava il riavvio del worker process.