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

Un domain model per lavorare con WPF e NHibernate

Da quando ho scoperto NHibernate, ormai più di un anno fa, ho sempre disegnato il mio domain-model in modo tale che far persistere le mie entità con questo framework fosse il più naturale possibile. Dal punto di vista pratico, ciò significa che tutte le entità del domain-model derivino tutte da una classe astratta Entity, che definisce una sola proprietà ID di tipo int.

public abstract class Entity { public int ID { get { return _id; } set { _id = value; } } }

Ogni volta che una classe del mio domain-model deve essere persistita attraverso NHibernate, la faccio derivare da Entity. In questo modo, ogni classe espone una proprietà ID che contiene la chiave univoca dell'istanza. Ovviamente tutte le entità hanno un unsaved-value = 0, e questo permette all'engine di NHibernate di capire in che modo persistere una determinata istanza sul database. Quando ID = 0, l'oggetto esiste solo in memoria (oggetto transiente) e non è stato ancora persistito. Se ID != 0, l'oggetto in memoria esiste anche sul database (oggetto persistente).

Tutto deriva da Entity? Bene, un vantaggio anche per il mio DAL
Il fatto che tutte le mie entità derivino dalla classe astratta Entity porta anche un vantaggio all'interno del DAL. Di solito, i miei DAL utilizzano i generics. Pertanto, posso applicare un constraint, dicendo che il tipo T deve per forza rappresentare una classe derivata da Entity. La morale di tutto questo è che non posso istanziare un'istanza di DataProvider<int> o un DataProvider<MyAppSettings>, ma solo di DataProvider<Artista>, DataProvider<Auto>, DataProvider<Competenza> e così via, cioè solo di quelle classi che so che dovranno essere rese persistenti da NHibernate.

Fin qua nulla di male. Cosa succede però se devo creare un domain-model da utilizzare all'interno di un'applicazione WPF? Con .NET 2.0, ad esempio, per poter sfruttare le potenzialità di data-binding sulle Windows Forms era opportuno creare un domain-model le cui classi implementassero l'interfaccia INotifyPropertyChanged. Questa interfaccia consente l'implementazione di un data-binding efficiente tra un oggetto ed i controlli sulla UI, e viceversa. Questo approccio funziona ancora in WPF, non è cambiato nulla da questo punto di vista, ma per tutta una serie di ragioni è opportuno ragionare in modo diverso.

Perchè utilizzare le dependency properties?
Questo "modo diverso" consiste nell'utilizzare le dependency properties, che consentono di sfruttare features avanzate come data-binding serio, che con WPF va molto al di là delle potenzialità offerte da .NET 2.0. Per esempio, potremmo bindare la proprietà Eta della classe Artista con uno Shape di qualche tipo, in modo tale che a seconda dell'età il Rectangle che abbiamo sulla Window si allunghi o si accorci. Oppure, potremmo definire un trigger sulla proprietà booleana PossiedeAuto, in modo tale che quando vale true, appaia un'immagine da qualche parte, altrimenti no. In generale, molte delle funzionalità di WPF (stili, template, trigger, data-binding, animazioni, etc.) sfruttano proprio le dependency properties: se sappiamo che il nostro domain-model dovrà in qualche modo interagire con l'engine di WPF, forse è bene pensarci prima e disegnare le nostri classi in modo opportuno.

Quando un oggetto intende utilizzare le dependency properties, deve derivare dalla classe DependencyObject di WPF. Qui abbiamo un problema: le entità del nostro domain-model dovrebbero derivare da due classi, sia DependencyObject che Entity. Questo in .NET non è possibile, in quanto non è prevista ereditarietà multipla. Poco male, è sufficiente pensare diversamente e invece di aver a che fare con una classe Entity, definiamo un'interfaccia IPersistableEntity (dal nome altisonante :-).

using System; using System.Windows; namespace DomainModel { public interface IPersistableEntity { int ID { get; set; } } public class Artista : DependencyObject, IPersistableEntity { public static readonly DependencyProperty IDProperty; public int ID { get { return (int)GetValue(IDProperty); } set { SetValue(IDProperty, value); } } static Artista() { Type tp = typeof(Artista); IDProperty = DependencyProperty.Register("ID", typeof(int), tp); } } }

