Saturday, October 31, 2009

Sviluppo Web e qualità, ma in generare sviluppo e qualità

Avete mai provato a tenere abilitato lo script debugger di Internet Explorer?
image
e poi a navigare un po’ e un po’ la…? che esperienza teribbile :-), tre quarti delle pagine contengone quintalate di errori JavaScript:
image
producendo una experience che definire pessima è un complimento :-)
L’unica fortuna, per chi produce ste cose, è che quelle opzioni devono essere esplicitamente configurate e quindi in pressochè tutti i browser le cose vanno bene, ma la domanda a questo punto è:
  • ma il dev che ha scritto quello roba l’ha provata?
  • ma il tester, di UAT, che l’ha testata ha il debug degli script abilitato?
.m

[OT] Certe cose mi fanno pensare… molto…

Quoto (e condivido):
…snobbata dai più: ma spuntano le foto ed ecco che i grandi quotidiani si avventano allarmati su una notizia che già c’era: «Morto dopo l’arresto, diffuse le foto shock» ha titolato il Corriere, purché sia chiaro che la notizia ritenuta più interessante non era «Morto dopo l’arresto», ma «diffuse le foto shock», dunque le foto.
E il video napoletano stile Gomorra? Avete notato che qualcuno aveva le maniche corte? L’esecuzione, già nota, è dell’11 maggio scorso, e tuttavia Repubblica: «Adesso la città ci aiuti». E perché solo «adesso»? Ah già, perché c’è il video, già, che stupidi.
Fonte: Macchianera.
.m

Friday, October 30, 2009

NHibernate: IPreInsertEventListener

Poco tempo fa ho parlato di strategie di generazione del document identifier con NHibernate, quello che vogliamo ottenere è rendere trasparente all’utilizzatore il fatto che un “id”, ad esempio il numero fattura, debba essere generato e dipenda da fattori esterni all’entità.
In soldoni vogliamo limitarci ad una cosa del genere:
using( var dc = this.dataContextFactory.Create() )
{
    using( var tx = dc.BegingTransaction( IsolationLevel.Serializable ) )
    {
        dc.Insert( myEntity );
        tx.Commit();
    }
}
nel post di cui sopra introducevo un elegante soluzione proposta da Fabio Maulo, oggi mi sono trovato davanti alla prima vera necessità e nell’ottica dello “sviluppo sostenibile” mi sono detto devi mettere il minor effort possibile per raggiungere il risultato desiderato, risultato che è:
  1. generare un identificatore univoco, non una PK okkio, ma un identificatore univoco per l’utente che potrebbe anche essere una PK o Unique ma non è un vincolo che lo sia;
  2. “nascondere”, per una serie di buoni motivi su cui non mi dilungo, questa logica di generazione;
Se avessi potuto mettere mano al db il tutto probabilmente sarebbe finito in una Stored Procedure e/o una sana User Defined Function… ma non posso mattere mano al db :-/
Event Listeners
Il tool è fantastico e ha un livello di pluggabilità abbastanza invidsioso :-), recentemente sono stati infatti introdotti i così detti NHibernate Events (prima c’erano, e ci sono ancora, gli interceptor, ma sono un filino più ostici da usare).
NHibernate espone una serie di “eventi”, non nel senso del tradizionale evento .Net, che possiamo intercettare; lo scopo quindi è quello di inserirsi nella pipeline di insert e fare li il lavoro sporco:
container.Register( Component.For<IIdentityGenerationService<MyEntity>>()
    .ImplementedBy<MyEntityIdentityGenerationService>() );

container.Register( Component.For<MyEntityPreInsertEventListener>() );

var configuration = container.Resolve<Configuration>();
configuration.AddListener( e => e.PreInsertEventListeners, this.container.Resolve<MyEntityPreInsertEventListener>() );
Setup:
  1. registriamo, in Castle, “qualcuno” che sia in grado di generare una nuova “identità”;
  2. registriamo anche il “listener” al fine di, senza saper ne leggere ne scrivere, iniettare le eventuali dipendenze;
  3. configuariamo NHibernate aggiungendo il nuovo listener, pescandolo dal framework di IoC;
Identità (che è anche un bel film :-))
public interface IHaveIdentity
{

}

public interface IHaveIdentity : IHaveIdentity
{
    T Identity { get; }
}
la prima interfaccia sostanzialmente ci servirà da “marker interface”, definiamo poi il tizio che sa fare il lavoro sporco:
public interface IIdentityGenerationService
{
    Object GenerateIdentity( IHaveIdentity entity, ISession session );
}

public interface IIdentityGenerationService : IIdentityGenerationService
{

}
In questo caso invece la seconda interfaccia ci serve per differenziare/univocizzare (teribbbbile :-P) la registrazione in Castle.
IPreInsertEventListener
finalmente :-)
public class MyEntityPreInsertEventListener : IPreInsertEventListener
{
    readonly IServiceProvider container;

    public MyEntityPreInsertEventListener( IServiceProvider container )
    {
        this.container = container;
    }

    public bool OnPreInsert( PreInsertEvent evt )
    {
        var ihi = evt.Entity as IHaveIdentity;
        if( ihi != null )
        {
            var entityType = evt.Entity.GetType();
            var svcType = typeof( IIdentityGenerationService<> ).MakeGenericType( entityType );
            var svc = ( IIdentityGenerationService )this.container.GetService( svcType );

            using( var innerSession = evt.Session.GetSession( evt.Session.EntityMode ) )
            {
                var newIdentity = svc.GenerateIdentity( ihi, innerSession );

                var rh = ReflectionHelper.BoundTo<MyEntity>();

                rh.GetProperty( e => e.Identity )
                    .SetValue( ihi, newIdentity, null );

                int index = Array.IndexOf( evt.Persister.PropertyNames, rh.NameOf( e => e.Identity ) );
                if( index != -1 )
                {
                    evt.State[ index ] = newIdentity;
                }
            }
        }

        return false;
    }
}
Urka, quanta robaccia :-), andiamo per gradi:
  • abbiamo bisogno di intercettare solo le insert e abbiamo bisogno di essere notificati prima della insert;
  • verifichiamo se l’entità che stiamo per inserire ci interessa: IHaveIdentity è li apposta :-)
    • andiamo a recuperare il servizio di generazione delle identità;
    • apriamo una nuova sessione, questo è molto importante perchè se riutilizzate la vostra finite in un loop infinito che porta ad una inevitabile StackOverflowException, e lo facciamo con IEventSource.GetSession( … ) in questo modo la child session si porta dietro tutto della parent session: stessa connection e stessa transazione;
    • chiediamo al servizio di generare una nuova identità;
    • via reflection, ricordate il minor effort possibile, settiamo il nuovo valore;
    • dobbiamo ricordarci anche di aggiornare il valore dei parametri di NH che stanno viaggiando verso il db altrimenti l’entità è aggiornata ma il db non è consistente… non è vero… :-) funzionare funziona ma succede una cosa molto curiosa:
      • nh fa la insert…
      • si accorge che la entity però è diversa;
      • subito dapo la insert fa un update…questo è poco bello perchè fa una operazione inutile ed evitabile aggiornando i valori dei parametri;
    • alla fine ritorniamo “false” e questo concedetemelo ma fa veramente brutto… :-) ritorniamo false perchè alcuni listner, come quello che usiamo noi, possono “votare” se l’operazione deve essere eseguita o meno… falso: in realtà possono votare se l’operazione deve essere abortita o meno, ecco perchè false, ma secondo me è decisamente contro natura il ragionamento da fare;
Tralascio l’implementazione del servizio di generazione delle identità che si limita ad usare la session per eseguire una “query” (con ICriteria) e recuperare le informazioni che servono per generare una nuova identità, triviale insomma.
Il tutto è adesso completamente trasparente, che era esattamente quello che volevamo, e il codice di cui sopra funziona come ci aspettiamo e il fido NHProof ce lo conferma.
.m

