Technology Experience

Contenuti gestiti da Igor Damiani
posts - 949, comments - 2741, trackbacks - 15120

My Links

News

  • Questo blog si propone di raccogliere riflessioni, teoriche e pratiche, su tutto quello che riguarda il world-computing che mi sta attorno: programmazione in .NET, software attuale e futuro, notizie provenienti dal web, tecnologia in generale, open-source.

    L'idea è quella di lasciare una sorta di patrimonio personale, una raccolta di idee che un giorno potrebbe farmi sorridere, al pensiero di dov'ero e cosa stavo facendo.

    10/05/2005,
    Milano

Archives

Post Categories

Generale

[5] NHibernate: un caso tratto da un'applicazione reale

Nella mia piccola serie di post dedicati ad NHibernate (1, 2, 3 e 4) abbiamo visto come creare un domain model e come persisterlo con NHibernate. Abbiamo visto come mappare ogni singola proprietà, come creare relazioni uno-a-molti, come implementare un data provider, etc. etc. In questi ultimi giorni ho voluto a tutti i costi raggiungere un altro risultato, per il quale ho davvero googlato molto, ho chiesto, ho letto. Molte pagine sul Web riportano molte soluzioni diverse, che però non mi hanno soddisfatto per nulla. Mi sto riferendo alla possibilità di sfruttare collezioni fortemente tipizzate nel nostro domain model, nell'ottica di avere un domain model che possa esporre correttamente collection adatte a gestire gli oggetti che ci servono. E' passato un po' di tempo da quei post, perciò preferisco riprendere la situazione per un brevissimo riassunto.

Riassunto delle puntate precedenti
Nei post linkati sopra, parlavamo semplicemente di una classe HockeyPlayer, che esponevano una collection Faults. Il codice dei field privati, copiati & incollati dal post originario, è questo:

public class HockeyPlayer : INotifyPropertyChanged
{
    
#region Field
    private int 
id;
    
private string name;
    
private int height;
    
private int weight;
    
private int number;
    
private IList faults;
    
#endregion
}

Il succo del discorso è proprio questo. La proprietà Faults è esposta attraverso l'interfaccia IList, più che adatta per contenere uno o più elementi ma, lo sappiamo bene, non tipizzati. Difatti, il costruttore della classe è questo:

public HockeyPlayer(string Name, int Height, int Weight, int Number)
{
    name = Name;
    height = Height;
    weight = Weight;
    number = Number;
    faults = 
new ArrayList();
}

Il tipo concreto attraverso il quale utilizziamo la proprietà Faults è un ArrayList che per sua natura può contenere qualsiasi tipo di oggetti, e non è tipizzato. .NET 2.0 ci permette, con i generics, di usare collection tipizzate, molto utili per rendere un po' più robusta la nostra applicazione e il domain model sottostante. Modifichiamo radicalmente quindi la classe HockeyPlayer e facciamo in modo che l'elenco dei Faults di ogni giocatore sia type-safe. Innanzitutto, creiamo una classe FaultsCollection che non fa altro che derivare dalla classe BindingList<Fault>. Il codice è il seguente:

public class FaultsCollection : BindingList<Fault>
{
    
public FaultsCollection() { }

    
public FaultsCollection(IList lista)
    {
        
foreach (Fault f in lista)
            
this.Add(f);
    }
}

Ricordo che di default il compilatore crea automaticamente il costruttore vuoto della classe, a patto però che non ne creiamo di nostri. Se creiamo un secondo costruttore - come nel codice qui sopra - siamo obbligati ad esplicitare anche il primo. Fatto questo, possiamo modificare la classe HockeyPlayer ed utilizzare FaultsCollection.

// Field privato
private FaultsCollection faults;
// Proprietà pubblica
public FaultsCollection Faults
{
    
get return faults; }
    
set { faults = value; NotifyPropertyChanged("Faults"); }
}

Nulla di particolarmente complicato. Il vero problema arriva adesso.

