Monday, October 27, 2008

PostSharp: CompileTimeValidate()

Se vado avanti così potrei diventare PostSharp addicted… già sono compiler addicted conclamato, sono sulla buona strada per diventare Unit Testing addicted e dopo aver provato la compile time validation di PostSharp posso asserire che si sta chiudendo il cerchio.
Un paio di giorni fa abbiamo visto come usare PostSharp per validare a runtime i parametri di un metodo (per inciso funziona anche con i costruttori) facendo semplicemente una cosa del genere:
class MyTestClass
{
    public void Foo( [NotNull]String arg )
    {
        Console.WriteLine( "Foo( String ): " + arg );
    }
}
e producendo a runtime una ArgumentNullException se chiamiamo Foo() passando un valore null. Adesso immaginiamo di scrivere una cosa del genere:
class MyTestClass
{
    public void Foo( [NotNull, Int32RangeValidator( CompareLogic = CompareLogicOperator.Greater, ReferenceValue = 0) ]Int32 arg )
    {
        Console.WriteLine( "Foo( Int32 ): " + arg );
    }
}
abbiamo un overload di Foo() che prende un intero e vogliamo che questo sia maggiore di 0 ma erroneamente, perchè Int32 è un value type, vogliamo anche che non sia null… compiliamo e:
image
Lasciatemi dire: semplicemente fantastico!
Il tutto è stato di una semplicità disarmante:
[Serializable]
public class ArgumentValidatorAspect : OnMethodBoundaryAspect
{
    public override bool CompileTimeValidate( MethodBase method )
    {
        Boolean returnValue = true;

        method.GetParameters()
                .Where( pi => pi.IsAttributeDefined<NotNullAttribute>() )
                .ForEach( pi =>
                    {
                        if( pi.ParameterType.IsValueType )
                        {
                            Message error = new Message( SeverityType.Error,
"RV0001",
"Cannot apply NotNullAttribute to a ValueType.",
method.Name ); MessageSource.MessageSink.Write( error ); returnValue = false; } } ); return returnValue; }
}
PostSharp ha un motore di validazione che viene invocato a compile time e che ci permette di controllare, parzialmente, il processo di build eventualmente interrompendolo se non sono soddisfatti determinati requisiti, come nel nostro caso.
Questo apre a scenari notevoli, dimentichiamoci per un momento di tutto quello che PostSharp può fare a runtime e soffermiamoci solo sulla fase a compile time: la manna del programmatore sono gli errori del compilatore avere la possibilità di controllare il processo di build anche attraverso attributi custom/aspect è semplicemente potentissimo.
.m

Sunday, October 26, 2008

AOP, C# e PostSharp… Update

Mi riallaccio al post di ieri per fare una precisazione, faccio un nuovo post perchè fare un update dell’altro è pressochè impossibile… sopportatemi ;-)
Ieri ho detto che: “…quello che però non ha fatto è stato rimettere sul parametro del metodo privato l’attributo, e questo credo sia un bug, vedremo…;
Riferendomi a questa situazione:
[ArgumentValidatorAspect()]
class MyTestClass
{
    public void Foo( [NotNull]String arg )
    {
        Console.WriteLine( "Foo:" + arg );
    }
}
e compilando il risultato che otteniamo, visto con il fido Reflector, è questo:
 image_thumb8
cioè l’attributo NotNullAtrtribute è rimasto sul metodo pubblico e non si è “spostato” su quello privato, nel nostro caso questo è un fastidio obbligandoci a fare giri strani per recuperare gli attributi applicati ai parametri.
Orbene, è possibile ottenere il comportamento desiderato istruendo opportunamente il post compilatore di PostSharp:
[assembly: PostSharp.Extensibility.ImplementationBoundAttribute( typeof( NotNullAttribute ) )]
In questo modo stiamo dicendo a PostSharp che vogliamo che il nostro attributo venga “spostato” e legato all’implementazione, ottenendo di fatto questo:
image
che è esattamente quello che ci serve.
Adesso abbiamo un altro fastidio… :-D se cerchiamo dall’esterno di analizzare la classe MyTestClass non sapremo mai che uno dei parametri del metodo Foo() è marcato con l’attributo NotNull questo perchè a tutti gli effetti non lo è più. Ho quindi chiesto all’autore se è possibile avere un comportamento misto avendo cioè l’attributo da entrambe le parti, vediamo che ci dice…
Buona domenica,
.m