Thursday, October 29, 2009

NHibernate, event listener: che cosa scomoda…

Questa è abbastanza “scomoda” se usate gli event listener di NHibernate da codice dovete stare molto attenti a non rimuovere quelli di default:
var fc = container.GetService<FluentConfiguration>();
fc.ExposeConfiguration( cfg =>
{
    cfg.EventListeners.PreInsertEventListeners = new[] { new MySaveEventListener() };
} );
Quello che succede infatti, e pure giustamente, è che se nella lista degli eventi c’è già qualcuno lo state semplicemente sovrascrivendo e questo è poco bello :-)
Una soluzione semplice ed elegante è questo Extension Method che vi permette di scrivere in maniera molto naturale questo:
var fc = container.GetService<FluentConfiguration>();
fc.ExposeConfiguration( cfg => 
{
    cfg.AddListener( e => e.PreInsertEventListeners, new MySaveEventListener() );
});
ne riparliamo prossimamente
.m

Wednesday, October 28, 2009

ProjectName, Namespace e AssemblyName: ma quanta robaccia… :-)

Questo è un’altro dei passaggi cruciali, abbastanza manutenibile anche a posteriori a patto che non siate sotto source control che rende le cose un po’ più ostiche.
Per prima cosa ricordiamoci che i sistemi di source control non digeriscono molto bene il rename di un progetto, e TFS non fa difetto ;-); per essere precisi il problema è che se siete (e io lo sono alla noia) pignoli quando rinominate un progetto volete anche rinominare la folder.. e questo spacca la solution… e che ----- :-), quindi si può fare ma è abbastanza tedioso, meglio cercare di evitarlo.
Tornando in tema, quando create un progetto Visual Studio fa le seguenti assunzioni:
  • creo una cartella con lo stesso nome del progetto e li ci metto il progetto e tutto il resto;
  • assegno come default namespace del progetto il nome del progetto, applicando una serie di trasformazioni se il nome del progetto non è proprio consono… tipo contiene degli spazi;
  • assegno all’assembly, output della build, lo stesso nome del progetto;
Molte bene, a me piace tutto :-) però attenzione alle scelte che fate:
  1. Casing: se il nome del progetto è “foo my library” il default namespace sarà una cosa del tipo foo_my_library che è tutto tranne che compliant alle naming convention: quindi perchè non chiamare il progetto “Foo.MyLibrary”?
  2. Io non metto mai il nome dell’azienda nel nome del progetto, ma modifico a mano dopo il default namespace: la mia personale convenzione è che il project name inizia sempre con il project code name seguito da qualcosa chje identifichi bene a cosa serve:
    1. project name: Next.Backend.Publishing;
    2. assembly name: Next.Backend.Publishing (.dll) che mi va benissimo;
    3. default namespace: ManagedDesigns.Next.Backend.Publishing, che è l’unica cosa che cambio;
fanno eccezione alla regola tipicamente i progetti di “startup”, le UI per intenderci, che spesso hanno nomi più esoterici:
  1. project name: Next Mvc UI;
  2. assembly name: Next (.dll);
  3. default namespace: ManagedDesigns.Next;
Perchè nell’assembly name non mettiamo il nome dell’azienda? perchè allungherebbe il nostro path… e non aggiungerebbe informazioni utili.
Perchè tutte queste menate che portano via un sacco di tempo?… che mi fanno fare tutto tranne che scrivere codice? perchè in un progetto degno del suo nome la scrittura del codice non è detto che sia la parte più importante, quello che a prima vista sembra il contorno potrebbe rivelarsi fondamentale in seguito e potrebbe rivelarsi essere la magagna che non vi fa dormire di notte.
Pensate banalmente a dover fare un po’ di sana reflection e a tutto il tempo che perdete perchè il naming degli assembly, dei tipi e dei namespace semplicemente non è intuitivo…
voglio caricare dinamicamente l’assembly del data layer… ma perchè si chiama “samples oracle tests.dll” e non “Next.Data.Oracle.dll”? e il collega… ahhh… si certo stavo facendo delle prove e poi sono andato avanti li… vaporizzatelo :-)
.m

Tuesday, October 27, 2009

Perchè quei nomignoli così scomodi?

Probabilmente, ma anche no…, qualcuno si sarà chiesto: ma perchè quei nomi? perchè lib e non libraries, perchè doc e non documents?
image
per il semplicissimo motivo che è molto facile raggiungere il fastidioso limite dei 256 caratteri che un path su disco ha… che è decisamente obsolescente ma dietro il quale c’è una lunga storia che esula da questo post.
Perchè? perchè il simpatico Visual Studio ha il vizio di creare i progetti inserendoli in una subfolder con lo stesso nome del progetto e siccome trovo che sia un gran comodo mi va bene così :-)… lo trovo comodo perchè mi piace poter vedere e capire a colpo d’okkio che la folder Ran.Toolkit.Windows probabilmente conterrà una class library… ma questo porta ad piccolo inghippo che:
c:\Users\Mauro\Development\Projects\Ran Toolkit\baseline\src\Ran.Toolkit.Windows\bin\x86\debug\Ran.Toolkit.Windows.dll
cuba già 119 caratteri… ma se siete su Windows XP, cosa lecitissima, quel “Users” diventa un malefico “Documents & Settings”… motivo per cui è diventato Users :-)
Concludendo: se lo conosci non ti uccide :-)
.m

Trenitalia: inetti (punto)

Non credo possano esistere altri temini per quelli di Ferrovie dello Stato… e pensare che ero riuscito a trovare qualcosa di positivo… mi rimangio subito tutto :-)
image
Mi dicono che o l’utene o la password sono sbagliati… ma mi dicono anche che mi restano ancora 4 tentativi (su 5 ndr)… il che a casa mia significa che l’utente è giusto altrimenti quale account disattivi? :-P
image
…direi che hanno un brutto rapporto con i DateTime…
image
Alla fine faccio tutto quello che devo fare per comperare l’abbonamento… e si spacca tutto :-/… la sessione è il male… usata così poi fa veramente pietà.
Come minimo UAT(s) sono cosa sconosciuta… ma proprio come minimo…
.m

La preparazione…

timer …come ho già detto sto cercando di avvicinarmi ad una metodologia di lavoro gestione del tempo che sia il più produttiva possibile e il meno stressante possibile… sembra un’utopia ma sto scoprendo pian piano che non lo è :-)
Per ora ho messo in pratica alcune semplici regole:
  1. Da qualche anno: Get Things Done: ogni cosa che mi precipita addosso viene semplicemente pianificata, in questo senso un buon uso/organizzazione/conoscenza di Outlook è veramente importante;
  2. Da una settimana circa sto studiando/applicando la Tecnica del Pomodoro: per ora lo strumento principe è la carta per non aggiungere criticità alla complessità che già è la vita quotidiana :-) il minor sforzo per raggiungere l’obiettivo: carta, matita e gomma;
  3. Da stamattina ho disabilitato le notifiche di:
    1. nuova posta di Outlook;
    2. nuovi post di FeedDaemon;
    3. nuovi post/thread di MesNews;
il punto 3) devo dire che ha avuto un effetto decisamente non atteso: sto osservando una notevole riduzione dello stress percepito. In questo modo evito di farmi mettere pressione dalle notifiche e semplicemente schedulo del tempo per leggere la posta e il resto… tanto il mondo va avanti anche se io rispondo dopo 1h ora invece che immediatamente :-)
.m

è proprio un altro mondo…

  • vai all’MVP Global Summit e scambi quattro chiacchere, come se nulla fosse, con Anders Hejlsberg;
  • ti presenti in una mailing list come nuovo membro e Brian Harry risponde e ti dice che settimana prossima sarà a Roma e ti invita a cena…
  • sei in coda in mensa e quello dietro di te è Rich Kaplan;