La classe Artista qui sopra è stata compressa/adattata/sistemata per riportare parte del codice. Il codice di produzione è evidentemente organizzato un po' meglio e migliorato sotto diversi aspetti. Comunque sia, la classe eredita da DependencyObject ed implementa l'interfaccia IPersistableEntity. Quest'ultima in particolare richiede l'implementazione di una proprietà ID di tipo int, che è stata realizzata come dependency property. Questo implica una serie di cose che sono ben visibili dal codice: un field pubblico IDProperty di tipo DependencyProperty, un costruttore privato che registra tutte le dependency properties che mi servono.

Conclusione
Implementando il domain-model secondo questi criteri, sviluppo classi che possono lavorare sull'interfaccia di WPF e possono essere persistite da NHibernate passando dal DAL del progetto che sto sviluppando (progetto per una volta non freeware).

Print | posted on Monday, March 19, 2007 12:54 PM | Filed Under [ Sviluppo .NET ]

Feedback

Gravatar

# re: Un domain model per lavorare con WPF e NHibernate

pur'io fo la stessa cosa solo che la mia classe non si chiama Entity ma DomainObject.
3/19/2007 1:05 PM | Alessandro Scardova
Gravatar

# Re: Un domain model per lavorare con WPF e NHibernate

x fabio:
Prediligo una gerarchia di classi potenzialmente meno profonda, e semmai comporre più interfacce.
3/19/2007 2:03 PM | Igor Damiani
Gravatar

# re: Un domain model per lavorare con WPF e NHibernate

fa uno strano effetto vedere un oggetto legato da due contesti profondamente lontani (Db e GUI).
3/19/2007 2:29 PM | Tommaso Caldarola
Gravatar

# Re: Un domain model per lavorare con WPF e NHibernate

x Tommaso:
sì, sono d'accordo, sebbene l'abbia fatto io. Diciamo che il legame con il db non è così forte, la presenza di una property ID di per sè non aggiunge complessità.
Per le dependency property, ci penso su... :-)
3/19/2007 3:31 PM | Igor Damiani
Gravatar

# re: Un domain model per lavorare con WPF e NHibernate

Ti do ragione. Il sistema che a breve rilascerò ha questa caratteristica. Puristicamente non è bello ma è maledettamente comodo ;-)
3/19/2007 5:24 PM | Tommaso Caldarola
Gravatar

# re: Un domain model per lavorare con WPF e NHibernate

Igor, complimenti per il post. Mi è piaciuto davvero molto.
3/20/2007 7:37 AM | Luciano Castro
Gravatar

# re: Un domain model per lavorare con WPF e NHibernate

Nope, quello che WPF necessita è l'essere informato del fatto che la proprietà è cambiata, le possibili alternative sono:
1-Usare una DP
2-Implementare INotifyPropertyChanged :-)

Come ho scritto in precedenza la presenza di una DP è mandatory lato target:

BindingBase b= FrameworkElements.SetBinding(DependencyProperty dp,BindingBase binding);

Ma in Binding.Source non ci sono vincoli:
http://msdn2.microsoft.com/en-us/library/system.windows.data.binding.source.aspx

Ovviamente questo non basta in quanto hai bisogno di sapere quando una lista viene modificata e quindi implementare INotifyCollectionChanged (ma ti leghi a WPF) oppure usare un ObservableCollection che wrappa le entities UI neutral.
3/21/2007 3:13 PM | Corrado Cavalli
Gravatar

# re: Un domain model per lavorare con WPF e NHibernate

Personalmente cerco di realizzare Domain Model completamente "agnostici" sia al DB che al Presentation Layer, Facade e Collections (che qualcuno chiama DAL, giustamente)

Il metodo che ho scelto per risolvere questo problema è implementare una bella classe (o meglio, una gerarchia) EntityView - EntityViewType con supporto a tutte le interfacce per il binding, supporto agli array ritornati da HQL (EntityViewArray)

Piu passa il tempo piu comincio a pensare che la Reflection nei linguaggi Managed abbia cambiato profondamente anche l'approccio stesso alla OOP.

Credo, IHMO, che sia la soluzione piu' pulita, ritengo inaccetabile un prerequisito di design come un'interfaccia della FCL del Domain Model.
3/29/2007 11:48 PM | Dario Cecere
Comments have been closed on this topic.

Powered by:
Powered By Subtext Powered By ASP.NET