Saturday, October 25, 2008

AOP, C# e PostSharp

è da un po’ di tempo che mi riprometto di fare delle prove e capire come funziona il gioiellino.
Diciamo che sono partito con una necessità decisamente triviale ma che illustra bene le potenzialità della cosa. Lo scopo è far funzionare questo codice:
class MyTestClass
{
    public void Foo( [NotNull]String arg )
    {
        Console.WriteLine( "Foo:" + arg );
    }
}
e fare in modo di ottenere una ArgumentNullException( ‘arg’ ) se il metodo Foo() viene invocato così:
MyTestClass obj = new MyTestClass();
obj.Foo( null );
Nello specifico NotNullAttribute è un normalissimo attributo .net, senza nulla di particolare:
[AttributeUsage( AttributeTargets.Parameter )]
public class NotNullAttribute : Attribute
{
}
questo è un tipico scenario in cui AOP (Aspect Oriented Programming) ci può essere di grandissimo aiuto, una implementazione dei paradigmi AOP per .net è formita proprio da PostSharp; partiamo dal codice che è più semplice:
[Serializable]
public class ArgumentValidatorAspect : OnMethodInvocationAspect
{
    public override void OnInvocation( MethodInvocationEventArgs context )
    {
        context.Proceed();
    }
}
definiamo una nostra classe che deriva da OnMethodInvocationAspect, classe definita nel framework di PostSharp, e ci limitiamo a fare l’override del metodo OnInvocation().
Due cose sono degne di nota:
  1. la nostra classe è a tutti gli effetti un attributo: OnMethodInvocationAspect infatti alla lontana deriva da Attribute;
  2. la nostra classe deve essere marcata con l’attributo Serializable, vedremo di seguito il motivo, e questo comporta tutta una serie di possibili problematiche e di considerazioni che devono essere fatte quando si sviluppa un “Aspect” con PostSharp;
Fatto ciò possiamo fare una cosa molto interessante:
[ArgumentValidatorAspect()]
class MyTestClass
{
    public void Foo( [NotNull]String arg )
    {
        Console.WriteLine( "Foo:" + arg );
    }
}
possiamo decorare la nostra classe di test con l’attributo appena definito. Possiamo quindi realizzare una semplice applicazione console che testa il tutto:
class Program
{
    static void Main( string[] args )
    {
        MyTestClass tc = new MyTestClass();
        tc.Foo( "Hello World!" );
    }
}
Una volta che abbiamo configurato Visual Studio per “usare” PostSharp, vedremo dopo cosa comporta, possiamo compilare il nostro semplicissimo progetto e premere F5 quello che otteniamo è esattamente quello che ci asspettiamo:
image
Se però siamo curiosi e andiamo ad indagare con Reflector che cosa contiene il nostro assembly scopriamo delle cose decisamente interessanti:
image
La nostra classe infatti contiene qualcosa che non ci aspetteremmo di trovare: contiene il metodo pubblico Foo() che abbiamo definito noi e contiene anche un metodo privato ~Foo(), se andiamo a vedere il corpo dei metodi scopriamo che il nostro codice sta nel metodo privato e non in quello pubblico:
 image
Che cosa è successo? la cosa è abbastanza semplice:
  • il primo step è quello di configurare Visual Studio aggiungendo al progetto i target di MSBuild di PostSharp: l’help di PostSharp è decisamente esauastivo nei passaggi da seguire;
  • Quando lanciate una build al termine della compilazione vengono invocati i task di PostSharp che in questo caso non fanno altro che:
    • disassemblare l’assembly prodotto;
    • andare alla ricerca dei tipi marcati con l’attributo “ArgumentValidatorAspect”;
    • modificare il codice dei tipi in modo che le chiamate vengano redirette all’aspect;
    • istanziare il vostro aspect e serializzarlo, ecco perchè è necessario che sia Serializable, e infine salvare l’aspect serializzato nelle risorse;
    • rigenerare l’assembly e completare la build;
A runtime succede che quando invocate il metodo Foo() in realtà state invocando il nuovo metodo Foo() che, utilizzando l’aspect deserializzato, è in grado di redirigere la chiamata prima all’aspect e poi dall’aspect verso il vero metodo ~Foo():
  1. Foo();
  2. Aspect:
    1. Aspect –> Proceed();
  3. ~Foo();
