Meglio, più in generale, non funziona più l’intellisense per qualsiasi documento XML per cui abbiamo uno ( o più.. ) XSD. Questo accade perchè Visual Studio trova più sorgenti per lo schema dichiarato nell’ XML, nel caso NHibernate abbiamo la seguente dichiarazione in testa: <?xml version="1.0"?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="....." namespace="....." >
Se abbiamo nella solution, o nella directory %Program Files%/Microsoft Visual Studio XXX/Xml/Schemas uno o più file che definiscono lo stesso namespace, in questo caso <xs:schema targetNamespace="urn:nhibernate-mapping-2.2" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns="urn:nhibernate-mapping-2.2" elementFormDefault="qualified" attributeFormDefault="unqualified">
Visual Studio trova un ambiguità per cui smette di fornire l’intellisense. La situazione si può risolvere facilmente senza disperare:
Basta cliccare sul documento XML da visual studio e dalla finestra proprietà editare la voce Schemas come mostrato qui sotto:
Così facendo si apre una finestra in cui è possibile abilitare/disabilitare gli schema che visual studio intende usare:
Deselezionando uno degli schemi ambigui tutto torna a funzionare. Ovviamente questo funziona per qualsiasi ambiguità di schema, non solo per NHibernate.
In questa parte costruiamo un progetto con NHibernate che ci consentirà di persistere una prima entità. In effetti la parte OR/M sarà tenuta il più semplice possibile, niente reference o collezioni o componenti: l’obiettivo è creare uno scheletro funzionante e testabile. Ovviamente tutto quanto detto servirà da spunto, alcune cose potranno sembrare ovvie, altre meno, fate come credete meglio, ma poichè questo è un corso “da zero” voglio soffermarmi su tutti i punti. Decidiamo di mettere le entitità in una Class Library ( dll ). Questa è una scelta che probabilmente sarà conveniente fare anche per una soluzione vera da mandare in produzione. Creiamo quindi una nuova solution con Visual Studio. Distinguiamo il nome della soluzione con NHFromScratch.All, ed aggiungiamo all interno un nuovo progetto di tipo class library, che andiamo a chiamare NHFromScratch.Entities. All’ interno della cartella radice della solution, creiamo una sottocartella “Lib”, atta a contenere le dipendenze della soluzione ( NHibernate e non ), ad esempio in aggiunta alle dipendenze di NH vedete la nunit.framework.dll, dipendenza che ci servirà a breve quando vorremo testare le nostre entità. Bene, il passo successivo è creare il mapping per la prima ed unica entità che vogliamo gestire. Ci sono una moltitudine di strategie per creare un mapping per NH, quella che presento qua è una soluzione “plain vanilla”, senza nessun fronzolo e a costo zero: con la pratica ognuno sceglie il metodo che meglio crede, ma per imparare è mia opinione che scrivere il file XML sia di aiuto. Aggiungiamo quindi una sottocartella “Mapping” nella class library appena creata, e in questo folder creiamo un nuovo file con il nome della entità ( Customer nel nostro caso ) con l’estensione .hbm.xml. Se abbiamo abilitato l’intellisense, come fortemente consigliato nella parte 1 l’unico tag che dobbiamo ricordare a memoria è <hibernate-mapping>, tutti gli altri saranno esposti via intellisense dopo che il namespace sarà correttamente specificato, come mostrato qui sotto: Cosa importantissima da ricordare, ricordiamo di impostare “Embedded Resource” come build action sul file di mapping, altrimenti… la configurazione di NHibernate non funzionerà. Ad essere precisi questo si potrebbe evitare, ma per farlo dovremmo distribuire i file di mapping insieme all’applicazione, ed è un’opzione che francamente non mi piace e nemmeno ho mai visto praticare. Fatto questo non rimane che scrivere difatto il mapping. In questo primo test useremo solo tre elementi di mapping: class,key e property, praticamente il minimo indispensabile per avere un entità al lavoro. Approfondiremo i vari aspetti nelle prossime parti. 1: <?xml version="1.0" encoding="utf-8" ?> 2: <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" namespace="NHFromScratch.Entities" 3: assembly="NHFromScratch.Entities"> 4: <class name="Customer" table="Customers"> 5: <id name="Id" type="Int32"> 6: <generator class="native"/> 7: </id> 8: <property name="Name" type="String" not-null="true"/> 9: <property name="AddressLine1" type="String" not-null="true"/> 10: <property name="AddressLine2" type="String" /> 11: <property name="City" type="String" not-null="true"/> 12: <property name="ZipCode" type="String" not-null="true"/> 13: <property name="Active" type="Boolean" not-null="true"/> 14: </class> 15: 16: </hibernate-mapping> 17: 18: 19:
Facciamo tuttavia due parole per dire a grandi linee cosa contiene il mapping della entità Customer. Innanzitutto è specificato il namespace e l’assembly che contiene l’entità mappata. Questo non è obbligatorio, ma ci consente di scrivere in maniera più leggibile il nome della classe più avanti. Il tag class indica appunto la classe che intendiamo utilizzare come entity. Ricordiamo che spesso non esiste un rapporto 1:1 tra tabelle ed entità, ms in questo semplice esempio così accadrà. Il tag class ci permette di definire il nome della classe, e a quale tabella è associata su DB. Il tag più sotto <id> è necessario: NHibernate vuole sapere come il database “distingue” le entità. Ci sono svariati modi per fare questo, e ll modo viene scelto dal sotto-tag generator: in questo esempio abbiamo specificato un generator “native”, che significa id univoco assegnato dal DB ( identity, auto-incrementale ) comunque esso si chiami nel DB target. Successivamente vengono le proprietà, con i loro bei nomi e tipi dato. Noterete che non ho specificato nessun nome per la corrispondente colonna sul DB: semplicemente se il nome è uguale nessuno ci obbliga a farlo, non ripetiamo noi stessi. Anzi, diciamo anche che il tipo sarebbe un surplus se scrivessimo a mano la classe sottostante: NHibernate usa molto inferire le informazioni ovunque sia possibile. Noi però la classe non abbiamo voglia di farla a mano, per cui ci serviamo di hbm2net ( vedi la parte 1 ). Per scatenare questo tool, usiamo un pre build step di Visual Studio, come quello qui sotto:
in pratica diciamo ad hbm2net di lavorare su tutti i file .hbm.xml che trova nella sottocartella mapping della solution, di mettere gli output ( ovvero le classi ) nella cartella del progetto. Sebbene hbm2net sia un editor template based che può generare un po’ qualsiasi artefatto partendo dall hbm, senza parametri si limita a generare le classi corrispondenti al mapping. Lanciando la build nell’output dovreste vedere il progress anche di hbm2net:
Finita la build, vi trovate il file autogenerato per la entity su file system. Aggiungetelo al progetto.
Date un’occhiata alla classe autogenerata, date un occhiata anche al mapping: se avete provato qualche altro tool per NH noterete che il lavoro fatto finora sembra molto semplificato. Stiamo scrivendo solo lo stretto indispensabile, quello che non ci serve lo lasciamo assolutamente da parte, perchè questo è NHibernate da ZERO.
Bene, per completare la solution aggiungiamo la reference ad NHibernate: NHibernate e Iesi.Collection sono gli unici assembly che dobbiamo referenziare a tempo di build.
E’ finalmente l’ora di andare in test. Aggiungiamo alla solution un progetto di tipo Class Library, in cui inseriremo gli unit test.
La solution di test, in modo simile ad un progetto che deve andare in produzione, necessita delle reference non statiche di NHibernate. Noi le abbiamo nella cartella Lib della solution, dobbiamo spostarli a fianco della dll di produzione. Possiamo scegliere di farlo con un post-build step, come illustrato qui sotto.
Anche le reference di questo progetto sono un po’ più impegnative: ci serve Iesi.Collection & NHibernate come al solito, ma anche il nostro assembly con le entità, log4net per il logging, ed in più, ovvviamente, nunit.framework, va da sè.
A me personalmente piace la barra verde di NUnit, per cui testiamo il progetto con NUnit, mettendo NUnit.exe come “start external program”.
A questo punto creiamo il nostro unit test, con lo scopo di configurare NH, un po’ di log, e vedere se riusciamo a creare il Database. Sì, in questo tutorial il database non lo facciamo a mano, perchè anche se questo è NHibernate da ZERO, vogliamo scrivere il meno codice possibile.
Il codice dello unit test è qui di seguito:
1: namespace NHFromScratch.Tests 2: { 3: [TestFixture] 4: public class TestCustomer 5: { 6: static TestCustomer() 7: { 8: ConfigureLogForNet(); 9: } 10: 11: ISessionFactory sessionFactory; 12: [SetUp] 13: public void Setup() 14: { 15: 16: CreateSessionFactory(); 17: } 18: 19: [Test] 20: public void CanCreatedatabse() 21: { 22: SchemaExport export = new SchemaExport(CreateConfiguration()); 23: export.Execute(true, true, false); 24: } 25: 26: [Test] 27: public void CanPersistCustomer() 28: { 29: Console.WriteLine("\n****** SAVE A CUSTOMER ********"); 30: //save a customer 31: using (var session = sessionFactory.OpenSession()) 32: using(var transaction = session.BeginTransaction()) 33: { 34: 35: Customer c = new Customer() 36: { 37: Active = true 38: , 39: AddressLine1 = "xxxx" 40: , 41: City = "Cuneo" 42: , 43: Name = "Bill Gates" 44: , 45: ZipCode = "12060" 46: }; 47: session.Save(c); 48: transaction.Commit(); 49: } 50: Console.WriteLine("\n****** RETRIEVE A CUSTOMER ********"); 51: //retrieve a customer 52: using (var session = sessionFactory.OpenSession()) 53: 54: { 55: 56: var customer = session.CreateCriteria<Customer>() 57: .Add(Expression.Eq("Name", "Bill Gates")) 58: .UniqueResult(); 59: 60: Assert.NotNull(customer); 61: } 62: 63: Console.WriteLine("\n****** MODIFY A CUSTOMER ********"); 64: //modify a customer 65: using (var session = sessionFactory.OpenSession()) 66: using (var transaction = session.BeginTransaction()) 67: { 68: 69: var customer = session.CreateCriteria<Customer>() 70: .Add(Expression.Eq("Name", "Bill Gates")) 71: .UniqueResult<Customer>(); 72: customer.ZipCode = "0000"; 73: Assert.NotNull(customer); 74: 75: transaction.Commit(); 76: } 77: 78: 79: Console.WriteLine("\n****** VERIFY CUSTOMER IS MODIFIED ********"); 80: //verify mod 81: using (var session = sessionFactory.OpenSession()) 82: { 83: 84: var customer = session.CreateCriteria<Customer>() 85: .Add(Expression.Eq("Name", "Bill Gates")) 86: .UniqueResult<Customer>(); 87: 88: Assert.NotNull(customer); 89: Assert.AreEqual("0000", customer.ZipCode); 90: } 91: 92: 93: Console.WriteLine("\n****** DELETE A CUSTOMER ********"); 94: //delete a customer 95: using (var session = sessionFactory.OpenSession()) 96: using (var transaction = session.BeginTransaction()) 97: { 98: 99: var customer = session.CreateCriteria<Customer>() 100: .Add(Expression.Eq("Name", "Bill Gates")) 101: .UniqueResult(); 102: session.Delete(customer); 103: transaction.Commit(); 104: } 105: Console.WriteLine("\n****** VERIFY CUSTOMER IS DELETED ********"); 106: //verify delete 107: using (var session = sessionFactory.OpenSession()) 108: 109: { 110: 111: var customer = session.CreateCriteria<Customer>() 112: .Add(Expression.Eq("Name", "Bill Gates")) 113: .UniqueResult(); 114: Assert.IsNull(customer); 115: } 116: } 117: 118: 119: 120: private static void ConfigureLogForNet() 121: { 122: TraceAppender app = new TraceAppender(); 123: app.Layout = new SimpleLayout(); 124: //BasicConfigurator.Configure( app); 125: } 126: 127: private void CreateSessionFactory() 128: { 129: Configuration cfg = CreateConfiguration(); 130: sessionFactory = cfg.BuildSessionFactory(); 131: } 132: 133: private static Configuration CreateConfiguration() 134: { 135: Configuration cfg = new Configuration(); 136: cfg.Configure(); 137: // implicitamente carichiamo tutti i mapping che si trovano nell'assembly che 138: // contiene customer 139: cfg.AddAssembly(typeof(Customer).Assembly); 140: return cfg; 141: } 142: 143: 144: } 145: }
Fatto questo lanciamo il test “CanCreateDatabase” e, come probabilmente sospettiamo, andiamo in rosso con il logger che dice qualcosa come qua sotto:
In effetti, ci siamo “dimenticati” di configurare NHibernate, per cui a che DB potrebbe mai puntare ? Come vedete avere il log abilitato lascia pochi dubbi rispetto a quale sia l’errore. Tra l’altro manca persino il database…
Quindi creiamo prima un database vuoto, non serve fare nessuna tabella, lasciamo che sia NHibernate a fare il lavoro sporco
Nell’esempio ho creato un database con nome NHFROMSCRATCH sull’SQLEXPRESS locale. Poi tornando alla , quando si vedeva il contenuto dello ZIP, rocorderete la cartella Configuration_Templates: in questa cartella ci sono appunto dei template di configurazione, in cui basta sostituire un po’ di cose per essere pronti a lavorare con il db scelto:

Nel nostro caso scegliamo MSSQL. Il contenuto del file, dopo le modifiche del caso è questo:
1: <?xml version="1.0" encoding="utf-8" ?> 2: 3: 4: <hibernate-configuration xmlns="urn:nhibernate-configuration-2.2"> 5: <session-factory name="NHibernate.Test"> 6: <property name="connection.driver_class">NHibernate.Driver.SqlClientDriver</property> 7: <property name="connection.connection_string">Server=.\SQLEXPRESS;initial catalog=NHFROMSCRATCH;Integrated Security=SSPI</property> 8: <property name="adonet.batch_size">10</property> 9: <property name="show_sql">true</property> 10: <property name="dialect">NHibernate.Dialect.MsSql2005Dialect</property> 11: <property name="use_outer_join">true</property> 12: <property name="command_timeout">60</property> 13: <property name="query.substitutions">true 1, false 0, yes 'Y', no 'N'</property> 14: <property name="proxyfactory.factory_class">NHibernate.ByteCode.LinFu.ProxyFactoryFactory, NHibernate.ByteCode.LinFu</property> 15: </session-factory> 16: </hibernate-configuration>
Come si vede, le modifiche sostanziali sono nella proprietà connection string, e in show_sql, che ho messo a true, per visualizzare le query mentre NH le esegue: un trucco che ci permetterà non tanto di vedere come NHibernate fa le query, ma piuttosto quante ne fa, per fare performance tuning. Questa sessione di configurazione potrà essere messa nel file di configurazione dell’applicazione, o vicino al file binario che la usa in un file di nome hibernate.cfg.xml, soluzione scelta nell’esempio.
Ricordiamoci che, dopo aver inserito il file hibernate.cfg.xml nel progetto, dobbiamo impostare il flag “copy if newer” cosicchè verrà copiato automaticamente nella cartella di uscita.
vediamo ora come è fatto il test “CanCreateDatabase”:
1: [Test] 2: public void CanCreatedatabse() 3: { 4: SchemaExport export = new SchemaExport(CreateConfiguration()); 5: export.Execute(true, true, false); 6: }
Usiamo la classe di Helper SchemaExport: questa classe, partendo dalla Configurazione corrente, si occupa di effettuare la creazione dello schema del database. Ora lanciando il test questi si comporta meglio, e nella porzione console è possibile vedere la DDL che è stata inviata al database.

Tramite il flag della funzione Export, è possibile sottometere lo script al sistema database sottostante, nel nostro caso otterremo su DB l’unica tabella del nostro minuscolo modello di test:
Quindi facciamo un breve recap fino a questo punto: con un solo file ( il mapping appunto ) abbiamo creato un database e il codice c#. Tutto questo grazie ad hbm2net, che è un code generator open source recuoperabile da NHContrib. Con un po’ di pazienza è possibil emodificare e/o scrivere template aggiuntivi per hbm2net per geenrare altri tipidi artefacts, ad esempio DTO o altro, ma questo sarà visto più avanti. In pratica possiamo vedere hbm2net come un generatore di codice programmabile, che di default produce il codice delle entity partendo dai file hbm.
Ora possiamo scrivere il codice di prova per il modello. Mettendo a “true” il valore di show_sql nella configurazione, NH manda in standard output le query, per cui in debug è possibile ispezionare cosa succede dietro le quinte. Il nostro semplice unit test crea/modifica/cancella una semplice istanza della entità customer, eco il codice dello unit test che fa tutto questo:
1: namespace NHFromScratch.Tests 2: { 3: [TestFixture] 4: public class TestCustomer 5: { 6: static TestCustomer() 7: { 8: ConfigureLogForNet(); 9: } 10: 11: ISessionFactory sessionFactory; 12: [SetUp] 13: public void Setup() 14: { 15: 16: CreateSessionFactory(); 17: } 18: 19: [Test] 20: public void CanCreatedatabse() 21: { 22: SchemaExport export = new SchemaExport(CreateConfiguration()); 23: export.Execute(true, true, false); 24: } 25: 26: [Test] 27: public void CanPersistCustomer() 28: { 29: Console.WriteLine("\n****** SAVE A CUSTOMER ********"); 30: //save a customer 31: using (var session = sessionFactory.OpenSession()) 32: using(var transaction = session.BeginTransaction()) 33: { 34: 35: Customer c = new Customer() 36: { 37: Active = true 38: , 39: AddressLine1 = "xxxx" 40: , 41: City = "Cuneo" 42: , 43: Name = "Bill Gates" 44: , 45: ZipCode = "12060" 46: }; 47: session.Save(c); 48: transaction.Commit(); 49: } 50: Console.WriteLine("\n****** RETRIEVE A CUSTOMER ********"); 51: //retrieve a customer 52: using (var session = sessionFactory.OpenSession()) 53: 54: { 55: 56: var customer = session.CreateCriteria<Customer>() 57: .Add(Expression.Eq("Name", "Bill Gates")) 58: .UniqueResult(); 59: 60: Assert.NotNull(customer); 61: } 62: 63: Console.WriteLine("\n****** MODIFY A CUSTOMER ********"); 64: //modify a customer 65: using (var session = sessionFactory.OpenSession()) 66: using (var transaction = session.BeginTransaction()) 67: { 68: 69: var customer = session.CreateCriteria<Customer>() 70: .Add(Expression.Eq("Name", "Bill Gates")) 71: .UniqueResult<Customer>(); 72: customer.ZipCode = "0000"; 73: Assert.NotNull(customer); 74: 75: transaction.Commit(); 76: } 77: 78: 79: Console.WriteLine("\n****** VERIFY CUSTOMER IS MODIFIED ********"); 80: //verify mod 81: using (var session = sessionFactory.OpenSession()) 82: { 83: 84: var customer = session.CreateCriteria<Customer>() 85: .Add(Expression.Eq("Name", "Bill Gates")) 86: .UniqueResult<Customer>(); 87: 88: Assert.NotNull(customer); 89: Assert.AreEqual("0000", customer.ZipCode); 90: } 91: 92: 93: Console.WriteLine("\n****** DELETE A CUSTOMER ********"); 94: //delete a customer 95: using (var session = sessionFactory.OpenSession()) 96: using (var transaction = session.BeginTransaction()) 97: { 98: 99: var customer = session.CreateCriteria<Customer>() 100: .Add(Expression.Eq("Name", "Bill Gates")) 101: .UniqueResult(); 102: session.Delete(customer); 103: transaction.Commit(); 104: } 105: Console.WriteLine("\n****** VERIFY CUSTOMER IS DELETED ********"); 106: //verify delete 107: using (var session = sessionFactory.OpenSession()) 108: 109: { 110: 111: var customer = session.CreateCriteria<Customer>() 112: .Add(Expression.Eq("Name", "Bill Gates")) 113: .UniqueResult(); 114: Assert.IsNull(customer); 115: } 116: } 117: 118: 119: 120: private static void ConfigureLogForNet() 121: { 122: TraceAppender app = new TraceAppender(); 123: app.Layout = new SimpleLayout(); 124: //BasicConfigurator.Configure( app); 125: } 126: 127: private void CreateSessionFactory() 128: { 129: Configuration cfg = CreateConfiguration(); 130: sessionFactory = cfg.BuildSessionFactory(); 131: } 132: 133: private static Configuration CreateConfiguration() 134: { 135: Configuration cfg = new Configuration(); 136: cfg.Configure(); 137: // implicitamente carichiamo tutti i mapping che si trovano nell'assembly che 138: // contiene customer 139: cfg.AddAssembly(typeof(Customer).Assembly); 140: return cfg; 141: } 142: 143: 144: } 145: }
Ed ecco uno screenshots di come NH opera dietro le quinte:
Nella prossima parte vedremo meglio i dettagli del file di mapping e il tag <many-to-one> insieme a <bag>.
Vai alla parte 2: Creare un progetto
Mi è ventuta l’idea di produrre una serie di post che consentano a chi vuole iniziare a lavorare con NHibernate, o almeno a provare per vedere se questo OR/M è la scelta giusta, di creare in modo ordinato un ambiente di test pulito, sapendo bene cosa serve, evitando il percorso “pasticciato” che spesso si fa all’inizio. Bene, come partenza bisogna scaricare NHibernate. Il posto giusto dove farlo è il progetto su sourceforge, e il modo giusto di farlo è procurare i file binary necessari. Scaricare i sorgenti per il momento può essere tranquillamente tralasciato. Puntando al link di download, vi trovate qualcosa del genere: Come vedete ci sono più versioni, ed anche una ultima release, che in questo caso è la 3.0.0 Alpha2. Io ho cerchiato la versione 2.1.2 GA che è quella che useremo per questa serie di articoli, ma ho anche cerchiato GA: “general availability”. Dunque, perchè non ho scaricato l’ultima versione ? Cosa significa GA ? NHibernate ha una politica di rilascio ben consolidata, e marca con GA le versioni “da produzione”. Quindi, se non siete a conoscenza di qualche funzionalità particolare che c’è solo nella alpha di turno, se non siete dell’idea di vedervi cambiate sotto il naso funzioni e comportamenti, scaricate le versioni GA più recenti che trovate. Non esistono al momento “Setup” di NHibernate, mi risulta che ci fosse qualcosa in passato, comunque anche se trovaste qualcosa, vi suggerisco di evitarlo: NH consente di fare un private deploy, e avere traccia di quello che serve ci sarà utile in fase di consegna dell’applicativo sulle macchine di produzione. Scarichiamo quindi il file NHIbernate-2.1.2.GA-bin.zip. Scaricato il file il layout del contenuto è quello schematizzato qui sotto: Nella root, oltre ad un po’ di paccottiglia varia, troviamo due folder importanti che sono quelli che ci servono per mettere NHibernate all’opera: Required_Bins, and Required_For_LazyLoading.Tutte le dll contenute nel file required bin devono essere rilasciate insieme all’applicazione ( oltre a servire ovviamente in fase di sviluppo :) ), mentre dalla cartella Required_For_LazyLoading si può scegliere quale folder distribuire in base a quale proxy generator si sceglie. Questo è un tema che si vedrà più avanti, per adesso basta sapere che NHibernate può derivare internamente alcune delle nostre classi quando decidiamo di avere delle entità “Lazy”, e questo è ottenuto tramite delle librerie esterne di generazione di classi “proxy”. Quale proxy generator usare dipende dalla configurazione, e quindi dalla stessa configurazione dipende quale folder distribuire. Negli esempi useremo LinFu. Diciamo che questa scelta è di scarso impatto sulla vita del vostro progetto, salvo in situazioni particolari, per cui per il momento possiamo andare avanti. Importantissimi in fase di sviluppo sono i file nhibernate-mapping.xsd e nhibernate-configuration.xsd. Questi due file vi consentono di avere l’intellisense abilitato per i file di mapping e per la configurazione, e quindi sono assolutamente indispensabili. Malgrado siano finiti nella cartella required_bin, sono invece praticamente inutili in fase di distribuzione. Per attivare l’intellisense occorre copiare i due file suddetti nella cartella apposita di visual studio: Attenzione: può succedere che imporvvisamente Visual Studio smetta di fornire l'intellisense: tipicamente questo accade perchè ci sono più versioni dello stesso schema XML associate. Date un occhiata a questo post che spiega come risolvere il problema.
Inoltre, per sviluppare e per seguire gli esempi in questo tutorial occorrerà NUnit. Se ce lo avete già usate quello, altrimenti recuperate l’ultima versione dal sito ufficiale. Io di solito scarico il binario, non il setup, e lo scompatto sotto il folder programmi. Anche NUnit funziona con il semplice deploy Xcopy. Ultima cosa prima di partire, lavorando con NHibernate ci sono alcune attività ripetitive che sarebbe bello evitare: consiglio di scaricare anche il tool hbm2net che ci consentirà di generare automaticamente le classi per nel corso del tutorial. Il tool si trova nel progetto NHContrib. Scaricate e scompattate lo zip in un folder, per esempio in C:\hbm2net. Il tool è un compilatore linea di comando, e sarà lanciato automaticamente da Visual Studio, ma perchè ciò avvenga occorre aggiungere la cartella in cui avete messo hbm2net nella variabile di ambiente PATH. per fare questo raggiungete le “proprietà del Sistema”: e poi aggiungere il path suddetto alla variabile PATH: Nella Parte2 vediamo come organizzare un progetto con NH.
For maintenance reasons sometime the original NHForge location is not available. In such a case you can download the file containing the binary release of the tools in the original NHContrib project download location: 
Apprendo da questo post di Fabio Maulo una interessante feature di NHibernate 3 implementata nel nuovo subset di API: QueryOver. Il post di Fabio approfondisce il pattern Query Object, e quello che salta all’occhio è la pulizia con cui si riesca finalmemte a farte un paging “ad arte”. Per fare l’accesso paginato in generale occorre avere il count delle righe che il sistema vorrebbe tornarci, e questo constringeva in passato a fare delle implementazioni un po’ sporche in cui si era costretti a specificare la query di selezione e quella di count. QueryOver propone la funzione query.ToRowCountQuery(); – dove query è appunto una query di QueryOver, e la funzione ci restituisce la query di conteggio in modo pulito e trasparente. Assolutamente molto utile. Io non sono uno da trunk, ma una funzione del genere mi fa venire voglia di passare subito a NH3, ancor prima che venga rilasciata la GA.
The right place for bug reporting or requiring additional features is Since the project is new, it is normal to have something to fix. The problem is that my testing would just be not sufficient, so your help will really be appreciated. Thanks!
First of all I renamed the project on Sourceforge. Now it is more sensible NHibernate Workbench. In the SVN repository there is now a tag to the version 1.0.0.11, and the trunk claim to be the version 1.0.0.2.00. Now I’m planning to allow to use NH Workbench attached to a running application: this should help us to play with application compiled without mappings ( ie ConfOrm and Fluent NH ). Then I would like to improve the “Probe” class letting it be more versatile and modifiable by the user, probably using some sort of script engine: I’m thinking to use IronPython, but any suggestion are welcome. Just to clarify: the probe class serves to insolate NHWorkbench from the NH version used by the project under test. We basically runs the test in a separate app domain, but we need the “probe” type to be unbounded to any NH specific version. This is done by using reflection, but it would be easier to be done in a script. The same engine will be useful to write some NHibernate testing: instead of use just HQL, we will be able to submit some portion of code on the fly and see what happen. The other step is to allow the user writing a mapping on the fly and imemdiately see what happen ( by using hbm2net behind the scenes ). Ok, it’s a lot of work, I’ve no idea the order this will be done, let me know if you have any idea and preference.
Sometimes I receive some notification on where source code for both db2hbm and hbm2net are located. The best way is to check-out the source of the NHContrib project: https://nhcontrib.svn.sourceforge.net/svnroot/nhcontrib hbm2net is still in alfa, but I frequently use it in my projects as a class generator. It really lack some documentation: it is really a powerful artifact generator, and by writing proper T4 templates any artifact can be generated. Db2hbm is working ok for MSSQL, but there is not yet an implementation for oracle and other DB, even if Ricardo Peres provided me some interesting code to work on. Hope this help who’s looking for these tools source code.
Just looking around for some related NHibernate projects, I found this open source profiler. For people who start to use NHibernate and before to buy something more accurate as NHProf, it is really useful having a log easy to read to discover performance or bad usage issues.
As you probably guess, the Fatica.Labs.HqlEditor evolved as a component used by NH Workbench, that is basically a tool ispired by the old and wise NHQA by Ayende. There is, in comparison, some new ideas and some missing required functions. Anyway I decided to publish a first drop because it already help me on my day job. If you find the project useful please consider visits the following links: And, if you want to join the project, please let me know.
In the spirit of “Release early. Release often. And listen to your customers” ( cit. ), even if not so early in term of time since the preview, I decided to release a first drop of the “HQL Intellisense thing” I’m working on. The current version is just able to load an existing mapping assembly, a configuration, help us to write an hql query, submit it to NH and see some results. Here an overall screenshot:  To use it you need to download the bits, and then “create a project” a project is, in the NH Workbench world, a bounch of file representing what we are working on ( and actually is a project in the MSBUILD world. To use the tool now we need at least a working NH configuration file ( your app.config or web config ) and one or more mapping assembly(ies). You add the files to the project by right clicking the project tree: After you added the file you can save the project, so it can be reopened when needed. Please note that the mapping assembly has to be opened from a location containing all the required dependencies ( usually the application folder, or the bin folder ). After the project is created, you need to compile it before starting to write the queries: You can compile the project by clicking the button on the toolbar as shown in the picture Fig3 Compiling the project should produce a report in the log area: If you find the report too verbose, you can uncheck some of the button in the log toolbar. After a successful compilation, we can open a query (hql) document: This will open a pane in the document area in which we can write HQL queries with some intellisense/auto-completion. Plaese note that, for have the entity completion, after the “from” keyword we need to press ctrl+space to see the completion combo. Here an example HQL document. After a valid query is done we can submit it to NH and see the result: The “play” button is enabled only if a valid query ( no errors ) is written in the document. The first and count places are useful to limit the query results. By pressing the play button, you will be able to se the query results ( if any ): Next steps: - Solve the bugs till now
- Add supports for hbm2net, so user can write mapping and immediately see it at works.
Enjoy !
There was a bug in the many-to-many strategy, causing a null reference exception. The bug was solved, and a current snapshot of db2hbm can be found here as usual. 
Fabio Maulo propone un sondaggio per la versione di NH in uscita. Dovrà compilare su Fx 3.5 o 4.0 ? Personalmente preferirei ancora un uscita in 3.5. Votate!
There is some interesting news with HqlEditor: now it is able to translate the query using the proper parser ( Antlr or Classic ) depending on the NH version used to build the assembly in test. An error message is shown when the syntax is parsed as invalid. A first table containing the entities is present too, here below a screenshot: As you can probably notice, the data grid is not a real grid. This is because is not so easy to present an NH graph without converting it in some sort of DTO, but I don’t want to add anything but the query process itself. So I decided to use a sort of JSON serializer to present each object in a textual fashion. In the toolbar there is the query limit too: this act by adding a SetFirstResult(), SetMaxResults() function call in the query creation. Using a count=0 forces the system to avoid limiting the query: if the data you retrieve is a big bounch you will probably experience some delay, but should be useful in the case the driver does not support limits.
Per abilitare il logging delle query con NHibernate occorre: - Avere nella bin dell applicativo ( ie: nella /bin per le applicazioni web, a fianco dell’ esequibile per le applicazioni stand-alone ) la dll di log4net.
- Aggiungere nel file di configurazione la sessione di config per log4net:
<configuration> <configSections> <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net"/> </configSections> <log4net> <appender name="console" type="log4net.Appender.ConsoleAppender"> <layout type="log4net.Layout.PatternLayout"> <param name="ConversionPattern" value="%-5p - %m%n" /> </layout> </appender> <logger name="NHibernate.SQL" additivity="false"> <level value="ALL"/> <appender-ref ref="console" /> </logger> <root> <priority value="WARN" /> <appender-ref ref="console" /> </root> </log4net> </configuration> Con questa configurazione si usa il console appender, per un’applicativo web potrebbe essere meglio usare un TraceAppender o un altro appender di proprio gusto - Assicurarsi di chiamare, almeno una voltanell’applicazione log4net.XmlConfigurator.Configure()
- Aggiungere questa proprietà nella configurazione di NH: <property name="show_sql">true</property>
Se NON vogliamo mettere nulla di log4net nella configurazione: Possiamo configurare log4net dall’ applicativo, con un paio di linee di codice: 1: TraceAppender app = new TraceAppender(); 2: app.Layout = new SimpleLayout(); 3: LoggerMatchFilter filter = new LoggerMatchFilter(); 4: filter.LoggerToMatch="NHibernate.SQL"; 5: filter.AcceptOnMatch = true; 6: filter.ActivateOptions(); 7: app.AddFilter(filter); // L'ordine di questo filtro 8: app.AddFilter(new DenyAllFilter()); // e di quest'altro E' importante 9: app.ActivateOptions(); 10: BasicConfigurator.Configure( app);
In questo caso si possono saltare gli step da 1 a 3. Questo codice deve essere chiamato una volta nell’applicazione in fase di startup, per un’applicazione web, potrebbe andare bene l’evento di startup dell'applicazione in global.asax. Con l’appender e i filtri configurati si ottiene nell'area trace di Visual Studio ( Output-tab Debug) l’output delle sole query generate da NH ( senza gli altri logger, se servono si possono togliere i filtri )
There is some interesting progress with my project Fatica.Labs.HqlEditor. I just want to share some screenshot: Well, it is growing to be a real tool, and in my idea would became a sort of test bed in which the user can add or modify mapping, try the queries, change the config, export a database script, reverse engineering and so on. Actually all the low level tool to achieve that are available. Ok, let’s explain the layout: - The document area, here we have mapping/config/hql all with intellisense. In the screenshot the code completion for an Hql is shown. In future maybe I will be able to insert a T4 editor for the hbm2net templates.
- The project area: here we have a bounch of file that are representing our testing project: mapping, configurations, assemblies and so on. I have use the MSbuild object as a backend for the project, because in the near future I would like to use it to really build some artifacts using hbm2net and db2hbm.
- Here is the SQL preview of the query in editing. Now the view is showing an error because the query is incomplete.
- The funny log, a graphical appender for log4net
 Some more words about the project itself: the testing environment is hosted in a separate appdomain, this will allow us to: - Modify the mapping runtime generating new version of the assembly
- Testing production assemblies built with legacy nh versions ( well, not so legacy, starting from 2.xxx )
Let’s have another screenshot, showing a real SQL preview: Next step is to produce the query results in some sort of usable representation ( I need to push the data across two app domain ) so I would probably use some JSON serialization and then display the JSON raw data with some readable formatting. You can see a little demo video here. The project is not yet released, please treat it as a CTP ;) anyway, the svn repository is here: https://faticalabshqled.svn.sourceforge.net/svnroot/faticalabshqled
Fabio Maulo sta proponendo una nuova astrategia di mapping per NHibernate: ConfORM. In pratica si tratta di una strategia code only, quindi nessun file di XML, ma tutto via codice con l’approccio fluent interface. Stranamente la community si è un po’ stupita di vedere un ennesima strategia, e tutti stanno lì a chiedersi il perchè. Bene, il perchè è che è una nuova possibilità di scelta, ed avere molte scelte è un plus degli ambienti open source. In un suo post Fabio disegna uno scenario completo della ricca rosa di partecipanti al problema mapping. Difatto ConfORM è l’unica API che sfonda completamente lo strato hbm, e va direttamente alla radice. Tutto questo si traduce immediatamente in un incremento di performance, ed in una migliore linearità progettuale: meno strati è meglio, anzi, meno strati inutili è meglio. Francamente, dopo una piccola esperienza acquisita nel problema mapping con la scrittura del tool NHModeller, ho deciso di tornare ed imparare la strategia HBM. Dopo un po’ non è così male, ma la prossima volta che faccio un progetto mio provo ad usarlo ( in azienda non se ne parla nemmeno: già il mapping XML è visto come una stregoneria, e c’è chi legifera che mappare le foreign Key come long sia una buona idea anzichè un antipattern ). In conclusione: chissenefrega se un nuovo sistema sembra essere il duplicato di un altro:Vincerà il migliore, ed in ogni caso il migliore secondo gli utilizzatori 
If anybody still uses HQL with NHibernate, it would probably find useful some editing with intellisense. Using the SharpDevelop Text Editor, the ANTLR grammar from the NHibernate sources, and some hacking in the java Eclipse Plugin code, the results is this: Ctrl+Space completion 1: entity alias Ctrl+Space completion 2: Entity names Dot completion: property completion Ctrl+Space completion: all keywords and functions  Here you can find the related sourceforge project.
Esiste, o meglio esisteva un tool di code generation, per creare automaticamente le classi partendo dai file di mapping di NHibernate. Questo tool (hbm2net, presente in NHContrib ) è stato un po' dimenticato, per cui ho deciso di provare a riesumarlo, e di ammodernarlo un po' dandogli la possibilità di utilizzare il Text Template Transformation Toolkit (T4). Ho previsto un template interno per la semplice generazione delle classi di mapping, ma potenzialmente è possibile generare con facilità qualsiasi altro codice provvedendo un template esterno, ad esempio mascherine di UI, layer WCF etc etc.
La versione attuale è una pre-alfa, serve solo a dare un'idea, e a vedere se ci sono delle dipendenze in deploy di difficile gestione, non è ben chiaro a me se T4 sia presente in tutte le installazioni di Visual Studio.
Se volete provare il tool potete scaricarlo da qui. Per utilizzare il templating T4 dovete utilizzare la seguente linea di comand:
hbm2net --config=t4config.xml *.hbm.xml
è importante utilizzare il config indicato, altrimenti hbm2net defaulta sul render di NVelocity. Verra creata una cartella generated con i file sorgenti corrisondenti agli hbm.
Potete scaricare hbm2net da qui.
Per provare al volo è incluso nello zip anche un file di mapping simple1.hbm.xml.
Fatemi avere dei feedback!
|