<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:copyright="http://blogs.law.harvard.edu/tech/rss" xmlns:image="http://purl.org/rss/1.0/modules/image/">
    <channel>
        <title>.NET Development</title>
        <link>http://blogs.ugidotnet.org/3854/category/.NET Development.aspx</link>
        <description>.NET Development</description>
        <language>it-IT</language>
        <copyright>Lorenzo Melato</copyright>
        <generator>Subtext Version 2.6.0.0</generator>
        <item>
            <title>Manning Publications sponsor del corso di RavenDB a Padova il prossimo 28/29 Ottobre</title>
            <link>http://blogs.ugidotnet.org/3854/archive/2013/10/09/manning-publications-sponsor-del-corso-di-ravendb-a-padova-il.aspx</link>
            <description>Dettagli qui:
http://ynnovahq.com/it/latest-news/it-manning-publications-sponsor-del-corso-di-ravendb-del-2829-ottobre-a-piove-di-sacco-pd/

C'è un ultimo posto disponibile per il corso. Chi si fa avanti e chiude il booking? ;-)
 &lt;img src="http://blogs.ugidotnet.org/3854/aggbug/101688.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Lorenzo Melato</dc:creator>
            <guid>http://blogs.ugidotnet.org/3854/archive/2013/10/09/manning-publications-sponsor-del-corso-di-ravendb-a-padova-il.aspx</guid>
            <pubDate>Wed, 09 Oct 2013 13:58:27 GMT</pubDate>
            <comments>http://blogs.ugidotnet.org/3854/archive/2013/10/09/manning-publications-sponsor-del-corso-di-ravendb-a-padova-il.aspx#feedback</comments>
            <slash:comments>1</slash:comments>
            <wfw:commentRss>http://blogs.ugidotnet.org/3854/comments/commentRss/101688.aspx</wfw:commentRss>
            <trackback:ping>http://blogs.ugidotnet.org/3854/services/trackbacks/101688.aspx</trackback:ping>
        </item>
        <item>
            <title>Pubblicata l'agenda del corso di RavenDB del prossimo Ottobre a Padova</title>
            <link>http://blogs.ugidotnet.org/3854/archive/2013/07/29/pubblicata-lagenda-del-corso-di-ravendb-del-prossimo-ottobre-a.aspx</link>
            <description>La trovate qui.

L.&lt;img src="http://blogs.ugidotnet.org/3854/aggbug/101627.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Lorenzo Melato</dc:creator>
            <guid>http://blogs.ugidotnet.org/3854/archive/2013/07/29/pubblicata-lagenda-del-corso-di-ravendb-del-prossimo-ottobre-a.aspx</guid>
            <pubDate>Mon, 29 Jul 2013 16:13:12 GMT</pubDate>
            <comments>http://blogs.ugidotnet.org/3854/archive/2013/07/29/pubblicata-lagenda-del-corso-di-ravendb-del-prossimo-ottobre-a.aspx#feedback</comments>
            <wfw:commentRss>http://blogs.ugidotnet.org/3854/comments/commentRss/101627.aspx</wfw:commentRss>
            <trackback:ping>http://blogs.ugidotnet.org/3854/services/trackbacks/101627.aspx</trackback:ping>
        </item>
        <item>
            <title>Chi sarebbe interessato ad un corso su RavenDB a Padova?</title>
            <link>http://blogs.ugidotnet.org/3854/archive/2013/07/25/chi-sarebbe-interessato-ad-un-corso-su-ravendb-a-padova.aspx</link>
            <description>In azienda stiamo cercando di organizzare un corso di due giorni su RavenDB a Padova, indicativamente nel corso del prossimo mese di Ottobre.
Il docente, d'eccezione, sarà un teacher ufficiale di RavenDB. 
Il corso sarà probabilmente orientato all'ultima versione, la 2.5, e partirà dalle basi per arrivare fino alle funzionalità più avanzate. 
C'è l'intenzione di riservare del tempo anche alle best practice per l'utilizzo con Windows Azure.