A questo punto penso che anche a voi si sia accesa la lampadine e abbiate detto: mumble mumble… ma se definiamo il nostro attributo così:
[AttributeUsage( AttributeTargets.Parameter )]
public class NotNullAttribute : Attribute
{
    public void Validate( ParameterInfo parameter, Object value )
    {
        if( value == null )
        {
            throw new ArgumentNullException( parameter.Name );
        }
    }
}
e il nostro aspect cosà:
[Serializable]
public class ArgumentValidatorAspect : OnMethodInvocationAspect
{
    public override void OnInvocation( MethodInvocationEventArgs context )
    {
        //L'array di valori passati al metodo
        object[] values = context.GetArgumentArray();

        //Il nome del metodo pubblico: PostSharp genera un metodo private con anteposta la tilde
        String publicMethodName = context.Delegate.Method.Name.Substring( 1 );

        //Un rifermento al metodo pubblico
        MethodInfo mi = context.Delegate.Method.DeclaringType.GetMethod( publicMethodName );
        
        //Lambda, lambda e lambda ;-)
        mi.GetParameters()
            .Where( pi => pi.IsAttributeDefined<NotNullAttribute>() )
            .Select( pi => new
                        {
                            Parameter = pi,
                            Attribute = pi.GetAttribute<NotNullAttribute>(),
                            Value = values[ pi.Position ]
                        } )
            .ForEach( tupla => tupla.Attribute.Validate( tupla.Parameter, tupla.Value ) );

        context.Proceed();
    }
}
scopriamo che il seguente codice funziona esattamente come ci aspettiamo:
try
{
    MyTestClass tc = new MyTestClass();
    tc.Foo( null );
}
catch( ArgumentNullException anex )
{
    Console.WriteLine( "Exception: {0}", anex.Message );
}
Producendo questo risultato: image
Bingo ;-)
Facciamo un po’ di commenti sul codice dell’apect perchè merita:
    public override void OnInvocation( MethodInvocationEventArgs context )
il metodo OnInvocation ci passa un’istanza di una classe MethodInvocationEventArgs che ci da accesso a tutte le informazioni relative al metodo che stiamo intercettando, attenzione che qui c’è uso di reflection, ma:
  • a detta dell’autore viene usata una ed una sola volta e poi le informazioni cachate;
  • nelle versioni future molte parti stanno per essere sostituite con generazione di codice MSIL custom in fase di build per superare le problematiche di performance di reflection a runtime;
    //L'array di valori passati al metodo
    object[] values = context.GetArgumentArray();

    //Il nome del metodo pubblico: PostSharp genera un metodo private con anteposta la tilde
    String publicMethodName = context.Delegate.Method.Name.Substring( 1 );
    //Un rifermento al metodo pubblico
    MethodInfo mi = context.Delegate.Method.DeclaringType.GetMethod( publicMethodName );
Le informazioni che possiamo quindi recuperare sono ad esempio:
  • l’array dei valori dei parametri in ingresso al metodo: e qui possiamo comiciare ad immaginarci tutti gli scenari di logging e tracing semiautomatico…;
  • un’istanza della classe MethodInfo che ci da accesso a tutte le informazioni sul metodo chiamato. Come probabilmente avrete notato il codice fa un’operazione apparentemente strana:
    • abbiamo decorato il parametro con l’attributo “NotNull”;
    • PostSharp in fase di build ha rinominato il nostro metodo trasformandolo in privato e anteponendo al nome la tilde;
    • quello che però non ha fatto è stato rimettere sul parametro del metodo privato l’attributo, e questo credo sia un bug, vedremo…;
    • quindi in questo momento quello che dobbiamo fare per recuperare il nostro parametro è un giro un po’ arzigogolato:
      • recuperare prima il nome del metodo pubblico: quello privato senza la tilde;
      • recuperare un riferimento al metodo pubblico;
      • cercare l’eventuale presenza dei parametri e invocare la validazione;
mi.GetParameters()
    .Where( pi => pi.IsAttributeDefined<NotNullAttribute>() )
    .Select( pi => new
                {
                    Parameter = pi,
                    Attribute = pi.GetAttribute<NotNullAttribute>(),
                    Value = values[ pi.Position ]
                } )
    .ForEach( tupla => tupla.Attribute.Validate( tupla.Parameter, tupla.Value ) );