non ho parole, sto cercando di pensare ad un paragone con qualcosa qui in Itaglia… ma proprio non ci riesco.
.m

Monday, October 26, 2009

…adesso è un po’ meglio :-)

rispondo a Nicolò.
L’originale è un Gadget per la sidebar che potete trovare nella Gallery di Live ma dopo un po’ di smanettamenti e customizzazioni è decisamente migliorato:
image
adesso fa esattamente quello che mi serve, l’ultima minch*atina che mi manca è un bel file wave con il trillo tipico da contaminuti, poi si che posso cucinare come si deve :-)
.m

Ma quanto è ben fatto Asp.Net MVC!

Non sono propriamente uno sviluppatore web, o almeno non lo sono più in modo assiduo da qualche anno, quindi sono rimasto un po’ indietro :-)
Da un anno, abbondante, a questa parte in bottega stiamo seguento un cliente, di dimensioni ragguardevoli, nello sviluppo di una soluzione di content publishing; sono entrato nel team da poco e siccome una parte corposa del progetto è basta su Asp.Net MVC avevo bisogno di recuperare il tempo perso e nel frattempo prendere confidenza con il framework MVC, naturalmente non potevo farlo pesando sulle spalle del team, almeno non per la parte prettamente infrastrutturale.
Mi sono armato di buona volontà e ho iniziato la creazione di un “piccolo” progetto che avevo in cantiere da un po’, progetto che poteva essere fatto con MVC, progetto che ha caratteristiche che lo rendono compatibile con le mie necessità dilearning e altre caratteristiche che possono essere piegate alle mie necessità di learning :-)
Naturalmente io sono allergico al manuale delle istruzioni e peggio ancora ho la tendenza a voler partire dalla cose facili… :-), ergo:
e mi sono detto:
  • devo riuscire a pluggare Castle Windsor come framework per IoC;
  • devo riuscire a spostare i Controller(s) in un assembly separato;
e con sorpresa ho scoperto che è di una semplicità disarmante, il che, imho, è decisamente un pregio.
ControllerFactory
L’architettura di MVC prevede che la creazione di un controller sia delegata ad una factory, ergo quando registrate le route:
routes.MapRoute(
    "Default",
    "{controller}/{action}/{id}",
    new { controller = "Home", action = "Index", id = "" }
);
oltre a fare tutto quello che fanno state anche dicendo: “ooh engine di MVC! guarda che per soddisfare a quella richiesta devi usare il controller che si chiama HomeController e che sta nella folder Controllers”, conventtion-over-configuration.
In realtà non è vero che gli state dicendo che sta nella folder Controllers, ma lo vedremo tra poco. Sta di fatto che quando arriva una richiesta http che deve essere girata a quel controller l’engine di MVC va dalla DefaultControllerFactory e gli chiede di risolvere il problema: voglio questo controller, mo arrangiati un po’ te :-).
Come possiamo metterci in mezzo? semplicissimo:
var container = new WindsorContainer();
var factory = new WindsorControllerFactory( container );

ControllerBuilder.Current.SetControllerFactory( factory );
dove una WindsorControllerFactory altro non è che:
class WindsorControllerFactory : DefaultControllerFactory
{
    IWindsorContainer container;
    public WindsorControllerFactory( IWindsorContainer container )
    {
        this.container = container;
    }

    protected override IController GetControllerInstance( RequestContext requestContext, Type controllerType )
    {
        return container.Resolve( controllerType ) as IController;
    }
}
triviale è dir poco :-)
DefaultNamespaces
Ma cosa succede in fase di risoluzione del “nome” del controller? una volta fatta la prima parte, iniettato c'ioè il nostro motore di IoC preferito, se piazziamo un bel breakpoint in quel metodo GetControllerInstance ci accorgiamo che il tipo che arriva come secondo parametro è il System.Type del controller che MVC ritiene debba soddisfare la richiesta.
Il problema ce ci poniamo è: ma come fa questa scelta? l’unica cosa che sa è che a fronte di una richiesta http deve girare l’onere ad un controller che si chiama Home. Quello che succede è più o meno questo, e qui eventualmente lascio intervenire Simone per i dettagli:
  • una volta scoperto che il controller è Home, lo “scopre” grazie alla RouteTable;
  • parte la ricerca, via reflection, per una classe HomeController:
    • che erediti da Controller;
    • che si trovi nello stesso assembly dell’applicazione MVC;
    • e che, presumo, abbia un full type name: “Application Default Namespace” + Controllers + “Controller Name
se questa convenzione non viene rispettata il giochetto non funziona. Quindi come possiamo spostare i controller in un posto diverso?
ControllerBuilder.Current.DefaultNamespaces.Add( "ManagedDesigns.Next.Mvc.Runtime.Controllers" );
Questo altro non fa che dare la possibilità al motre di MVC di cercare i controller anche li, dove quel li può essere un assembly separato, a patto che stia nella bin dell’applicazione, permettendoci di fare ad esempio questo, in fase di configurazione di Castle:
container.Register( AllTypes.Of<Controller>()
    .FromAssemblyNamed( "Next.Mvc.Runtime" )
    .Configure( cd => cd.LifeStyle.Is( LifestyleType.Transient ) ) );
registriamo in maniera implicita tutti i tipi che derivano da Controller e che stanno nell’assembly indicato.
  • Elegante;
  • Semplice;
  • Efficace;
.m

Vodafone: servizio terzomondista

Non sono arrabbiato, sono semplicemente imbufalito.
Stamattina accendo il telefono e mi becco un laconico “sim registration failed”, chiamo Vodafone e scopro che mi hanno disattivato il numero… perchè? perchè non ricarico da troppo tempo… so che nel contratto è scritto e quindi non mi lamento della procedura, quello che mi fa veramente andare in bestia è che mi spammano di SMS a nastro con promozioni, o presunte tali, e il sistema automatico non prevede di avvisare un cliente (è vero è sbagliato… sono un vecchio cliente, quindi non conto nulla) che usa quel numero da circa 10 anni che lo stanno per falciare?!?!?!
.m

Non è proprio un pomodoro…

image
… ma vediamo se riusciamo a mettere un po’ di ordine nel marasma di cose che mi assillano.
.m

ICommand e gli elementi disabilitati.

Durante la preparazione del materiale per il corso del mese prossimo salta fuori questa piccola rogna che mi segno per i posteri ma che devo indagare un po’ meglio:
se avete un elemento Wpf che supporta l’infrastruttura di commanding, quindi un ICommandSource, in binding con un ICommand e nel momento in cui lo stato del commando cambia (evento CanExcecuteChanged) l’elemento Wpf è disabilitato (IsEnabled == false) la variazione di stato del comando viene ignorata e non viene rivaluata (CanExecute) quando l’elemento Wpf torna ad essere abilitato.
Qualcuno ha avuto esperienze simili?
.m

Sunday, October 25, 2009

[OT] razzismo

il razzismo è una cosa detestabile, ogni forma di sfregio che ha come fondamento e fa leva sulla diversità è bieca e detestabile, anche se queste parole non riescono ad esprimere la rabbia che il razzismo fa crescere in me.
Quello che però, seriamente, mi chiedo è: detestare un razzista è una forma di razzismo?
.m
p.s.: non c’è nessun riferimento a fatti o persone pubblici, semplicemente ad avvenimenti molto privati.

“pirletti” crescono :-)

img
.m

Saturday, October 24, 2009

Le configurazioni e gli ambienti: un rapporto difficile

Sappiamo già che le cose cambieranno, ma finchè non usiamo Visual Studio 2010 e non capiamo come far funzionare quella cosa anche in progetti non web dobbiamo arrangiarci.
Il problema è abbastanza tipico ed è molto semplicemente che la configurazione della macchina di sviluppo è sempre diversa dalla configurazione delle macchine di test che è sempre diversa dalla configurazione della macchine di preproduzione che a sua volta è sempre diversa da quella delle macchine di produzione… respiro :-)
La cosa importante è che:
  • va bene che sia così;
  • non ha assolutamente nessun senso che si cerchi di cambiare le cose;