Chi fosse interessato mi può contattare attraverso il mio profilo linkedin o lasciando un commento qui sul blog. 

Il corso si farà se riusciremo a trovare almeno dieci persone interessate.

Ciao!&lt;img src="http://blogs.ugidotnet.org/3854/aggbug/101625.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Lorenzo Melato</dc:creator>
            <guid>http://blogs.ugidotnet.org/3854/archive/2013/07/25/chi-sarebbe-interessato-ad-un-corso-su-ravendb-a-padova.aspx</guid>
            <pubDate>Thu, 25 Jul 2013 10:06:32 GMT</pubDate>
            <comments>http://blogs.ugidotnet.org/3854/archive/2013/07/25/chi-sarebbe-interessato-ad-un-corso-su-ravendb-a-padova.aspx#feedback</comments>
            <slash:comments>1</slash:comments>
            <wfw:commentRss>http://blogs.ugidotnet.org/3854/comments/commentRss/101625.aspx</wfw:commentRss>
            <trackback:ping>http://blogs.ugidotnet.org/3854/services/trackbacks/101625.aspx</trackback:ping>
        </item>
        <item>
            <title>Il punto su Singleton</title>
            <link>http://blogs.ugidotnet.org/3854/archive/2007/07/08/84929.aspx</link>
            <description>Proprio in questi giorni mi è capitato di leggere un interessante articolo sull'integrazione di NHibernate con ASP.NET (che trovate qui). Analizzando il codice allegato all'articolo in questione mi è caduto l'occhio su una particolare implementazione del pattern Singleton in C#. Tra i commenti, l'autore citava un link ad un articolo che avevo già letto di sfuggita un po' di tempo fa al quale solo ora sono riuscito a dare il peso che merita. 
 Ricalco di seguito qualche concetto e qualche riga di approfondimento, giusto a pro memoria e per reference in italiano.
 In sostanza un Singleton è una classe che permette la creazione di una singola istanza di se stessa (requisito di resource management [1]) e fornisce un unico, semplice accesso pubblico a detta istanza (requisito di accessibilità [1]).
 Obiettivo del Singleton è spostare la responsabilità dell'esistenza di una ed una sola istanza di una determinata classe dallo sviluppatore alla classe stessa.
 Un tipico requisito di un Singleton è che l'istanza non sia creata finchè non ce n'è reale necessità, che sia cioè "lazy-created".
 Tutte le implementazioni del Singleton condividono alcune caratteristiche:
 
     Un unico costruttore, privato e senza parametri. Per non permettere ad altre classi di instanziarlo direttamente e per non permetterne l'uso come classe base in una gerarchia di ereditarietà. Tipicamente il Singleton è sealed, anche se non servirebbe, per il motivo che abbiamo appena citato, ma sembra che segnare la classe sealed aiuti il compilatore a ottimizzare meglio [2]; 
     Un campo statico che mantiene un riferimento alla singola istanza del Singleton; 
     Un metodo o una proprietà pubblici per ottenere il riferimento all'istanza del Singleton ed eventualmente crearla, se non ancora creata;
    
    Vediamo di seguito qualche implementazione del pattern Singleton con un occhio di riguardo alla "thread-safety" e alla "lazyness", come consigliato in [2].
     
    L'implementazione classica... non thread safe
     
    
       1:  public sealed class Singleton1
       2:  {
       3:      private static Singleton1 _instance = null;
       4:   
       5:      private Singleton1() { }
       6:   
       7:      public static Singleton1 Instance
       8:      {
       9:          get
      10:          {
      11:              if (_instance == null)
      12:              {
      13:                  _instance = new Singleton1();
      14:              }
      15:              return _instance;
      16:          }
      17:      }
      18:  }
     

     
    L'implementazione proposta è la classica descritta in [3], semplicemente tradotta da C++ a C#.
    La proprietà Instance alla riga 7 garantisce il requisito di accessibilità, è infatti l'unico punto di accesso pubblico all'istanza. Il codice all'interno della proprietà garantisce il requisito di resource management definito e cioè che esista una sola istanza della classe, il getter controlla infatti la preventiva esistenza dell'istanza e la crea solo ed esclusivamente se non è già stata creata, altrimenti restituisce l'istanza già esistente.
    
     
    In uno scenario single-thread il codice in questione funziona ed è perfettamente legale. In scenari multi-thread iniziano i problemi... questa implementazione non è thread safe, due o più thread potrebbero contemporaneamente valutare il test alla riga 11 come vero e creare più istanze della classe, violando il requisito di resource management.
    Rigirando la frittata, anche se a mio parere non ci sono forti motivazioni (ad esempio straordinari aumenti di performance) per un approccio del genere, nel tipico caso di Singleton immutabile e nel caso in cui la costruzione del Singleton sia poco onerosa, è possibile chiudere un occhio riguardo la violazione del requisito di resource management, lasciando al garbage collector l'onore e l'onere di eliminare una eventuale "altra istanza" lasciata orfana di riferimento dalla fortuita costruzione di una seconda istanza e utilizzare, quindi, questa implementazione.
     
    Un passo avanti, garantiamo la thread-safety
     
    
       1:  public sealed class Singleton2
       2:  {
       3:      private static Singleton2 _instance = null;
       4:      private static readonly Object syncObject = new Object();
       5:   
       6:      private Singleton2() { }
       7:   
       8:      public static Singleton2 Instance
       9:      {
      10:          get
      11:          {
      12:              lock (syncObject)
      13:              {
      14:                  if (_instance == null)
      15:                  {
      16:                      _instance = new Singleton2();
      17:                  }
      18:                  return _instance;
      19:              }
      20:          }
      21:      }
      22:  }
     
    
    L'implementazione è effettivamente thread safe, due o più thread non possono accedere contemporaneamente all'area protetta da lock quindi solo il primo thread che entrerà nel lock potrà creare l'istanza del singleton.
    Il codice soffre però un problema di performance, è infatti richiesto un lock per ogni richiesta dell'istanza, anche nel caso in cui l'istanza sia già stata creata.
     
    Thread safety, lock, performance, volatile e MemoryBarrier()
     
    
       1:  public sealed class Singleton3
       2:  {
       3:      private static volatile Singleton3 _instance = null;
       4:      private static readonly Object syncObject = new Object();
       5:   
       6:      private Singleton3() { }
       7:   
       8:      public static Singleton3 Instance
       9:      {
      10:          get
      11:          {
      12:              if (_instance == null)
      13:              {
      14:                  lock (syncObject)
      15:                  {
      16:                      if (_instance == null)
      17:                      {
      18:                          _instance = new Singleton3();
      19:                      }
      20:                  }
      21:              }
      22:              return _instance;
      23:          }
      24:      }
      25:  }
    
    A differenza dell'esempio precedente, il lock viene eseguito solo la prima volta che viene richiesta l'istanza. Infatti l'if più esterno (alla riga 12) serve proprio a verificare che l'istanza sia già stata creata ed in tal caso l'ingresso nel lock introdurrebbe solo un inutile overhead. Questa semplice modifica rispetto all'esempio precendente ha un chiaro impatto sulle performance, l'utilizzo di lock è utile in effetti solo nella fase iniziale in cui l'istanza non è ancora stata creata, quando cioè è indispensabile la sincronizzazione, e di conseguenza è un inutile spreco di tempo per tutto il resto del ciclo di vita del singleton in cui viene semplicemente restituita l'istanza già creata. 
    Merita particolare attenzione in questa implementazione l'utilizzo di volatile nella dichiarazione di _instance (riga 3)... ma prima di entrare nel merito forse vale la pena di ricordare le 3 regole fondamentali a cui i modelli di memoria (anche quello del .NET Framework) devono sottostare:
    
        Una lettura o una scrittura da un determinato thread ad una determinata locazione di memoria non può essere spostata temporalmente più avanti di una scrittura dallo stesso thread alla stessa locazione;
        Le letture non possono essere spostate temporalmente più indietro dell'acquisizione di un lock;
        Le scritture non possono essere spostate temporalmente più avanti del rilascio di un lock;
    
    Il modello di memoria del .NET Framework, oltre alle 3 regole citate, suddivide le tipologie di accesso alla memoria in "accessi ordinari" e "accessi volatile". Per gli accessi volatile sono definite le due regole seguenti:
    
        Letture e scritture non possono essere spostate temporalmente più indietro di una lettura volatile;
        Letture e scritture non possono essere spostate temporalmente più avanti di una scrittura volatile.
    
    Conseguenza di queste regole è, di fatto, l'inibizione del riordino degli accessi alla memoria, una particolare ottimizzazione applicata dai compilatori e dalle CPU non x86 per ottimizzare l'esecuzione del codice.
    Altra conseguenza è che viene forzato l'aggiornamento della variabile dichiarata volatile al valore più recente prima che questa venga applicata all'interno di un particolare contesto.
    Ora... cosa sarebbe potuto succedere se _instance non fosse stata dichiarata volatile?
    Dal punto di vista del rispetto dei requisiti di accessibilità e di resource management assolutamente niente. Infatti, il requisito di resource management è garantito dalla presenza del secondo check su _instance (riga 16) e dal rispetto da parte del memory model della seconda regola fondamentale. Si supponga di avere 2 thread:
    
        Il thread 1 passa il primo if (riga 12), l'esecuzione viene interrotta e passa al thread 2; 
        Il thread 2 passa il primo if, entra nel lock, passa il secondo if e crea l'istanza; 
        Il thread 1 riprende l'esecuzione ed entra nel lock. La seconda regola fondamentale dei modelli di memoria dice che: "le letture non possono essere spostate temporalmente più indietro dell'acquisizione di un lock" e di conseguenza che _instance avrà sicuramente, a questo punto, l'ultimo valore pubblicato dal thread 2 prima di rilasciare il lock. L'if alla riga 16 verificherà che _instance è diverso da null e il thread 1 proseguirà l'esecuzione restituendo l'istanza precedentemente creata da thread 2.
    
    In realtà, un problema subdolo si nasconde nel corpo dell'if alla riga 16 e precisamente nel codice che va a creare l'istanza di Singleton3... ed è proprio in questo caso che volatile ci viene in aiuto. Si supponga di avere il codice seguente:
     
    
       1:  public sealed class Singleton3
       2:  {
       3:      private static volatile Singleton3 _instance = null;
       4:      private static readonly Object syncObject = new Object();
       5:      private String _message;
       6:   
       7:      private Singleton3() { _message = "Hello World!"; }
       8:   
       9:      public static Singleton3 Instance
       10:      {
      11:          get
      12:          {
      13:              if (_instance == null)
      14:              {
      15:                  lock (syncObject)
      16:                  {
      17:                      if (_instance == null)
      18:                      {
      19:                          _instance = new Singleton3();
      20:                      }
      21:                  }
      22:              }
      23:              return _instance;
      24:          }
      25:      }
      26:  }
    
    Su architetture IA64, che permettono il riordino delle scritture in memoria, potrebbe accadere che, all'interno del corpo dell'if alla riga 19, le scritture vengano riordinate in maniera tale che la scrittura di _message all'interno del costruttore possa essere spostata temporalmente più avanti della scrittura di _instance. La lettura di _instance (alla riga 13) non è protetta da lock, potrebbe quindi accadere che un secondo thread possa accedere all'istanza di Singleton3 prima che il costruttore abbia concluso l'inizializzazione.
    L'utilizzo di volatile su _instance ci mette al riparo da questo problema in virtù della regola introdotta dal memory model del .NET Framework che dice: "Letture e scritture non possono essere spostate temporalmente più avanti di una scrittura volatile", garantendo, di conseguenza, la sequenzialità delle scritture nel blocco di costruzione dell'istanza, viene garantita la corretta inizializzazione di Singleton3 anche in scenari multi-thread.
    L'implementazione precedente è thread-safe, è lazy-load ed è performante... ma possiamo fare ancora un pochino di più!
    Quello che possiamo fare è rimuovere il modificatore volatile dalla dichiarazione di _instance e utilizzare una MemoryBarrier esplicita subito prima della pubblicazione dell'istanza di Singleton3 come nell'esempio seguente:
     
    
       1:  public sealed class Singleton3
       2:  {
       3:      private static Singleton3 _instance = null;
       4:      private static readonly Object syncObject = new Object();
       5:      private String _message;
       6:   
       7:      private Singleton3() { _message = "Hello World!"; }
       8:   
       9:      public static Singleton3 Instance
       10:      {
      11:          get
      12:          {
      13:              if (_instance == null)
      14:              {
      15:                  lock (syncObject)
      16:                  {
      17:                      if (_instance == null)
      18:                      {
      19:                          Singleton3 newValue = new Singleton3();
      20:                          System.Threading.Thread.MemoryBarrier();
      21:                          _instance = newValue;
      22:                      }
      23:                  }
      24:              }
      25:              return _instance;
      26:          }
      27:      }
      28:  }
    
    
    Questo approccio è molto più efficiente dell'ultilizzo del modificatore volatile perchè, in realtà, ogni scrittura o lettura volatile è una memory barrier! anche dove non ne abbiamo bisogno!
    Nel nostro caso l'obiettivo è soltanto avere la sicurezza che le scritture all'interno del costruttore vengano eseguite prima della pubblicazione dell'istanza agli altri thread (e processori). MemoryBarrier() ci assicura che "nessuna scrittura possa essere spostata temporalmente più avanti della chiamata a MemoryBarrier()" e ci permette quindi di garantire la consistenza dell'istanza risparmiando la dichiazione volatile di _instance.
    E se utilizziamo il .NET Framework 2.0?
    Con l'introduzione del .NET Framework 2.0 il modello di memoria è stato riveduto e corretto al fine si supportare gli sviluppatori nel passaggio alle nuove architetture IA64. Rimangono buone tutte le precedenti regole e alcune di nuove sono state introdotte tra cui una in particolare:
    
        Le scritture non possono essere spostate temporalmente più avanti di altre scritture dallo stesso thread. 
    
    Questa regola ci permette di evitare sia la dichiarazione volatile di _instance sia la chamata a MemoryBarrier(), risolvendo a livello di memory model la corretta serializzazione delle scritture del costruttore rispetto alla pubblicazione dell'istanza.
     
    Thread safe senza lock. Lazyness (con riserva)
    
    
       1:  public sealed class Singleton4
       2:  {
       3:      private static readonly Singleton4 _instance = new Singleton4();
       4:   
       5:      private static Singleton4() { }
       6:   
       7:      private Singleton4() { }
       8:   
       9:      public static Singleton4 Instance
      10:      {
      11:          get
      12:          {
      13:              return _instance;
      14:          }
      15:      }
      16:  }
    
    
    Il singleton dell'esempio qui sopra è thread safe e lazy-created (con riserva).
    La lazyness è supportata da una funzionalità del CLR che garantisce che il type initializer venga eseguito "prima" dell'accesso ad un qualsiasi membro statico della classe. Nel nostro caso, subito prima del primo accesso alla proprietà statica Instance, il type initializer verrà eseguito e il campo statico _instance verrà inizializzato con una istanza di Singleton4. Il type initializer verrà eseguito però anche nel caso si acceda ad un qualsiasi altro campo o proprietà statica denifito nella classe violando (da qui la riserva), di fatto, la lazyness di Instance... Attenzione! potrebbe non essere il comportamento desiderato in fase di design.
    Altra importante questione riguardante la lazyness di Singleton4 è la presenza del costruttore statico (riga 5, costruttore esplicito). La presenza del costruttore esplicito garantisce che la classe non venga decorata con il flag beforefieldinit in fase di compilazione. Il flag beforefiledinit abilita il runtime ad ottimizzare l'esecuzione del type initializer scegliendo autonomamente il momento migliore per eseguirlo, garantendone comunque l'esecuzione prima dell'accesso ad un qualunque campo statico della classe. Noi non abbiamo quindi modo di determinare precisamente il momento in cui il type initializer viene eseguito, di conseguenza la lazyness è violata. Senza il flag beforefieldinit il runtime DEVE eseguire il type initializer immediatamente prima dell'accesso ad un qualunque campo statico della classe [6].
    La thread safety è supportata da una specifica del CLR che garantisce il fatto che il type initializer venga eseguito una ed una sola volta, anche in ambienti multithreaded. Il runtime, infatti, acquisisce un lock PRIMA di eseguire il type initializer, rendendo l'inizializzazione di _instance sincronizzata [6].
     
    Thread safe senza lock. Lazy-created (garantito)
     
    
       1:  class Singleton5
       2:  {
       3:      private Singleton5() { }
       4:   
       5:      public static Singleton5 Instance
       6:      {
       7:          get
       8:          {
       9:              return Nested._instance;
      10:          }
      11:      }
      12:   
      13:      class Nested
      14:      {
      15:          static Nested() { }
      16:   
      17:          internal static readonly Singleton5 _instance = new Singleton5();
      18:      }
      19:  }
    
    Il singleton dell'esempio qui sopra è thread safe (vedi le stesse motivazioni del precedente esempio) e lazy-created in ogni circostanza.
    La lazyness è infatti garantita dal fatto che il type initializer di _instance viene eseguito solo ed esclusivamente prima del primo accesso ai membri statici della classe Nested (che può essere fatto solo dallo stesso Singleton5), costruita solo per questo scopo e invisibile all'esterno di Singleton5. L'accesso ad una qualsiasi altro membro statico di Singleton5 non influenza l'inizializzazione di _instance.
     
    Note
    Nel caso di decida di utilizzare l'implementazione Singleton4 o Singleton5 bisogna fare particolarmente attenzione alla particolare gestione delle eccezioni dei type initializer statici:
    
        Qualsiasi eccezione sollevata all'interno del costruttore statico viene automaticamente wrappata da una eccezione di tipo TypeInitializationException, l'eccezione originale è comunque disponibile attraverso la proprietà InnerException di TypeInitializationException;  
        Il runtime, nel caso venga sollevata una eccezione nel costruttore statico, non tenterà di invocare una seconda volta il costruttore statico, rendendo, probabilmente, la stessa eccezione fatale per l'intera applicazione [6].
    
     
    Potete scaricare i sorgenti d'esempio del post qui.
     
    Bibliografia
    [1] Adrian Florea - L'individuazione via reflection delle classi singleton all'interno del Framework .NET
    [2] Jon Skeet - Implementing the Singleton Pattern in C#
    [3] Gamma, Helm, Johnson, Vlissides - Design Patters, Elementi per il riuso di software a oggetti
    [4] Alessandro Marotta Rizzo - Implementare Singleton con Volatile
    [5] Eric Gunnerson - A programmer's introduction to C# 2.0
    [6] K. Scott Allen -  Get a Charge From Statics with Seven Essential Programming Tips
    [7] Microsoft -  ECMA C# and Common Language Infrastructure Standards
    [8] Vance Morrison -  Understand the Impact of Low-Lock Techniques in Multithreaded Apps
 