Facendo uso di un po’ di sane Lambda e di fluent interface riusciamo a scrivere anche del codice elegante per la validazione, anche qui potremmo fare alchimie notevoli per renderlo ulteriormente flessibile magari introducendo un attributo di base ma non è oggetto del nostro test ;-). ndr: ForEach(), IsAttributeDefined() e GetAttribute() sono degli extension methods miei.
Le potenzialità sono notevoli, quello che si pò fare si può certamente fare anche in altri mille modi uno su tutti usando ad esempio i concetti di facility e interception messi a disposizione dai framewrok di inversion of control ma in questo caso con il vincolo che il componente che volete intercettare sia gestito dal framewrok di IoC e questo non è detto che sia possibile.
PostSharp ci mette ad disposzione un modello che ci consente di fare intercept anche di qualcosa che non abbiamo scritto noi:
[assembly: MyOnMethodInvocationAspect( 
AttributeTargetAssemblies = "mscorlib",
AttributeTargetTypes = "System.Threading.*" )]
mi limito a riportare un semplice e esempio lasciando a voi l’onere/onore di capirne le potenzialità ;-)
Un’altra cosa decisamente interessante che si può fare è usare PostSharp per fare validazione a Compile Time, si avete capito bene a Compile Time ed eventualmente “rompere” la build sulla base di considerazione fatte da vostro codice custom che viene invocato dal compilatore… e questo mi stuzzica decisamente di più ;-)
public class MySampleAspect : OnMethodInvocationAspect
{
    public override bool CompileTimeValidate( MethodBase method )
    {
        Message error = new Message( SeverityType.Error, "ErrorCode", "Error message.", "Error Location" );
        MessageSource.MessageSink.Write( error );

        return false;
    }
}
Interessante, decisamente… qui concentrerò i miei prossimi esperimenti.
.m

Thursday, October 23, 2008

Anonymous delegates/methods/lambda… unsubscribing…

Forse non tutti sanno che… :-D, bello iniziare così.
Sappiamo tutti cosa sono gli eventi e come utilizzarli/gestirli, abbiamo il sistema tradizionale (quasi legacy oserei dire ;-)):
WithEvent we = new WithEvent();
we.SomethingOccurred += new EventHandler( OnSomethingOccurred );
In cui agganciamo esplicitamente un delegate, che punta ad un nostro metodo, all’evento esposto da una classe.
Abbiamo poi altri due sistemi, il secondo evoluzione del primo, che sono decisamente più smart:
WithEvent we = new WithEvent();
we.SomethingOccurred += delegate( object sender, EventArgs e )
{
    //Do something...
};
possiamo infatti utilizzare gli anonymous methods per agganciare direttamente un handler, questo, oltre che ad una maggior leggibilità, porta anche una serie di vantaggi non indifferenti: uno su tutti la visibilità di eventuali variabili definite nello scope e la conseguente non necessità di gestire fastidiose variabili globali:
String foo = "foo";
WithEvent we = new WithEvent();
we.SomethingOccurred += delegate( object sender, EventArgs e )
{
    //Do something...
    foo = "new value";
};
sarà onere del compilatore smazzarsi il problema, e lui è decisamente bravo.
Possiamo ulterirmente abbellire la sintassi facendo uso delle fantastiche lambda expression:
String foo = "foo";
WithEvent we = new WithEvent();
we.SomethingOccurred += ( s, e ) => { foo = "new value"; };
ottenendo, imho, una sintassi semplicemente fenomenale.
Siamo però adesso di fronte ad un annoso problema, in entrambi gli ultimi 2 casi (anonymous methods e lambda) ci perdiamo per strada il fatto che sarebbe buona pratica anche rimuovere gli handler ad un evento. In questo purtroppo il compilatore non ci viene in aiuto perchè non genera nessun codice per la rimozione e così su 2 piedi ci troviamo di fronte ad piccolo inghippo di non facile soluzione… se infatti proviamo a scrivere sta “porcata” compila ma non sortisce l’effetto desiderato (e per fortuna aggiungo io…):
we.SomethingOccurred -= ( s, e ) => { foo = "new value"; };
questo perchè ogni volta che usiamo gli anonymous methods/lambda il compilatore genera degli handler diversi.
la soluzione però non è così brutta e neanche così ardua:
EventHandler handler = ( s, e ) => { foo = "new value"; };

we.SomethingOccurred += handler;
we.SomethingOccurred -= handler;
basta infatti crearlo noi l’handler… ma se volessimo deregistrare l’evento all’interno dell’handler stesso? potremmo essere tentati di scrivere una cosa del tipo:
String foo = "foo";
WithEvent we = new WithEvent();