è lo strumento che deve piegarsi alle nostre necessità non noi che ci dobbiamo piegare ai limiti, o presunti tali, dello strumento: conoscenza, è la chiave del successo.
Siccome ne ho già parlato non vi tedio oltre e vi rimando a quel post per la soluzione. In quel post mancano però un paio di puntaulizzazioni importanti:
  • Non fare di più di quello che ci serve, è tempo sprecato:
    image 
    Queste sono le post-build action di un progetto, vedremo prossimamante cosa fanno e che problema risolvono, sono banalmente dei comandi per la shell, giustamente qualcuno potrebbe dire… tristezza… perchè non l’hai fatto con MSBuild? perchè scrivere quei comandi cuba circa 20 secondi e sono a prova di vero nerd, se ci sono riuscito io :-)…, mentre mettere mano al file *proj di MSBuild, testarlo e assicurarsi che in tutte le condizioni funzioni non è proprio cosa banale e quello che vogliamo fare è decisamente banale: luke…keep it simple :-).
  • Sii curiso e pigia sui bottoncini che trovi sulla UI… mica mordono :-)
    image
    e scopri che Visual Studio ha un interessantissimo set di “macro” che rendono il task di cui sopra ancora più da dummies :-)
  • Anzi… 3 puntaulizzazioni: nell’ottica dell’essere a prova di scemo e friction-less una soluzione complessa ad un problema banale è sicuramente la cosa peggiore che possiamo fare; se diamo in mano la nostra solution ad un nuovo dev sarà più facile che comprenda una Post Build Action o che sia un guru di MSBuild, NAnt o QuelloCheViPareCompiloIoCheSonoFigo? :-)
.m

I’m lovin’ it

image
image
…no more words! :-)
.m

Friday, October 23, 2009

Fantastico, semplicemente fantastico

La nuova Team Build di TFS 2010 è semplicemente fantasmagorica, hanno rivoluzionato il mondo e adesso il driver della Team Build non è più il già eccelso motore di MSBuild ma ci siamo evoluti verso Workflow Foundation 4.0:
image
Questa è una piccola parte, notare la scrollbar sulla destra, del workflow del build template di default. Se andiamo a spulciare nei meandri troviamo questo:
image
che esegue la vera e propria build del progetto o dei progetti compresi nella solution, tutto il resto, i “fronzoli”… si fa per dire…, è completamente gestito da WF4.0 il che significa che possiamo scrivere le nostre belle activity ma soprattutto debuggare il processo della Team Build cose che prima era effettivamente un po’ ostica.
Questo mi porta anche a fare un’altra considerazione: l’ottimo lavoro fatto da Gian Maria diventerà da un lato ancora più semplice e dall’altro potenzialmente molto, ma molto, più potente e versatile, una per tutte: ve lo immaginate un processo di deploy transazionale/compensativo… magari distribuito? :-)
Ora… se mi danno anche questo insieme ad una Team Build di questo livello possiamo dire di essere vicini al fritcion-less assoluto e finalmente poterci dedicare in toto al business del nostro cliente e non alle menate di contorno.
.m

Senza parole…

IMAGE_027 Fogna di 1° classe, Milano-Venezia 17.25
.m

[OT] Se il treno su cui sono adesso…

…scoppiasse per strada si risolverebbe il problema del sovraffollamento del pianeta… :-)
Ma poi io mi chiedo… ma perchè la gente non si lava?
.m

[OT] quante stupidate…

Si vede che oggi il mio lavoro dipende dagli altri… e che i ritmi degli altri sono un filino più lenti dei miei…
image
Fonte: http://www.metronews.it
il mio amico Luca che si occupa di antropologia da anni mi dice:
Mauro, sai quale è il problema vero? è che i bambini di oggi non hanno più una vita sociale, non giocano in cortile, non litigano per un pallone e non vengono esclusi se dicono “il pallone è mio e io non gioco più”. Questa degenerazione della società moderna fa si che al bambino manchi il microcosmo basilare in cui crescere e cresca con la “console per video giocare” dove vince sempre, dove lui è l’imperatore perchè quando è stufo spegne, dove lui è il deus ex machina che cambia gioco quando vuole lui… e cosa succede quando questo bambino entra nella società? ad esempio la sua classe di prima elementare? Elementare Watson… :-) si comporta esattamente allo stesso modo con il compagno di banco il quale però non reagisce come la “console per video giocare”… patatrack… si può arginare? certo, scricando una quantità di lavoro in più sulla famiglia, non aggiungo altro…
Io scendo con Omar :-)
.m

[OT] Secondo me posso viaggiare gratis.

image
Quello qui sopra è un estratto della “Carta dei servizi 2009” di Ferrovie dello Stato, se quella roba ha un benchè minimo valore legale siamo autorizzati a non pagare il biglietto.
.m

Meticolosità e Organizzazione

Tutto ha un inizio e se iniziamo bene siamo già a metà dell’opera, questo recita un vecchio adagio… nel nostro caso non siamo proprio a metà dell’opera ma iniziare bene è essenziale.
Cosa succede quando dobbiamo avviare un nuovo progetto? tipicamente apriamo Visual Studio e creiamo un nuovo progetto del tipo che più ci piace :-) pessima scelta perchè se il “robo” che state creando deve vivere ed essere manutenuto vi siete già tagliati le gambe :-)
Perchè? perchè Visual Studio ha una serie di “vizi” che su un progetto corposo diventano un problema, una prima cosa è quindi la struttura su file system:
image
ndr: se volete le informazione sullo stato del source control come nello screenshot, simil Tortoise*, vi servono questi: Team Foundation Server Power Tools, che vi danno anche un sacco di altri goodies… :-)
La prima cosa che faccio è quindi creare la struttura delle folder, che hanno questo scopo:
  • Run: la root del progetto, tipicamente il nome corrisponde anche al codename, ma è un dettaglio;
    • Baseline: questa c’è sempre! baseline contiene il nostro mondo è la trunk del progetto, la volete chiamare trunk, chiamatela trunk :-): perchè serve? perchè come si vede nell’esempio (dev10b2-branch) se facciamo delle branch le possiamo tenere in parallelo all’interno della root del progetto avendo sempre tutto sotto controllo; poi…in ordine alfabetico:
      • build: nonostante il nome non serve solo a quello, abbiate pazienza ci arriveremo; per ora sappiate che ci serve ed è “vuota”, o meglio contiene come minimo 2 subfolder vuote:
        • debug;
        • release;
      • data: se, e ripeto se, il progetto ha una base dati diversa da un database server, in questo esempio SqlCompact, li ci metto il db; in altri casi ci possono finire gli script sql che servono per generare un set di dati inizale o di base necessari per il corretto fuinzionamento del prodotto, i tipici “default”;
      • doc: questa è abbastanza opzionale, essendo io tipicamente un remote user con scarsa connettività ho bisogno di un posto in cui tenere la documentazione che non richieda l’always-on come SharePoint, qui ci metto anche i file di Excel con le versioni off-line degli WorkItem di TFS;
      • lib: contiene *tutte* le libraries nostre o di terze parti da cui il progetto dipende;
      • res: contiene le risorse in forma grezza, un esempio banale? “Splashscreen.psd” quindi il file di Photoshop che poi diventerà lo Splashscreen dell’applicazione;
      • src: tutti, e ripeto tutti, i progetti di Viusal Studio, i sorgenti, i progetti della db pro e chi più ne ha più ne metta;
      • TestResults: “ignore”, generata da Visual Studio per metterci i risulatati dei test, va bene così;
      • us: le sole “User Story”, può essere tranquillamente mergiata con “doc”;