Technorati Tag: design patterns,gof,singleton,c#&lt;img src="http://blogs.ugidotnet.org/3854/aggbug/84929.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Lorenzo Melato</dc:creator>
            <guid>http://blogs.ugidotnet.org/3854/archive/2007/07/08/84929.aspx</guid>
            <pubDate>Sun, 08 Jul 2007 13:15:00 GMT</pubDate>
            <comments>http://blogs.ugidotnet.org/3854/archive/2007/07/08/84929.aspx#feedback</comments>
            <slash:comments>26</slash:comments>
            <wfw:commentRss>http://blogs.ugidotnet.org/3854/comments/commentRss/84929.aspx</wfw:commentRss>
            <trackback:ping>http://blogs.ugidotnet.org/3854/services/trackbacks/84929.aspx</trackback:ping>
        </item>
        <item>
            <title>Visualizzare linee guida nell'editor di testo di Visual studio</title>
            <link>http://blogs.ugidotnet.org/3854/archive/2007/07/05/84755.aspx</link>
            <description> Interessante funzionalità di Visual Studio 2002, 2003 e 2005 della quale non ero a conoscenza, ma molto molto comoda... E' possibile visualizzare una o più guideline nell'editor di testo di visual studio aggiungendo una semplice stringa ad una chiave di registro, chissà poi perchè non l'hanno messa tra le opzioni dell'editor... Aprite regedit e navigate alla chiave seguente: [HKEY_CURRENT_USER]\Software\Microsoft\VisualStudio\8.0\Text Editor create ora una nuova stringa REG_SZ e chiamatela Guides, doppio click sulla stringa appena creata e aggiungete: RGB(230,230,230) 80, 120 con RGB(...) si sceglie il colore della o delle linee. 80 e 120 aggiungono 2 guidelines rispettivamente alla colonna 80 e 120 dell'editor di testo. Un consiglio, scegliete un colore molto chiaro (se lo sfondo è bianco) o comunque un colore che non dia troppo fastidio.
