Appena qualcuno nomina XAML, la maggior parte delle persone pensa immediatamente a complesse interfacce colme di effetti grafici mozzafiato. Certo, XAML è lo strumento principe per la loro realizzazione, però troppi dimenticano cos'è realmente XAML. Io stesso ho raggiunto una diversa consapevolezza solo chiacchierando con Raffaele Rialdi durante la WPC, e tale consapevolezza mi ha consentito di usarlo da subito anche se in realtà non è di user interface che ho bisogno. Veniamo al dunque: XAML è essenzialmente il miglior modo di serializzare un grafo di oggetti che sia mai stato inventato. Ciò lo rende uno strumento favoloso per rappresentare la gerarchia di oggetti che compone una user interface, ma se guardate la cosa da un diverso punto di vista potete intravedere una serie di possibilità interessanti che si aprono. All'interno degli assembly che costituiscono WPF ci sono un paio di classi statiche che sono in grado di prendere qualsiasi (o quasi) oggetto con tutta la sua gerarchia, serializzarlo in XAML e in seguito recuperarlo trasformando il markup nuovamente nell'oggetto di partenza. La bellezza di XamlWriter e XamlReader è che praticamente sono in grado di fare tutto ciò senza la minima traccia degli attributi di serializzazione cui siamo stati abituati da XmlSerializer.
Ora, prendete XAML, aggiungete un generosa dose di NHibernate, un poco di astuzia e avrete ottenuto un cocktail folgorante. Immaginate di avere degli oggetti mappati con NHibernate su un database, poco importa di che oggetti parliamo e che struttura hanno le tabelle, ma come spesso accade vi trovate ad avere la necessità di gestire delle proprietà dinamiche, un po' come fa Dynamics oppure WCF con IExtensibleObject. Succede talvolta che per gestire le customizzazioni sia necessario consentire alle classi di avere più proprietà di quelle definite nella loro dichiarazione. Si sa, i clienti riescono sempre ad inventare qualcosa che non avevamo previsto. Se avete una necessità di questo tipo e state lavorando con NHibernate, probabilmente avete un grosso problema. Il fatto è che se scrivete un file di mappatura dovete metterci tutte le proprietà e di certo non potete aggiungerne a runtime. Dovrete quindi gestirvi le mappature a mano generando i file XML da dare in pasto all'ORM e anche così i problemi che rimangono sono davvero tanti.
Bene, allora vi propongo una soluzione un po' diversa che ho adottato recentemente. Prendete il vostro database, qualunque esso sia e aggiungete alla opportuna tabella una campo di testo, il più grande possibile, un clob in Oracle, un ntext in SqlServer. Se avete la fortuna di usare SQL Server 2005 mettete direttamente un campo di tipo XML. In questo campo andranno a finire tutte le proprietà aggiuntive, di qualunque tipo esse siano, serializzate come un Dictionary<string, object> in XAML. Ora passate alla vostra classe che rappresenta l'oggetto di business e aggiungete una proprietà Xaml privata e associatela mediante il file di mappatura al campo di database di cui sopra. Una interessante caratteristica di NHibernate è di poter mappare anche i campi e le proprietà private. In questo caso è importante mappare una proprietà privata perchè i dati serializzati in XAML non devono mai essere raggiunti in questa forma ma al momento in cui sono assegnati dal runtime di NHibernate alla proprietà devono essere deserializzati e copiati in un field di tipo Dictionary<string, object> cui ho accennato poco fa. Questa operazione è semplice utilizzando le classi XamlWriter e XamlReader. Ecco come:
private string Xaml
{
get { return XamlWriter.Save(dictionary); }
set { dictionary = XamlReader.Load(value) as PropertyDictionary }
}
L'unica accortezza da seguire è quella di creare una classe PropertyDictionary che estenda Dictionary<string, object>. Questo perchè XamlWriter e XamlReader non sono in grado di operare su tipi generici, perciò bisogna ingannarli in questo modo. Ora, esponendo il Dictionary avrete ottenuto la vostra classe estensibile, perfettamente integrata con NHibernate. Mettete nelle proprietà praticamente quello che vi pare, e questo verrà automaticamente serializzato e deserializzato senza colpo ferire e naturalmente NHibernate provvederà a persistere il tutto sul database.