Creata la struttura delle cartelle il primo passo è piazzare il tutto sotto source control, e anche questo è condito da un piccolo inghippo:
  • creiamo un nuovo Workspace per il nostro progetto;
e qui ci incastriamo…
image
ndr: i Workspace hanno come “chiave” il nome del workspace stesso, il nome dell’utente e il nome del computer, ma nonostante questo non potete creare su macchine diverse workspace con lo stesso nome… ergo consiglio… il nome del workspace per me è: “nome progetto” + on "+ “nome macchina”.
Il problema reale però è un altro: per mappare un workspace su una cartella locale avete bisogno di poter creare la struttura delle folder, in realtà basta e avanza la “baseline”, sul server ma per poterlo fare avete bisogno del workspace, togo :-)
Quindi? addomestichiamolo! tanto è da fare solo una tantum alla creazione del progetto:
  • Creaimo la solution vuota:
image
  • Visual studio ha il brutto vizio, in questo caso, di obbligarvi a creare una folder per la solution e di dare come nome della folder quello della solution… benedetto lui :-)
  • Quindi chiamiamo la solution “trunk” o “baseline”;
  • Creiamola;
  • Chiudiamo Visual Studio;
  • Andiamo su filesystem e rinominiamo il file trunk.sln, il file *.suo lo possiamo cancellare per ora non ci serve a nulla, dandogli il nome che abbiamo scelto per il nostro progetto;
  • Riapriamo la solution;
  • Dal TeamExplorer creiamo un Workspace per la nostra solution senza definire nessun mapping;
  • click con il destro sulla solution e “Add to source control…” e anche in questo caso diamo un paio di martellate qua e la :-):
image
Come si vede simpatia TeamExplorer decide che il nome della folder su TFS debba essere il nome della solution, ma a noi non va bene e non è solo quello:
  • primissima cosa: selezionate il workspace corretto!
  • selezionate la root del team project o la folder in cui volete piazzare il tutto e create una nuova cartella a cui assegnerete il nome della vostra solution;
  • infine assegnate un nome diverso alla solution folder, ad esempio “trunk”;
image
a questo punto potete fare fiduciosi “ok” e siete abili e arruolati, potete infine, cliccando con il tasto destro sulla folder “trunk” nel TeamExplorer, fare:
image
e aggiungere tutta la struttura delle folder di cui sopra.
Che sudata :-), questo lavoraccio però ci garantisce che ogni singolo dev del team, o noi stessi su una macchina diversa, con un semplicissimo “Get Latest” ci ritroviamo tutto al posto giusto, quindi a prova di “scemo” che è il nostro obiettivo primario.
ndr: la folder “build” e le due, o più, subfolder servono come directory di appoggio temporanee per automatizzare alcuni task di build, perchè metterle sotto source control? semplicemente perchè in questo modo siamo certi che la struttura, anche se vuota, sia tenuta sincronizzata tra le varie macchine;
Ricordatevi che tutto ciò che possiamo automatizzare alla lunga, ma anche alla corta :-), si traduce in un risparmio di risorse preziose.
.m

Tales of a Visual Studio Addicted

Spesso e volentieri nascono interessanti discussioni su come salvarsi la vita durante l’uso quotidiano dello strumento più amato :-D: Visual Studio.
Trovo che lo strumento sia potentissimo e veramente di pregio ma ha anche lui i suoi limiti e quando ci spingiamo oltre la vita diventa veramente complessa e frustrante, tipicamente questo succede quando chiediamo troppo.
Vorrei quindi raccogliere in una nuova categoria le esperienze di tutti i giorni, vediamo che ne esce.
Il problema credo sia molto sentito e la cosa importante da dire è che è un non problema, io ho da sempre un buon rapporto con il mio fido compagno di viaggio, è tutta una questione di approfondimento, comprensione dei limiti ed introduzione di escamotage che vi consentano di lavorare come si deve e friction-less.
L’altro obiettivo che vogliamo raggiungere è che anche l’infrastruttura di build e di configurazione di un progetto, complesso, sia a prova di “scemo” e con questo intendo che la necessità è di poter migrare la/le solution da un dev all’altro senza magagne, quindi un dev qualsiasi deve essere in grado, a nozioni zero, di aprire la solution, compilare e avviare il progetto senza che questo comporti essere a conoscenza di 32 tips&tricks interni… che se gli interessano sono documentati ma non devono essere un requisito.
.m

Tfs Integration Platform

Se, e ripeto se, questa cosa vede la luce, faccio santo qualcuno :-)
Leggendo questa serie di post mi vengono i brividi al pensiero di quello che potrei mettere in piedi con un TFS “locale” sincronizzato con un TFS “aziendale” (stiamo lavorando per voi :-)).
  • Team Build “locale” con la mostruosità di vataggi che questo comporta;
  • possibilità di lavorare offline con una experience da paura perchè sarebbe esattamente come essere online;
  • Una bolletta 3G un po’ meno salata, che non guasta mai ;-)
  • .. chi più ne ha più ne metta :-)
.m

Se ogni tanto ci riesci…

… è decisamente peggio che non riuscirci mai.
Stamattina causa sciopero ATM ho dovuto anticipare tutto di circa mezz’ora ed è successa una cosa decisamente inaspettata:
  1. sono salito su un treno e non su un lebbrosario;
  2. sono partito in orario;
  3. sono arrivato puntuale;
  4. ho potuto appoggiare lo zaino sul pavimento senza che strani esseri lo intaccassero;
Non sono mica abituato… :-)
Questo significa che la possibilità di avere un servizio decente, non dico eccelso, dico solo decente c’è; il tutto però non fa altro che peggiorare la pessima opinione che ho di Trenitalia.
.m

Thursday, October 22, 2009

Windows XP, Office 2003…

Non ero più abituato… sono da un cliente per una consulenza e non posso usare la mia macchina quindi me ne danno una loro:
  1. Windows XP Pro;
  2. Office 2003;
  3. Visual Studio 2005;
Che confrontato al mio:
  1. Windows 7 x64;
  2. Office 2010 TP;
  3. Visual Studio 2008;
  4. Visual Studio 2010 Beta 2;
E’ preistoria :-)
.m

Wednesday, October 21, 2009

[OT] Pensieri sparsi…

  • perchè scendi dal treno, dai una mano ad una signora a scaricare una pesante borsa e questa ti guarda e sorride come se fossi un marziano?
  • che senso ha riscaldare la metropolitana costantemente? stamattina sono salito sulla linea verde di Milano e credo ci fossero 60°… il vagone era farcito di persone che respiravano, evidentemente bastano e avanzano;
  • perchè un treno riesce a partire in orario, cosa mai vista in quest’ultimo periodo, e accumulare su una tratta di 20’ un ritardo di 35’;
  • perchè la sopracitata compagnia ferroviaria riesce a produrre delle eccellenze (per noi sono eccellenze per altri la norma… lo so) come la possibilità di fare l’abbonamento online e poi cade miseramente su tutto il resto? in realtà il perchè lo so, la domanda è perchè è sempre così…?
  • la tecnologia ha migliorato la nostra vita o ci ha semplicemente fregato in corner? adesso puoi rispondere ad una mail dal tuo device preferito mentre sottoterra viaggi in metropolitana… prima leggevi un libro o un quotidiano. Io non lo chiamo progresso, la chiamo schiavitù;
Voi direte… quanta retorica… bhe… io mi sono rotto, quella di cui sopra non è retorica è che noi ormai siamo assuefatti e abituati a vivere nella ***** (mettetici un quello che volete qualsiasi di 5 lettere che inzia per “mer” e finisce per “da” :-D)
Qualche tempo fa sono stato a Il Cairo e in albergo chiaccherando con un americano mi sono reso conto che noi siamo proprio “cotti”, dall’aereoporto (a nord della città) alle piramidi (completamente dalla parte opposta) c’è una strana tangenziale dove cui la cosa più normale è:
  • vedere i cammelli;
  • osservare egiziani temerari che attraversano a piedi, senza neanche correre troppo;
  • notare con sospetto macchine, carretti con l’asino e robe strane, che vanno contromano…;
