Nhibernate VS EntityFramework - Esperienze reali

Confrontare sulla carta i due principali framework ORM può risultare complesso, specialmente se si è nelle fasi iniziali di un progetto

Info

da adf il 01/01/0001 00:00:00


Categoria
tecnologia, architetture
Tags
database, ORM, nhibernate, Entity Framework
Nhibernate VS EntityFramework - Esperienze reali

Mi è capitato di dover rispondere alla domanda: "Perchè utilizzi un ORM anche per i piccoli progetti?". Molto spesso il tempo concesso dalla discussione non da modo di spiegare bene le ragioni alla base di questa scelta. Per questo motivo vorrei dilungarmi un po' su questo argomento. Dando però per scontato che i benefici degli ORM siano ormai riconsciuti dalla comunità di sviluppatori, vorrei spiegare il perchè del loro uso diffuso all'interno dei miei progetti confrontando i due più diffusi framework in circolazione per .NET: NHibernate e Entity Framework.

Funzionalità Generali

Sia NHibernate che Entity Framework sono Object Relational Mapper in grado di lavorare con oggetti POCO, vale a dire oggetti senza alcuna relazione con il sistema di persistenza scelto. Come ORM, utilizzano i pattern UnitOfWork,,Repository e il modello Query Object(cfr. Patterns of Enterprise Application Architecture). Entrambi permettono di definire le relazioni tra le entità e consentono di "mappare" le relazioni gerarchiche utilizzando i paradigmi Table per Hierarchy (TPH), Table per Class (o joined subclass) (TPC) e Table per concrete type.

L'Entity Framework supporta soltanto (senza l'utilizzo di driver di terze parti) i database SqlServer, SqlServer Express e SqlServer CE. I driver terze parti disponibili per altri database supportano attualmete Oracle e MySQL.

NHibernate invece supporta nativamente SqlServer, SqlServer Express, SqlServer CE, Oracle, Sqlite, PostGreSQL, MySQL e molti altri. NHibernate fornisce il supporto per l'utilizzo della cache, che risulta molto utile per i dati modificati poco frequentemente o per scenari in cui si utilizzano sessioni lunghe. Questa cache è disponibile attraverso plugin aggiuntivi, alcuni dei quali forniti con la libreria base di NHibernate, che sono in grado anche di sfruttare funzionalità native come il Service Broker di SQLServer.

Configurazione

Entity Framework è stato concepito per sfruttare l'approccio Database-First, che quasi costringe lo sviluppatore ad utilizzare il designer integrato all'interno di Visual Studio. Questo designer produce un file XML (EDMX) che descrive le mappature necessarie tra le entità e le tabelle. Il designer però non mette a disposizione l'intera gamma di mappature possibili, costringendo gli sviluppatori, già per progetti di media complessità, a doversi cimentare con il file XML che risulta un po' criptico da manipolare direttamente, e che solo parzialmente può essere modificato in modo sicuro senza che le modifiche vengano poi sovrascritte dal designer. A partire dal file EDMX l'ambiente di sviluppo è poi in grado di generare sia le tabelle che le classi delle entità.

Il grande miglioramento apportato da EF 4 è che il motore per la generazione dei template (noto come T4) per creare le classi è facilmente modificabile per adattare le classi allo stile desiderato. E' mia personalissima opinione che i designer siano più spesso una maledizione che una benedizione, in particolare per l'utilizzo in applicazioni reali: in alcuni casi l'aggiornamento dell'IDE può non essere percorribile, si rischia di modificare il file XML per errore corrompendolo, a volte il designer rallenta di pari passo con l'aumento di complessità della struttura dati.

Per questo motivo la presenza di questo tool non è per me un valore aggiunto.

L'altro approccio rispetto al Database-first, che è poi anche quello seguito da NHibernate, consiste nello sviluppare prima le entità di dominio (le classi vere e proprie) e poi definire il loro rapporto con le tabelle del database utilizzando sia gli attributi dichiarativi o sia API di tipo Fluent. Per quanto mi riguarda preferisco evitare l'utilizzo di attributi dichiarativi per definire i mapping con il database in quanto questo mette le entità in relazione con il sistema di persistenza.