Technorati Tag: visual studio&lt;img src="http://blogs.ugidotnet.org/3854/aggbug/84755.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Lorenzo Melato</dc:creator>
            <guid>http://blogs.ugidotnet.org/3854/archive/2007/07/05/84755.aspx</guid>
            <pubDate>Thu, 05 Jul 2007 20:52:00 GMT</pubDate>
            <comments>http://blogs.ugidotnet.org/3854/archive/2007/07/05/84755.aspx#feedback</comments>
            <slash:comments>1</slash:comments>
            <wfw:commentRss>http://blogs.ugidotnet.org/3854/comments/commentRss/84755.aspx</wfw:commentRss>
            <trackback:ping>http://blogs.ugidotnet.org/3854/services/trackbacks/84755.aspx</trackback:ping>
        </item>
        <item>
            <title>URL rewriting, questo sconosciuto</title>
            <link>http://blogs.ugidotnet.org/3854/archive/2006/03/08/36373.aspx</link>
            <description>
In questo articolo http://www.ugidotnet.org/articles/articles_read.aspx?ID=112&amp;nbsp;il 
buon Andrea&amp;nbsp;Boschin&amp;nbsp;affronta 
con estrema lucidità ed efficacia il problema dell' URL rewriting e il Sitemap 
provider di ASP.NET.
Da leggere.

powered by IMHO 1.2
&lt;img src="http://blogs.ugidotnet.org/3854/aggbug/36373.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Lorenzo Melato</dc:creator>
            <guid>http://blogs.ugidotnet.org/3854/archive/2006/03/08/36373.aspx</guid>
            <pubDate>Wed, 08 Mar 2006 15:20:00 GMT</pubDate>
            <comments>http://blogs.ugidotnet.org/3854/archive/2006/03/08/36373.aspx#feedback</comments>
            <slash:comments>1</slash:comments>
            <wfw:commentRss>http://blogs.ugidotnet.org/3854/comments/commentRss/36373.aspx</wfw:commentRss>
            <trackback:ping>http://blogs.ugidotnet.org/3854/services/trackbacks/36373.aspx</trackback:ping>
        </item>
    </channel>
</rss>