per me era gogliardico, non molto di più di quello che un automobilista itagliano può osservare in una giornata tipica in una grande città a caso… per il mio interlocutore era semplicemente folle… ha ragione lui, punto.
.m

Connettersi a TFS 2010 da VS2008

Innanzitutto dovete installare questa patch:
http://www.microsoft.com/downloads/details.aspx?displaylang=en&FamilyID=cf13ea45-d17b-4edc-8e6c-6c5b208ec54d
Dal tipico nome sintetico :-D, poi attenzione che per aggiungere il server al Team Explorer di VS2008 dovete specificare a mano tutto l’url http/https:
image
.m

Tuesday, October 20, 2009

VS2010 + SqlCompact == “dll hell” :-)

ok, questo è un problema!
Ho felicemente installato la beta 2 di Visual Studio 2010 e ho migrato un progetto personale a cui sto lavorando almeno ho una buona scusa per usarlo, altrimenti va a finire che i primi giorni ci gioco poi il lavoro pressa e mollo…
L’inghippo però è che il buon Visual Studio arriva con una versione diversa di Sql Compact, per l’esattezza la 3.5 Sp2 beta 2, che lo stesso VS, da quel che ho capito, usa per l’intellisense.
Da dopo l’installazione il progetto personale, anche prima della migrazione quindi anche se usato da VS2008, non ne vuole più sapere di andare… parte e, a seconda dei tentativi, si schianta perchè c’è un “version mismatch” tra la versione managed e quella nativa… la cosa più bella è che se faccio tutto a manina e compilo forzando la piattaforma a x86 il tutto compila e parte pure ma poi le query non vanno… semplicemente non producono risultati… semplicemente perchè i comandi non arrivano mai al db :-) figo… si vede la transazione che si apre… e che si chiude con un “commit” ma nulla nel mezzo…
Una nota: non ho fatto retargeting del framework, per ora sto compilando a fronte del framework 3.5, vorrei consegnare prima della RTM di VS2010 :-)
Mezz’oretta e ho migrato tutto su Sql Express, non che sia un problema ma Sql Compact era un gran comodo per quello che sto facendo.
Developer avvisato, developer mezzo debuggato :-D
.m

TFS.2010.Beta2 -> up & running

Anche quello up & running, un paio di note:
  1. Evito di spiegare cosa ho fatto, Lorenzo è stato esaustivo;
  2. Sql Server: su una macchina x64 non potete usare la Express;
  3. Sql Server: è necessario avere installato il full-text-search;
  4. Wizard di configurazione: si accorge che avete un Sql Server installato e vi propone di usarlo “nomeMacchina\nomeIstanza” non toccatelo… io ci avevo messo “.\NomeIstanza” e si è incrocchiato tutto… maledetto me :-)
image
.m

VS.2010.Beta2 -> up & running

L’installazione è filata via liscia, okkio all’SDK per Silverlight 3.0, adesso incrociamo “i ditini” :-D
image 
Questa era la cosa che mi mancava di più, avendo Office x64 mi ero perso l’AddIn per TFS:
image
Conferma che funziona tutto da VS2010 verso il TFS2005 il che secondo me non è niente male :-)
Il prossimo passaggio probabilmente sarà avere un bel TFS in locale così posso scroccare il Build Server, essendo io un utente remoto è sempre un delirio.
.m

Monday, October 19, 2009

NHibernate: nullable one-to-one

Questa è una di quelle cose che non si drovrebbero fare… ma se avete un db che non potete toccare non è che ci siano molte scappatoie :-)
Il bello è che il tool risponde alla grande, anche se in questo caso deve essere un po’ addomesticato. Lo scenario è questo:
image
Mappato su un db con, più o meno, questa struttura:
image
Nel dominio avete il concetto di Preventivo (Estimate) e di Contratto (Agreement) e le 2 entità sono relazionate tra loro: ad un preventivo potrebbe corrispondere uno ed un solo contratto e ad un contratto potrebbe corrispondere uno ed un solo preventivo.
Il “potrebbe” in entrambe le casistiche è derivante dal fatto che un Preventivo:
  • “in corso” non ha un ancora un contratto;
  • “defunto” non avrà mai un contratto;
In maniera similare un Contratto potrebbe nascere senza essere passato da un Preventivo. La problematica in se non è nulla di trascendentale e qui trovate un ottimo riepilogo del problema e della soluzione, o meglio di parte della soluzione.
I problemi sorgono quando avete la necessità di cercare tutti i preventivi che non hanno un contratto… che dal punto di vista del modello ad oggetti è una cosa del tipo:
IEnumerable<Estimate> list;
list.Where( e => e.Agreement == null );
Ma volete che il tutto sia fatto da NHibernate direttamente in fase di fetch con una bella left-outer-join andando a cercare i preventivi che hanno un contratto con pk a null nel resultset.
Il Mapping
Già la fase di mapping, con il fido Fluent NHibernate, non è proprio delle più intuitive, per come è strutturato il db il mapping da Contratto verso Preventivo è abbastanza triviale:
public AgreementMapping()
{
    this.SchemaIs( "..." );
    this.WithTable( "..." );

    this.References( e => e.Estimate, "EstimateUID" )
        .Cascade.SaveUpdate()
        .Nullable();
Ci limitiamo a definire una “reference” tra il tipo Agreement e il tipo Estimate attraverso al proprietà Agreement.Estimate e specifichiamo il nome della colonna per la FK; spostandoci invece sul tipo Estimate le cose sono un po’ diverse perchè non potete più usare Reference, o perlomeno io non ci sono riuscito:
public EstimateMapping()
{
    this.SchemaIs( "..." );
    this.WithTable( "..." );

    this.HasOne( e => e.Agreement )
        .WithForeignKey()
        .PropertyRef( a => a.Estimate )
        .Cascade.SaveUpdate();
La combinazione vincente è data da HasOne insieme a PropertyRef:
  • HasOne definisce una simil-relazione 1-1 tra Estimate.Agreement e Agreement;
  • la magia la fa PropertyRef perchè istruisce il motore di mapping facendo si che tutte le informazioni per il mapping vengano prese dal mapping della classe Agreement e facendo di conseguensa si che NHibernate sia in grado di generare correttamente gli statement sql;
La cosa va che è un piacere finchè non cerchiamo di fare questo:
var criteria = provider.CreateCriteria<Estimate>();
criteria.Add( Restrictions.IsNull( "Areement" ) );
var data = criteria.List<Estimate>();
Che ci rimbalza perchè siamo dei disgraziati :-D
image
Anche un ipotetico tentativo di questo tipo falisce miseramente:
  criteria.Add( Restrictions.IsNull( "Areement.Key" ) );
Quella cosa non può funzionare, in realtà il giochetto è abbastanza tricky:
var criteria = provider.CreateCriteria<Estimate>();
criteria.CreateCriteria( "Agreement", "a", JoinType.LeftOuterJoin )
    .Add( Restrictions.IsNull( "a.Key" ) );
var data = criteria.List<Estimate>();
è cioè sufficiente creare un alias per la proprietà Agreement e poi andare ad indagare la PK alla ricerca di valori null restituiti dalla left outer join, producendo di fatto questa query:
image
che è esattamente quello che ci serve!
Concludendo
  1. il tool è impressionante, flessibile e potente;
  2. l’uso di Query Specification, come ho già avuto modo di dettagliare, permette di incapsulare tutta la logica degli ICriteria nascondendo e centralizzando i dettagli dell’implementazione;
.m

Sunday, October 18, 2009

Testare i test :-)

è un problema :-)
[TestMethod]
public void ExceptionTestWrapper_Try_using_non_buggy_code_should_not_fail()
{
    Tester.Expecting<ArgumentNullException>( "expectedParamName" ).Try( () =>
    {
        new ClassUnderTest( null );
    } );
}

