I was in a meeting where we had a discussion about keeping the modifications made to a database synchronized with the NHibernate mappings. Some said that there was a method called Validate() which just needed to be run and that all that was needed. I was right, it would turn out to be skeptical of this comment and of the Validate() method.
You can approach the implementation of NHibernate from many angles, however 2 are most common. The first is from a Greenfield perspective, where there is no database yet and you will be able to use Domain Driven Design (DDD) and the SchemaExport NHibernate capability to create the database. In my opinion, this is where you can gain most of the advantages of NHibernate, because, again in my opinion, this is how NHibernate should be used. However, we live in the real world were many, many system are not designed using DDD and it is the database which dictates how the client operates, commonly referred to in NHibernate terms as, a legacy database.
The point it, when using NHibernate from a DDD perspective, the SchemaValidate will notify you if a table, column, column type or foreign key is missing is incorrect. This is because it uses the mappings to create the data source and can use the mapping to confirm what exists in the mapping exists on the data source too.
The case where you are working on legacy database is somewhat different. Since the mapping is not the driver of the datasource model, you can’t expect the SchemaValidate to tell you if a new table, a new column or if a new relationship between tables has been added. I configured the SchemaValidate as below on a test ‘legacy’ database with 1 table. I did the mapping, configuration and everything required to get it to work properly. I then added a new table and a new column to the existing table. The Validate() method did not notify me that either had been added. Based on that experience, I made the previously stated assumptions.
Nonetheless, I provide below the method by which I configured my program to use the Validate() method of the SchemaValidate class.
First I needed to get a copy of the NHibernate.Test.dll and add it as a reference. I do not know why or what I am doing wrong but like with the log4net.dll I could not get the component to be recognized because the Target Framework selected in the Properties of the Project was set to ‘.Net 4 Framework Client Profile’. Once I change it to use just ‘.Net 4 Framework’ I could get access to the classes and methods within the namespace.
From:
To:
The following assembly references are required.
using NHibernate.Test;
using NHibernate.Tool.hbm2ddl;
using System.IO;
using System.Reflection;
Then you add the following code which identifies which mapping file to validate the database against. Then create a new Configuration instance and load the default configuration into it. The TestConfigurationHelper class is found within the NHibernate.Test.dll assembly.
string classCorder = "Namespace.Directory.ClassName.hbm.xml";
Configuration configHelper = new Configuration();
configHelper = TestConfigurationHelper.GetDefaultConfiguration();
I then loaded the ClassName into a stream instance and added it to the Configuration object, by using the below code segment.
Type type = typeof(Namespace.Directory.ClassName);
Assembly myAssembly = Assembly.GetAssembly(type);
using (Stream stream = myAssembly.GetManifestResourceStream(classCorder))
{
configHelper.AddInputStream(stream);
}
Lastly, I created an instance of the SchemaValidator which is located in the NHibernate.Tool.hbm2ddl namespace, passing it the Configuration object and calling the Validate() within a try…catch. If the Validate() method finds discrepancies, it will throw a HibernateException with a message stating where the divergence is.
SchemaValidator schemaValidator = new SchemaValidator(configHelper);
try
{
schemaValidator.Validate();
}
catch (HibernateException e)
{
Console.WriteLine(e.Message);
}
I did change a type value within my mapping file and the above did trigger an exception notifying me that this type on the datasource does not match what is in the mapping file. My first reaction was that this would happen during the regular instantiation of the SessionFactory, so what is this good for?
The answer is, up to now I have only implemented NHibernate on ‘legacy’ datasources, I.e. the database already exists and I am mapping the database using NHibernate, instead of creating the domain with NHibernate and using it to create the datasource.
Ultimately, I believe that the SchemaValidate is most useful after a SchemaExport is run to confirm that everything went ok and the datasource does match the mappings. We were all hoping that we wouldn’t have to manually update or use some other technology to keep the mappings and data model in the same state. Because, in my situation, the database owns the model, and not NHibernate, this manual or other action is required to keep things synchronized.