Persistiamo la classe modificata con NHibernate
La faccio breve: se in un business object esponiamo una nostra custom collection (come abbiamo fatto), NHibernate si lamenta un po'. Il motivo è che NHibernate tenta di convertire la collection nella classe NHibernate.Collections.Bag. Tale classe implementa l'interfaccia IList, allo stesso modo della nostra FaultsCollection. Alla fine, dopo numerosi esperimenti e test, sono arrivato alla conclusione che NHibernate riesce a castare FaultsCollection ad IList (e ci mancherebbe altro), ma non riesce giustamente a castare da Bag e FaultsCollection.

Le soluzioni che ho trovate sul Web nelle settimane scorse parlano di creare classi wrapper, oppure di usare i generics di NHibernate (e nella fattispecie la classe EntitySet<T>), che tra l'altro fanno parte di una release ancora in alpha). Le classi wrapper le ho escluse a priori: se ho n classi che espongono custom collection, per ciascuna di esse devo creare la classe wrapper corrispondente. Ad esempio, se ho HockeyPlayer, devo implementare anche HockeyPlayerRepository, uguale in tutto e per tutto alla prima, solo che usa IList invece della FaultsCollection. Ho voluto quindi creare un domain model usando esclusivamente FX2.0, senza interferenze esterne.

La mia soluzione: una proprietà wrapper
Chiamatelo workaround, chiamatelo sporco, chiamatelo come lo volete, ma io ho raggiunto un ottimo compromesso creando semplicemente una seconda proprietà nascosta che non fa altro che esporre FaultsCollection attraverso un casting a IList.

[Browsable(false)]
[EditorBrowsable(EditorBrowsableState.Never)]
public IList NHFaults
{
    
get return (IList)faults; }
    
set { faults = new FaultsCollection(value); }
}

L'accessor get non fa altro che castare, mentre il set in realtà va a valorizzare faults attraverso il costruttore che abbiamo visto prima, che accetta un solo parametro in input di tipo IList. Attraverso l'utilizzo degli attributi Browsable e EditorBrowsable, evito che questa proprietà venga visualizzata nell'Intellisense o in un'eventuale finestra Proprietà. Fatto questo, ho dovuto poi modificare il file di mapping di NHibernate: il motore di persistenza dovrà usare la proprietà NHFaults, che in realtà accede ai dati contenuti nel field privato faults.

Qualche dettaglio in più...
Ci tengo a precisare che noi nel nostro codice non dovremmo mai usare NHFaults. Questo è il motivo per cui l'ho resa nascosta con gli attributi di cui parlavamo prima. Il domain model deve sempre e comunque passare dalla collection tipizzata FaultsCollection, creata proprio per questo motivo. L'unico componente che utilizzerà NHFaults sarà proprio NHibernate che:

  1. quando chiede all'oggetto il valore di NHFaults, lo ottiene semplicemente castando a IList la nostra custom collection
  2. quando imposta il valore, valorizzerà direttamente FaultsCollection

Ogni osservazione o critica, se costruttiva ed intelligente, è ovviamente ben accetta!

powered by IMHO 1.2

Print | posted on mercoledì 19 luglio 2006 15:52 | Filed Under [ Sviluppo .NET ]

Feedback

Gravatar

# re: [5] NHibernate: un caso tratto da un'applicazione reale

Io uso questo modello (grazie a Giancarlo) e devo dirti che non c'è bisogno di avere le collection da persistere "public".
20/07/2006 12:03 | Tommaso Caldarola
Gravatar

# re: [5] NHibernate: un caso tratto da un'applicazione reale

Claudio io sto cercando una soluzione per mappare in una array una serie di campi del del tipo value1, value2, value3 etc. ect.
Potresti spiegare meglio la tua soluzione dell'utilizzo dell'attributo "access".

Grazie
20/07/2006 12:26 | Alessandro Cavalieri
Comments have been closed on this topic.

Powered by:
Powered By Subtext Powered By ASP.NET