[TestMethod]
[ExpectedException( typeof( AssertFailedException ) )]
public void ExceptionTestWrapper_Try_using_non_buggy_code_but_with_wrong_expected_paramName_should_fail()
{
    Tester.Expecting<ArgumentNullException>( "invalidParamName" ).Try( () =>
    {
        new ClassUnderTest( null );
    } );
}

[TestMethod]
[ExpectedException( typeof( AssertFailedException ) )]
public void ExceptionTestWrapper_Try_using_buggy_code_should_fail()
{
    Tester.Expecting<ArgumentNullException>( "expectedParamName" ).Try( () =>
    {
        new ClassUnderTest( "this is a valid value for the target." );
    } );
}
questi sono 3 test di un micro framework di test che uso, non è stato semplice scriverli perchè i test devono fallire quando il codice si comporta come ci si aspetta… insomma l’esatto opposto di quello che ci aspetteremmo da un test :-)
.m

Friday, October 16, 2009

FluentNHibernate e le proprietà da non mappare…

Supponendo di aver bisogno di mappare un’entità che espone delle proprietà calcolate con FluentNHibernate avete qualche problema…
L’inghippo di fondo è questo: quando mappate una entity, in fase di inizializzazione dei proxy (e qui vado a intuito) il proxy builder cerca tutte le proprietà e si lamenta, con una sonora exception, se una delle proprietà non è virtual o se manca del set accessor. Il messaggio di errore poi è quantomeno fuorviante :-)
Girovagando su internet si trovano tante colorite soluzioni (pezze direi io) tra cui la più bella è quella di disabilitare in toto il proxy validator:
<property name="use_proxy_validator">falseproperty>
Ma a casa mia non è un’opzione piuttosto una porcata.
Ergo mi sono messo a cercare e salta fuori che FluentNHibernate ha una strana sintassi, che in teoria dovrebbe funzionare, ma che in realtà non produce alcun effetto tangibile:
public SubjectMapping()
{
    this.Not.Map( s => s.RuntimeEvaluatedProperty );
Quel “Not” dovrebbe impedire il tentativo di mapping… ciccia… alla fine la soluzione è comunque semplice:
public SubjectMapping()
{
    this.WithTable( "Subjects" );

    this.Not.LazyLoad();
    
    this.HasMany( s => s.Addresses )
        .LazyLoad()
        .Inverse()
        .Cascade.AllDeleteOrphan()
        .KeyColumnNames.Add( "SubjectId" );
e consiste nel disabilitare il lazy loading a livello di classe (this.Not.LazyLoad()) e riabilitarlo eplicitamente ove serve.
.m

Thursday, October 15, 2009

NHibernate: document identifier

Lo scenario più semplice è quello della generazione del “numero fattura” in fase di insert del nuovo documento, tralasciamo per ora le più svariate possibilità legate al concetto di bozza, ad un certo punto avete la necessità di generare questo benedetto numero che deve sottostare a determinate regole. La stessa cosa può succedere in altri casi, come ad esempio la generazione di un codice cliente, e questo ci permette di generalizzare dicendo che:
  1. Abbiamo la necessità di generare un codice univoco;
  2. Questo codice univoco deve essere umanamente leggibile e avere un significato nel mondo reale, quindi niente guid;
  3. Il codice generato deve sottostare a delle regole, come ad esempio:
    1. deve essere progressivo, e la progressione potrebbe dipendere dall’uso: numerica, alfabetica, quellochemicipiaceame… :-)
    2. potrebbe avere una logica di reset: il numero fattura si resetta ad inizio anno ad esempio;
    3. etc…
In molti casi si potrebbe addomesticare il tutto con dell’ottimo codice t-sql e siccome non è un male spesso e volentieri lo faccio, del resto la logica di generazione del numero fattura è decisamente banale, ma non è sempre così potreste avere delle casistiche, nel mio caso la generazione del codice cliente, in cui non è proprio banale far girare l’algoritmo in codice t-sql.
In che modo NHibernate ci può essere di aiuto? da questa necessità è nato un thread, con Fabio Maulo, sullo user group italiano di NHibernate.
Fabio propone di:
  • definire la logica di generazione come entità di dominio;
  • definire nello schema del db una tabella che ospita le caratteristiche di questa logica:
    • ad esempio ultimo numero generato e data di generazione dell’ultimo numero, nel caso di una fattura;
  • in fase di insert
    • caricare da db questa entità;
    • chiederle di generare un nuovo id;
    • eseguire l’update, del “generatore”, e l’insert della nuova entità, ad esempio la fattura, con un lock;
Adesso la domanda che sorge spontanea, e che ho inevitabilmente ribaltato al capo, è: ma sta roba la delego al dev che si occupa del processo di insert o deve essere implicita e quindi automatica?
A mio modo di vedere questo è uno di quei problemi molto borderline da un lato la logica di generazione del “numero documento” è logica di business ma dall’altro è strettamente dipendendente dallo storage e il boss propone di automatizzare questo giro, ergo una possibilità sono gli eventi di NHIbernate per inserirsi nella pipeline di salvataggio e a questo punto fare il bello e cattivo tempo.
.m

Monday, October 12, 2009

NHibernate: stra-puuteeenzaaa!

Scenario:
image 
un dominio molto banale mappato su una singola tabella con una colonna che fa da discriminante, è così, è legacy e me lo devo tenere :-).
Adesso il nostro simpatico utente vuole fare una ricerca per tutti i Subject, a prescindere dal fatto che siano Company o Person, che contengono in Subject.Name qualcosa tipo (Like) ‘%mauro%’: una sorta di full-text.
Banale direte voi:
var criteria = querySpec.Keywords
    .AsSqlServerKeywords()
    .Aggregate( session.CreateCriteria<Subject>(), ( a, kw ) =>
        a.Add( Restrictions.Like( "Name", kw, MatchMode.Exact ) )
    );
dove querySpec è una Specification e AsSqlServerKeywords un banale extension method che data una lista di keywords con i carattery jolly “windows based” (* e ?) li parsa e li rimpiazza con gli equivalenti per Sql Server (% e _) e poi forza una Restrictions.Like con MatchMode a Exact in modo che il generatore di query di NHibernate non raddoppi i caratteri jolly.
Ok, peccato che la proprietà Name non sia mappata perchè non è prevista nello schema della tabella quindi ciccia… quella roba si schianta di brutto.
Grazie al solito onnipresente Marco De Sanctis però ho trovato una soluzione che definirei semplicemente fantastica. Osservate il mapping, fatto con Fluent NHibernate:
this.Map( s => s.Name )
    .FormulaIs( "mySchema.udf_SubjectName( Type, FirstName, LastName, CompanyName )" );
Mappiamo la proprietà su una User Defined Function di Sql Server che NHibernate brillantemente invocherà per ogni riga del result set permettendoci di avere il dato che ci serve ma soprattutto permettendo ad NHibernate di generarci delle query.
Fantastico! Non c’è altro da dire.
.m

Saturday, October 10, 2009

AOP, Validazione e performance: un po’ di considerazioni