Con NHibernate non vi è alcuna generazione automatica di codice. Nella versione classica è necessario definire le proprie classi di dominio e utilizzare poi dei file XML per descrivere in che modo le tabelle del database si collegano alle entità di dominio. Questi file XML vengono poi utilizzati a runtime da NHibernate per creare il mapping. Lo schema XML non è molto complesso, ma è un po' datato, per questo motivo oggi ha preso molto piede la libreria FluentNhibernate. Si tratta di un progetto open source basato su NHibernate che consente l'uso di classi vere e proprie per il mapping basate su una sintassi fluent che permette di descrivere le relazioni tra le classi delle entità e la struttura della base dati. Una caratteristica interessante di FluentNHibernate è l'automapping, che utilizza le convenzioni (definibili liberamente dall'utente) per mappare le tabelle alle classi senza bisogno di alcuna configurazione da parte dello sviluppatore.

Un'altra differenza molto importante è che Entity Framework non supporta il mapping di enum o di tipi definiti dall'utente, e i campi di database possono mappare solo tipi primitivi. NHibernate invece supporta i tipi personalizzati attraverso l'interfaccia IUserType.

API

Entity Framework di un'interfaccia API più ristretta rispetto ad NHibernate ed espone meno parametri. Per questo motivo, è probabilmente più facile da "imparare", ma nel momento in cui è necessario un maggiore controllo le limitazioni delle API vengono a galla rapidamente.

NHibernate è inoltre molto più estensibile. Ad esempio: Nella costruzione del collegamento, che può essere utile per impostare variabili di sessione utente nel database Caching, come già discusso Intercettazione di query, inserti, eliminare e aggiornamenti Intercettazione di creazione di oggetti Registrazione diagnostica con log4net Per quanto a mia conoscenza, Entity Framework non ha nessuno di questi punti di estendibilità. D'altra parte, con una API più grande arriva una curva di apprendimento ripida. Ad esempio, la sessione NHibernate ha i seguenti metodi: Save, aggiornamento, SaveOrUpdate, SaveOrUpdateCopy, Replica e unire, alcuni dei quali hanno sovraccarichi che restituiscono tipi diversi. Il Lazy loading o ansiosi è una considerazione enorme, soprattutto con le applicazioni n-tier. Desideroso di carico può uccidere perché si finisce per succhiare giù l'intero database in una singola chiamata, ma lazy-loading può dare luogo al famigerato problema prestazionale delle n-select. Entity Framework è più immaturo da questo punto di vista perché da la possibilità di selezionare il comportamento lazy loading soltanto per l'intero contesto, senza poterlo applicare granularmente su una singola entità o relazione.

Con NHibernate invece è possibile configurare il comportamento in modo che oggetti principali siano caricati direttamente mentre gli insiemi siano lazy-loaded, e queste due strategie possono essere combinate a piacere fino a coprire tutte le situazioni possibili. NHibernate permette in definitiva una maggiore capillarità delle strategie di caricamento dei dati. È possibile caricare subito gli oggetti utilizzando una join o una sub-select e anche specificare la dimensione ottimale del batch. Nessuna di queste funzioni è disponibile con Entity Framework. Una strategia sub-select è utile per prevenire le query inutili per gli oggetti che si trovano già nella sessione, e può essere valutata caso per caso. Sia Entity Framework e NHibernate supportano Linq, anche se l'implementazione è diversa. In Entity Framework le query LINQ vengono passate al provider LINQ SQL nativo mentre NHibernate Linq è tradotto in HQL per passare poi alla generazione di SQL.

Da una prospettiva API la sensazione è più o meno lo stessa. Oltre a Linq, NHibernate fornisce ben tre tipologie di API: QueryByObject, ICriteria e HQL. Con un sistema di API così vasto, il mio consiglio è quello di sceglierne uno e di utilizzarlo sempre in modo coerente, preferibilmente all'interno di un repository pattern. Un'altra importante differenza è la possibilità di specificare il comportamento da seguire in cascata sugli oggetti figli. NHibernate consente di specificare se una relazione deve causare in cascata inserimento, aggiornamento, cancellazione ed eliminazione dei record orfani. EntityFramework invece permette soltanto di scegliere, sul comportamento in cascata, se effettuare o meno la cancellazione.