EventHandler handler = ( s, e ) => 
{ 
    foo = "new value";
    ( ( WithEvent )s ).SomethingOccurred -= handler;
};

we.SomethingOccurred += handler;
che è perfettamente lecito ma non compila… per il semplice fatto all’interno del corpo della lambda non posso far riferimento alla lambda stessa… in realtà questa cosa non è proprio vera e credo che il problema sia solo il compilatore che non è abbastanza smart, se infatti scriviamo:
EventHandler handler = null;
handler = ( s, e ) => 
{ 
    foo = "new value";
    ( ( WithEvent )s ).SomethingOccurred -= handler;
};
compila alla perfezione e funziona che è una meraviglia.
.m

Monday, October 20, 2008

CD/DVD Virtuali

Credo che il più famoso sia da sempre l’ormai defunto, almeno per me, Daemon Tools.. defunto perchè è più la spazzatura che si porta dietro che le feature…
Qualche mese fa sono approdato a MagicISO, comodo, leggero e fa il suo sporco lavoro senza fronzoli e senza “rompere”… ma c’è di meglio! o meglio… a MagicISO manca qualcosa: per montare una ISO è necessario fare uno scomodo giro nel menù contestuale dell’icona che trovate nella notification area.
Da qualche giorno sono però approdato a Virtual CloneDrive: como leggero fa il suo sporco lavoro ed è addirittura meno invasivo di MagicISO, si associa, durante il setup ai tipi di file che volete, e vi permette di montare un’immagine facendoci semplicemente doppio click sopra.
.m

Asus EeePC 904HD

ebbene si anche io sono stato colto da acquisto compulsivo e ci sono cascato… il tutto un po’ sospinto dalla “stufita” (termine ereditato dal bergamasco) di portarsi dietro i soliti 10Kg di roba… e stimolato dall’andata al TechEd ho ceduto all’acquisto.
E’ arrivato qualche giorno fa, c’era una bella installazione di Linux che non ha mai visto la luce :-D, adesso c’è una Windows Vista Ultimate con tanto di Visual Studio 2008 Team Suite, Office 2007, Sql Server 2005 Express e IIS 7… sto finendo di installare un po’ di fronzoli e devo dire che sono piacevolmente meravigliato dalle prestazioni complessive e anche da quelle del compilatore di Visual Studio.
.m

Thursday, October 2, 2008

La mia banca è differente…

… e fa veramente pena…
Da circa 10 anni CartaSi offre il servizio di gestione online della carta di credito, e naturalmente l’attivazione si fa tutta online, e il servizio di notifica (free) via SMS delle spese fatte… funziona, fa quello che deve fare e lo fa bene.
Da circa un paio di mesi (…) la mia banca offre lo stesso servizio per le “sue” carte di credito:
  • Non si può attivare online, ma bisogna recarsi presso la banca… spacciando il tutto con una non ben specificata maggior sicurezza del sistema :-|;
  • Il servizio di notifica via SMS è a pagamento per le spese inferiori ai 70€…;
Allora mi metto d’impegno e trovo una mattinata per recarmi in banca e fare il tutto, attivo il contratto e aspetto fiducioso… dopo 2gg mi arriva un SMS di conferma che il servizio di notifica è attivo per le spese oltre i 70€ (in questo modo evito di pagare), quello che succede nelle 3 settimane a venire è:
  • vado a fare la spesa più volte, pago con la carta e spendo sempre più di 70€… qualcuno di voi ha per caso ricevuto il mio SMS di notifica?
  • attendo con ansia, e continuo ad attendere da 3 settimane, la mail che conferma l’attivazione del servizio e mi fornisce la password temporanea per fare il primo accesso… anche di quella nessuna traccia;
Stamattina raccolgo un po’ di pazienza in giardino e vado in banca, spiego all’impiegata, per altro gentilissima, l’accaduto e lei candidamente mi dice: uhm… non si può fare nulla… le annullo il contratto e ne facciamo un altro :-|, io un po’ basito le dico va bene, dopo circa mezz’ora di tentativi mi guarda sconsolata e mi dice che il sistema non le permette di creare un nuovo contratto perchè ce ne è già uno… che però è cancellato…
Le lascio un recapito telefonico e le dico che è meglio che vada a lavorare (io non lei…) ;-)
però la mia banca è differente… e credo bene… non va una seg* :-D
.m