Tempo fa ho introdotto PostSharp, dal primo approccio è passato ormai un anno e da qualche mese lo uso con soddisfazione, in produzione, per la gestione della parte noiosa del tracing.
In questi giorni ho ripreso in mano la parte di AOP per la validazione dei parametri di input di un metodo, tipicamente quello che si fa è questo:
void StandardValidation( String v )
{
    if( v == null )
    {
        throw new ArgumentNullException( "v" );
    }
}
oppure, evolvendosi leggermente, si può scrivere questo:
void EnsureValidation( String v )
{
    Ensure.That( v ).Named( "v" ).IsNotNull();
}
che è effettivamente più elegante e anche leggibile (pensatelo proiettato in produzione e quindi applicato in uno scenario complesso). Resta però il fatto che è da scrivere e che sta li in mezzo ai piedi, ricorrendo a PostSharp si può arrivare a questo:
void AspectValidation( [NotNull]String v )
{

}
che ha lo stesso identico risultato e inoltre ha il pregio notevole, IMHO, di non avere nulla in mezzo ai piedi.
E le perfomance?
tutto bello e funzionale, ma le prestazioni che fine fanno? come al solito dipende da quali sono le nostre necessità: non abbiamo un problema di prestazioni finchè qualcuno non ci dice che va piano.
detto questo però ho voluto provare un test molto casalingo ma ridotto all’osso in modo che l’unica variabile fosse proprio o il codice per la validazione o il codice per il tracing.
Qui di seguito uno screenshot con i risulati, interessanti direi.
image
  1. la prima batteria di test è un’iterazione (100k volte) chiamando un metodo con 1 solo parametro, come nel codice di esempio del post, che esegue la validazione tradizionale;
  2. la seconda è sempre un’iterazione di 100k volte che però chiama un metodo, nei tre modi noti, con 5 parametri… con risultati curiosi: la validazione classica cuba un tempo doppio ma comunque irrilevante, la validazione con le fluent interface, “Ensure”, invece scoppia passando da 198ms a 955ms, pessimo non c’è che dire; la terza invece curiosamente è quella che in termini percentuali perde pochissimo nonostante tempi comunque molto alti in termini assoluti;
E’ altresi vero che se misuriamo la singola chiamata il tempo in tutti i casi scende a 0ms, il terzo test viene penalizzato dall’infrastruttura di PostSharp 1.5 che permette cose mirabolanti ma in questo scenario inutili, il tutto in attesa di PostSharp 2.0 (oggi in CTP1) che promette, e i test iniziali lo dimostrano, di fare una “code injection” intelligente e quindi eliminare l’overhead a runtime ove non necessario.
Il secondo test, quello del tracing/logging, in effetti fa questo, in questo caso la fa con uno Weaver per PostSharp 1.5, uno Weaver è un plugin per PostSharp che inietta codice custom direttamente a compile time scrivendo esattamente il codice che scriveremmo noi senza aver bisogno del runtime di PostSharp, che infatti non è più una dipendenza a runtime. Il piccolo problema, sempre in attesa di PostSharp 2.0, è che scrivere un Weaver è un delirio :-)
CompileTimeValidate
Una nota infine su quella che secondo me è una delle feature migliori e più sottovalutate di cui ho già parlato. In attesa di “Compiler-As-A-Service” di cui si sa poco o nulla sarebbe un gran bello avere degli attributi che vengono considerati dal compilatore come ad esempio ObsoleteAttribute.
Si… può… fare…!
Non vi tedio con i dettagli tecnici, che ci sono nel post che ho linkato, ma mi soffermo sul fatto che mentre con la versione 1.0 era impossibile fare molte cose a compile time con la 1.5 si può fare pressochè tutto permettendo di intervenire nel processo di compilazione e ad esempio validare anche cose che per il compilatore sarebbe impossibile anche solo prendere in considerazione.
.m

Wpf e la message pump

La necessità è quella di intercettare il plug/unplug di un qualsiasi device USB. Il problema è che il sistema operativo sparapacchia la notifica nella massage pump e il subclassing in Wpf è concetto inesistente…
Quindi?
E’ un po’ contorto ma alla fine con una manciata di righe di codice ci si arriva:
Public Interface IHardwareMonitor
    Event DeviceAttached As EventHandler
    Event DeviceRemoved As EventHandler
End Interface
definiamo quello che vogliamo ottenere: un semplice observer.
The Trick
A questo punto lo possiamo implementare, ad esempio, in questo modo:
Public NotInheritable Class WpfHardwareMonitor
    Implements IHardwareMonitor

    Public Event DeviceAttached(ByVal sender As Object, ByVal e As System.EventArgs) Implements Plc.IHardwareMonitor.DeviceAttached
    Private Sub OnDeviceAttached()
        RaiseEvent DeviceAttached(Me, EventArgs.Empty)
    End Sub

    Public Event DeviceRemoved(ByVal sender As Object, ByVal e As System.EventArgs) Implements Plc.IHardwareMonitor.DeviceRemoved
    Private Sub OnDeviceRemoved()
        RaiseEvent DeviceRemoved(Me, EventArgs.Empty)
    End Sub

    Dim source As HwndSource

    Public Sub New()

        AddHandler Application.Current.Exit, AddressOf OnAppExit
        AddHandler Application.Current.MainWindow.Loaded, AddressOf OnLoaded

    End Sub

    Sub OnAppExit(ByVal sender As Object, ByVal e As ExitEventArgs)
        source.RemoveHook(New HwndSourceHook(AddressOf WndProc))
        RemoveHandler Application.Current.Exit, AddressOf OnAppExit
    End Sub

    Sub OnLoaded(ByVal sender As Object, ByVal e As RoutedEventArgs)
        source = HwndSource.FromHwnd(New WindowInteropHelper(Application.Current.MainWindow).Handle)
        source.AddHook(New HwndSourceHook(AddressOf WndProc))

        RemoveHandler Application.Current.MainWindow.Loaded, AddressOf OnLoaded
    End Sub

    Const WM_DEVICECHANGE As Integer = &H219
    Const DBT_DEVICEARRIVAL As Integer = &H8000
    Const DBT_DEVICEREMOVECOMPLETE As Integer = &H8004

    Function WndProc(ByVal hwnd As IntPtr, ByVal msg As Integer, ByVal wParam As IntPtr, ByVal lParam As IntPtr, ByRef handled As Boolean) As IntPtr
        If msg = WM_DEVICECHANGE Then

            If wParam.ToInt32() = DBT_DEVICEARRIVAL Then
                Me.OnDeviceAttached()
            ElseIf wParam.ToInt32() = DBT_DEVICEREMOVECOMPLETE Then
                Me.OnDeviceRemoved()
            End If
        End If
    End Function

End Class
Il giochetto di fondo è quello di risuscire a ricavare l’handle di una finestra, top level, che in Wpf non serve:
source = HwndSource.FromHwnd(New WindowInteropHelper(Application.Current.MainWindow).Handle)
source.AddHook(New HwndSourceHook(AddressOf WndProc))
Il resto è normale codice per la gestione dei messaggi.
ndr
E’ VB.Net :-), mamma mia che fatica! non so se è perchè lo uso veramente di rado ma lo trovo decisamente ostico, decisamente. E’ evidente che dal punto di vista delle potenzialità non c’è differenza ma alcune cose mi laciano molto perplesso, molto.

.m

Friday, October 9, 2009

Thursday, October 8, 2009

Ho d.d.d.isinstallato ReSharper…

… per la seconda volta, insopportabile.
  1. nei file xaml è inutilizzabile, oltre al fatto che ha un fastidiosissimo bug (di lunga data) per cui il resize dell’editor smin*hia (scusate il francesismo) tutto;
  2. in certi momenti lo trovo mostruosamente invasivo;
  3. molte operazioni sminch*ano (a rieccolo :-)) il focus, per cui fanno si quello che devono ma poi il focus non è più sull’editor dove eravate prima… e sminchi*te voi tutto :-)
  4. il suo intellisense quando utilizzate un po’ di lambda syntax (ad esempio per agganciare gli handler agli eventi) prende decisioni decisamente discutibili: “ddd” :-D
  5. su solution corpose le prestazioni continuano a essere un problema e se siete abituati, come me, ad avere ben più di un’istanza di Visual Studio aperta ecco che il danno è ben visibile;
Good feedback, send me an eMail! :-)
.m