Supporto

Entity Framework, prodotto e supportato da Microsoft ha il vantaggio rispetto ad NHibernate di avere un parco sviluppatori più ampio in grado di rispondere alle a dubbi o risolvere problemi. La documentazione MSDN per Entity Framework è relativamente completa e sono presenti molti Evangelist che hanno adottato Entity Framework.

N

Hibernate d'altra parte non ha una azienda produttrice dietro, e la qualità della sua documentazione è sicuramente un punto debole per gli sviluppatori alle prime armi che vogliano apprenderne tutti i segreti. Ancora oggi, Google fornisce come sito di riferimento per NHibernate il sito di JBoss, ma l'attuale "home" di NHibernate è http://www.nhforge.org. Questo fatto, da solo, fa perdere tempo a molti sviluppatori.

Come detto in precedenza, le API per NHibernate sono molto più capillari di Entity Framework, e già questo dovrebbe spingere gli sviluppatori a porre maggiore enfasi sulla documentazione. Imparare ad usare correttamente NHibernate può essere un'esperienza lunga e a tratti frustrante, ma devo dire che i benefici sul lungo periodo sono notevoli. Entity Framework è meno maturo di NHibernate, e temo che dorà passare per numerose release prima di arricchirsi di funzionalità enterprise. Speriamo inoltre che Microsoft continui a supportare le vecchie versioni mantenendo le funzionalità legacy nelle nuove.

NHibernate è un progetto opensource che finora ha avuto successo senza un partner commerciale.

Conclusione

In questo articolo ho toccato numerosi punti in base alla mia esperienza su applicazioni n-tier reali sia con Entity Framework che con NHibernate. Per concludere, direi che Entity Framework è più facile da imparare ma difficile da controllare ed estendere ad uno scenario complesso. Con l'aumentare della complessità del dominio e delle relazioni ho puntato sempre di più verso NHibernate configurato tramite FluentNHibernate.

In ogni caso, ci tengo a sottolineare l'importanza di mantenere separati l'ORM dalle classi dell'applicazione, anche attraverso l'uso di una libreria IoC (come NInject) addove necessario. Senza dubbio NHibernate è al momento la scelta migliore per le applicazioni enterprise, ma potrebbe non essere sempre così.

Un ultima parola sull'importanza del team di sviluppo. Francamente, lavorare con un ORM e con classi di dominio è una cosa completamente diversa dallo sviluppo tramite script su database e recorset.

E' richiesta maggiore competeza quando entrano in gioco nuovi layer. Alcuni sviluppatori trovano la transizione troppo onerosa e non la prendono neanche in considerazione quando si deve decidere l'architettura del sistema da sviluppare. A tal proposito, vorrei dire che i vantaggi offerti da un ORM sono infiniti:

  • possibilità di modificare la struttura del database senza dover andare a cercare le query SQL sparse per il codice o all'interno delle Stored Procedure
  • possibilità di generare query complesse in poche righe di codice
  • possibilità di non perdere mai la visione ad oggetti dell'applicazione

Infine, oltre a semplificare tutta una serie di operazioni tipiche delle applicazioni enterprise, un ORM è utile soprattutto per ridurre la complessità di mapping di un dominio complesso in un database relazionale. Se il progetto è semplice, con nessuna relazione particolare, o nessuna complessità di dominio, l'ORM sembrerebbe soltanto introdurre una notevole complessità senza alcun beneficio percepibile. In realtà, abituarsi al suo utilizzo a partire da progetti semplici, aiuterà sia a dominare gradualmente le API, sia a cambiare modo di ragionare e ad allontanarsi dalla visione a RecordSet. Una volta acquisita dimestichezza, il setup di un progetto in NHibernate piuttosto che in ADO non richiederà sforzi maggiori ma porterà ad innumerevoli benefici in termini di flessibilità e manutenibilità, garantendo la corretta ingegnerizzazione di ogni applicazione.

More For Less utilizza gli ORM all'interno di tutti i suoi progetti per velocizzare gli sviluppi e per garantire flessibilità al data layer


Facebook Twitter Google Digg Reddit LinkedIn Pinterest StumbleUpon Email Stampa