Try fast search NHibernate

29 June 2009

NHibernate3 Criteria

When you work in an Open Source project, in how many hours a blog post, about a limitation, can become obsolete ?

This

var criteria = session.CreateCriteria(typeof(Person))
.Add(Restrictions
.And(Restrictions.Eq("Name", "Richard"),
Restrictions.Or(Restrictions.Gt("Age", 21),
Restrictions.Eq("HasCar", true))));

in NH3.0.0 will look

var query = session.QueryOver<Person>()
.Where(p => p.Name == "Richard" && (p.Age > 21 || p.HasCar));

well… the answer to the above question is : in few hours.

Thanks Richard.

P.S. perhaps now I can reconsider my opinion about Criteria readability.

Criteria on NH3.0.0

This is how will look the Criteria in NH3.0.0 (the actual trunk)

IList<Student> sudents =
session
.QueryOver<Student>()
.Where(s => s.Name == "Fabio").And(s => s.StudentNumber > 100)
.List();

We will work to allow

IList<Student> sudents =
session
.QueryOver<Student>()
.Where(s => s.Name == "Richard" && s.StudentNumber > 100)
.List();

The intention, so far, is have lot of features (may be not all) available with the actual implementation of Criteria but avoiding strings and having a more readable sentence.

Richard Brown, PL of nhibernatelambda, is working on it.

Localized Property with NHibernate

This is a annoying issue we saw again an again.

The story

Three weeks ago Gustavo Fuentes asked me a solution to implement a multi-language property. After heard his needs my first answer was : “go to IUserType”. He was not completely convinced by my answer and so I gave him some links. Few days after Michal Gabrukiewicz asked the solution, for the same problem, in nhusers-list (how much little is the world of IT, no?). Michal have shared his solution in his blog. Immediately I sent the link to Gustavo but, another time, he does not like it (que hincha pelotas). Five days ago, inspired by an IUserType in uNhAddIns, Gustavo shared his own solution in uNhAddIns issue tracker. As usual in Open Sources, when you share your code, somebodyelse can:

  1. see it
  2. use it
  3. find bugs
  4. fix bugs
  5. improve the solution

Waiting results of the election day in Argentina, I have applied the five points to the Gustavo’s solution.

The LocalizablePropertyType

The LocalizablePropertyType is a IUserType and IParameterizedType implementation that map a IDictionary<CultureInfo, string> to a VARCHAR (or what defined in your dialect for a string).

Your entity may look like:

public class EntityWithLocalizableProperty
{
public virtual IDictionary<CultureInfo, string> LocalizedDescriptions
{
get; set;
}
}

And your mapping like

<typedef name="localizable"
class="uNhAddIns.UserTypes.LocalizablePropertyType, uNhAddIns"/>

<
class name="EntityWithLocalizableProperty">
<
id type="int">
<
generator class="native"/>
</
id>
<
property name="LocalizableDescriptions" type="localizable"/>
</
class>

The LocalizablePropertyType has two parameters:

<typedef name="localizable"
class="uNhAddIns.UserTypes.LocalizablePropertyType, uNhAddIns">
<
param name="keyValueEncloser">~</param>
<
param name="length">560</param>
</
typedef>

The keyValueEncloser is a char used to enclose the culture-Info name and the value of the localizable string.

The length is the total length of the field in the DB.

Single string property

If you need a single property to represent the value, for the current culture, its implementation may look like:

public virtual string Description
{
get
{
return
LocalizedDescriptions.Count > 0 ?
LocalizedDescriptions.FirstOrDefault
(
e =>
e.Key.Equals(Thread.CurrentThread.CurrentCulture)
)
.Value
: null;
}
set
{
if (value == null)
{
LocalizedDescriptions.Remove(Thread.CurrentThread.CurrentCulture);
}
else
{
LocalizedDescriptions[Thread.CurrentThread.CurrentCulture] = value;
}
}
}
How is represented a value

Give a dictionary like this:

new Dictionary<CultureInfo, string>
{
{new CultureInfo("es-AR"), "Hola"},
{new CultureInfo("en-US"), "Hello"}
};

its persistent representation, using the char ‘~’ as keyValueEncloser, will be:

~es-AR~~Hola~~en-US~~Hello~
Querying

In term of usability inside the entity class I like very much this solution, perhaps the problem is during querying.

I have used the keyValueEncloser not only to remove some bugs but essentially to simplify commons queries over the localized value. After that I have implemented some extension and some helpers to use in queries.

Given an entity initialized like this

new EntityWithLocalizableProperty
{
LocalizableDescriptions =
new Dictionary<CultureInfo, string>
{
{new CultureInfo("es-AR"), "Hola"},
{new CultureInfo("en-US"), "Hello"}
}
};

by HQL:

s.CreateQuery("from EntityWithLocalizableProperty e where e.LocalizableDescriptions like :pTemplate")
.SetString("pTemplate", Localizable.ConvertToLikeClause("en-US", "H_l%"));

by Criteria:

s.CreateCriteria<EntityWithLocalizableProperty>()
.Add(Localizable.Like("LocalizableDescriptions", "en-US", "H_ll_"));

or using the current culture

s.CreateQuery("from EntityWithLocalizableProperty e where e.LocalizableDescriptions like :pTemplate")
.SetString("pTemplate", "H_l%".ToLocalizableLikeClause())

and by Criteria:

s.CreateCriteria<EntityWithLocalizableProperty>()
.Add(Localizable.Like("LocalizableDescriptions", "H_ll_"))

In practice the Localizable class is the responsible to convert a common values used in the Like clause to the right value following the persistent representation defined above (you can see some others overloads).

The Code

The implementation of LocalizablePropertyType is available in uNhAddIns.

27 June 2009

NHibernate Configuration

Perhaps not so many people know in how many ways NH can be configured since NH2.0.0. In this post I’ll try to summarize some one before implement Loquacious configuration in NH3.0.0.

Xml (default)

The xml way is the most common used so only some little notes are needed here.

Inside the root node, hibernate-configuration, you can configure 3 “things”: the bytecode provider, the reflection optimizer usage and the session-factory.

The bytecode provider and the reflection optimizer can be configured only and exclusively inside the application config (app.config or web.config); outside application config the two configuration are ignored. If you need to configure the bytecode provider or the reflection optimizer the minimal configuration required, inside app.config is, for example:

    <hibernate-configuration xmlns="urn:nhibernate-configuration-2.2">
<
bytecode-provider type="null"/>
<
reflection-optimizer use="false"/>
</
hibernate-configuration>

As you can see the session-factory section is not present (for this reason I wrote “minimal”).

The session-factory configuration can be wrote inside or outside the app.config or even in both (inside and outside). The minimal configuration required is:

<hibernate-configuration xmlns="urn:nhibernate-configuration-2.2">
<
session-factory name="NHibernate.Test">
<
property name="dialect">NHibernate.Dialect.MsSql2005Dialect</property>
<
property name="proxyfactory.factory_class">
NHibernate.ByteCode.LinFu.ProxyFactoryFactory, NHibernate.ByteCode.LinFu
</property>
</
session-factory>
</
hibernate-configuration>

As you can see there are only two properties and, as is, you can’t use this configuration because there isn’t the connection string.

If you want you can write the minimal configuration inside your application config and merge/override it with the configuration wrote in an external file. I’m using this technique everywhere for tests purpose because test suite and production has some common configuration and some other different configuration (for example the current_session_context_class). The code to merge/override the configuration inside app.config with the configuration outside the app.config is:

var configuration = new Configuration()
.Configure()
.Configure(yourNhConfPath);

After this two lines you can continue configuring NHibernate by code (even using the method chaining).

Xml (custom configSections)

As usual in NHibernate, you can have a custom Schema for the hibernate-configuration section. The way is simple:

    <configSections>
<
section name="hibernate-configuration"
type="YourCompany.YourProduct.YourConfigurationSectionHandler, YourAssembly" />
</
configSections>

The restriction is :

  1. The section name must be “hibernate-configuration”.
  2. Your ConfigurationSectionHandler must implements IHibernateConfiguration.

The reason to write a custom ConfigurationSectionHandler, more than implement something related to Enterprise Library-Configuration Application Block, can be related with the need of some framework (your imagination may work here).

Xml : The mystery

In these year I saw :

  • everybody using property
  • somebody using the mapping section
  • few people using the event and/or the listener sections
  • absolutely nobody using the class-cache nor collection-cache sections

The two sections related with the second-level-cache configuration are very useful. In general the configuration of the cache is something happening after wrote all mappings; you may have a tests suite that must work without cache and another test suite to check the behavior using second-level-cache. Even if I have changed NH2.1.0 to ignore the cache configuration, inside a class-mapping, when the cache.use_second_level_cache is set to false, the right place to configure the cache, of each class/collection, is inside the session-factory-configuration and not inside the class-mapping itself; you don’t need to modify a tested mapping only because cache and you don’t need to re-deploy class-mappings only because you need to modify/add the cache configuration of a class/collection.

Configuration by code

The whole configuration via XML can be done by pure .NET code.

The bytecode provider can be set before create an instance of the Configuration class:

Environment.BytecodeProvider = new EnhancedBytecode(container);
var cfg = new Configuration();

To set properties in a no strongly typed way (mean by strings) :

configuration.SetProperty(Environment.GenerateStatistics, "true");
configuration.SetProperty(Environment.BatchSize, "10");

To set listeners in a no strongly typed way (mean by strings) :

configuration.SetListeners(ListenerType.PreInsert,
new[] { "YourCompany.YourProduct.YourListener, YourAssembly" });

To set listeners in strongly typed way :

configuration.EventListeners.DeleteEventListeners =
new[] {new ResetReadOnlyEntityDeleteListener()}
.Concat(listeners.DeleteEventListeners).ToArray();

To add class-mappings the Configuration class has various Add* methods :

AddAssembly(Assembly assembly)
AddAssembly(string assemblyName)
AddClass(System.Type persistentClass)
AddDirectory(DirectoryInfo dir)
AddFile(string xmlFile)
AddFile(FileInfo xmlFile)
AddXmlReader(XmlReader hbmReader)
AddXml(string xml)
AddResource(string path, Assembly assembly)
...
...

Note AddXml(string xml) or AddXmlString(string xml) mean that you can add a mapping created at run-time:

string hbm =
@"<?xml version='1.0' encoding='utf-8' ?>
<hibernate-mapping xmlns='urn:nhibernate-mapping-2.2'
namespace='NHibernate.DomainModel'
assembly='NHibernate.DomainModel'>
<class name='A' persister='A'>
<id name='Id'>
<generator class='native' />
</id>
</class>
</hibernate-mapping>"
;

Configuration cfg = new Configuration();
cfg.AddXmlString(hbm);

To set second-level-cache configuration for classes and collections:

SetCacheConcurrencyStrategy(string clazz, string concurrencyStrategy)
SetCacheConcurrencyStrategy(string clazz, string concurrencyStrategy, string region)
SetCollectionCacheConcurrencyStrategy(string collectionRole, string concurrencyStrategy)

The trick of custom Dialect

Few people know this trick (perhaps only the one I’m seeing everyday in the mirror).

Each Dialect has the ability to define its own DefaultProperties. Inside the core, so far, we are using the DefaultProperties property only for few purpose (for example to define the default driver):

DefaultProperties[Environment.ConnectionDriver] =
"NHibernate.Driver.SqlClientDriver";

If you are not so scared by inherit from a dialect implemented in the core and extends it to register some functions or to change some behavior, you can use it even to define default properties:

public class YourCustomDialect : MsSql2005Dialect
{
public YourCustomDialect()
{
DefaultProperties[Environment.QuerySubstitutions] =
"wahr 1, falsch 0, ja 'J', nein 'N'";
DefaultProperties[Environment.ConnectionString] =
"your connection string";
DefaultProperties[Environment.BatchSize] = "50";
}
}

What we saw

Since NH2.0.0GA, you have three or four ways to configure NHibernate with merge/override from different sources.

23 June 2009

NUnitEx 1.0.1 was released

A new version of NUnitEx was released today.

There are only some few new features as:

New assertion for nullable types:

int? nullable= null;
nullable.Should().Not.Have.Value();

New assertion for Action<T> (even if in this case I prefer to use directly the NUnit syntax)

(new Action(() => new AClass(null)))
.Should(title).NotThrow<ArgumentNullException>();

New assertion for string

string something = null;
something.Should().Be.NullOrEmpty();
string.Empty.Should().Be.NullOrEmpty();

Note that in many cases you don’t need news assertion because you can chain a class method to an existing assertion as:

string.IsNullOrEmpty(something).Should().Be.True();

Where the new assertion is really needed, to have a more fluently assertion, there is no problem to create a new issue.

Enjoy NUnitEx.

21 June 2009

How much I like NUnitEx

It is so fluent….

// NUnit classic Assertion
Assert.AreEqual("1;2;3", (new[] {"1", "2"}).ConcatWithSeparator(';'));

// NUnit classic Constraints
Assert.That((new[] { "1", "2", "3" }).ConcatWithSeparator(';'), Is.EqualTo("1;2;3"));

// NUnitEx
(new[] { "1", "2", "3" }).ConcatWithSeparator(';').Should().Be.EqualTo("1;2;3");

No ?

s.CreateQuery("from EntityWithPropNames e where e.PropertiesNames like '%p2%'")
.UniqueResult<EntityWithPropNames>()
.PropertiesNames
.Should().Have.SameSequenceAs(new[] {"p1", "p2", "p3"});

18 June 2009

Introducción a ORM

El día 26 de Junio daré una conferencia gratuita en el Microsoft User Group (MUG) de Buenos Aires.

El objetivo de la charla es la introducción a los conceptos de ORM que están a la base de persistent layers como NHibernate, EntityFramework y otros.

Los temas que trataremos son:

  • Conceptos básicos de ORM
  • Técnicas de POID
  • Técnicas de mapeo de herencia
  • Técnicas de mapeo asociaciones/agregaciones
  • Implementaciones de Concurrencia
  • Uso de StoredProcedure/Triggers
  • Características destacadas de un PersistentLayer

Si piensan usar/escribir un persisten layer o tienen en mente asistir a un curso sobre algún framework especifico, basado en ORM, asistir a esta conferencia es aconsejable.

Para quien quiera asistir les dejo el link.

17 June 2009

Spanish Inflector.Net

Después del recibir feedback sobre el post anterior (gracias a Jorge y Hadi) he hecho algunos cambios a la implementación del Inflector.Net.

El primer cambio fue el volver atrás con la implementación del Tablize o sea que ahora trabaja pluralizando solo la última palabra (así como prevé la convetion de RubyOnRails). Este cambio hace que el SpanishInflector cubra más del 70% de las traducciones desde el nombre de la clase al nombre de la tabla.

Fue interesante probar la implementación sobre sistemas existentes. Este es el código que usé:

public void Naming()
{
var si = new SpanishInflector();
var cfg = new Configuration().Configure();
foreach(var pc in cfg.ClassMappings)
{
var tn = pc.Table.Name;
var en = StringHelper.UnqualifyEntityName(pc.EntityName);
var tableize = si.Tableize(en);
if (!tableize.Equals(tn))
{
Console.WriteLine("{0} - {1} [{2}]", en, tn, tableize);
}
}
}

Lo que resultó interesante fue el hecho que al usar el Inflector nos hubiéramos dado cuenta que algunos nombres de tablas están equivocados y hasta saltaron algunos nombres de clase equivocados.

Llegar al 100% de cobertura, y traducción exacta, es un poco difícil ya que se interponen nombres compuestos por dos, tres o más palabras y pluralizar solo la ultima, a veces, no es suficiente y a veces es equivocado (así como pluralizar todas). Hago algunos ejemplos:


Clase



Tabla


OrdenCliente OrdenesClientes
TipoDocumento TiposDocumento
FormaPago FormasPago
PagoForma PagosFormas
DocumentoTipoProducto DocumentoTiposProductos

A parte el hecho que, en algunos casos, se entra en la no opinable “cuestión de gustos” (léase formas distintas de llamar la misma asociación) no he encontrado un patrón que permita definir exactamente cuáles son las palabras que hay que pluralizar, ni hablar de cuando la clase está compuesta en cocoliche (palabras en Español y palabras en Inglés).

Ya que queda claro que una convención o se aplica siempre, aunque no nos guste, o jamás puede cubrir el 100% de los casos agregué la posibilidad de un data-dictionary o agregar reglas propias a la convención de default (solo la última palabra es la que se pluraliza).

Agregar una entrada en el data-dictionary es así:

AddDataDictionary("UsuarioRol", "UsuariosRoles");

o si quieren

inflector.AddDataDictionary("UsuarioRol", "UsuariosRoles");

En los días a venir encontraré el tiempo de “postear” sobre el uso del spanish-inflector para el INamingStrategy de NH (quien piensa usarlo en FNH es probable que ya sepa cómo usarlo).

LINQ and Repository

If you want LINQ2NH or you are planning to use LINQ2EF please don’t say me that you are defining Repository like this

public interface ICustomerRepository
{
Customer GetCustomerById(string id);
IEnumerable<Customer> FindByName(string name);
void AddCustomer(Customer customer);
}


16 June 2009

Improving ADO exception management in NHibernate

When you working with NHibernate you may need to manage some specific situation where an exception is thrown by the RDBMS/DataProvider. For example you may need to do something specific for a “constraint violation”, to manage a “deleted object” or a stale-object-state but generated by RDBMS (because isolation level for example).. and so on.

All those ADO.NET exceptions are catch and wrapped, in NHibernate, by a GenericADOException. NHibernate can’t manage each situation and re-throw a specific exception because the System.Data.Common.DbException does not have enough information to do it. Each DataProvider, in general, inherits its specific exception from DbException and adds some information to allow the recognition of the cause.

To allow you the management of each situation according with your RDBMS NH has : ISQLExceptionConverter.

To instruct NH in order to use your own implementation of ISQLExceptionConverter the corresponding configuration is:

<property name="sql_exception_converter">NameSpace.YourImpl, AssemblyName</property>

or, through programatic configuration :

conf.SetProperty(Environment.SqlExceptionConverter,typeof(YourImpl).AssemblyQualifiedName);

Before show an implementation of ISQLExceptionConverter there is another useful method to know:

ADOExceptionHelper.ExtractDbException(Exception sqlException)

The method extract the first System.Data.Common.DbException from a given sqlException tree (the exception itself and its inner exceptions).

Now an example of ISQLExceptionConverter for MsSQL:

public class MsSqlExceptionConverterExample : ISQLExceptionConverter
{
public Exception Convert(AdoExceptionContextInfo exInfo)
{
var sqle = ADOExceptionHelper.ExtractDbException(exInfo.SqlException) as SqlException;
if(sqle != null)
{
switch (sqle.Number)
{
case 547:
return new ConstraintViolationException(exInfo.Message,
sqle.InnerException, exInfo.Sql, null);
case 208:
return new SQLGrammarException(exInfo.Message,
sqle.InnerException, exInfo.Sql);
case 3960:
return new StaleObjectStateException(exInfo.EntityName, exInfo.EntityId);
}
}
return SQLStateConverter.HandledNonSpecificException(exInfo.SqlException,
exInfo.Message, exInfo.Sql);
}
}
  • 547 is the exception number for constraint conflict.
  • 208 is the exception number for an invalid object name in the SQL.
  • 3960 is the exception number for Snapshot isolation transaction aborted due to update conflict.

As you can see you can re-throw your own Exception for each situation and then capture and manage it in a high application level.

Again… Enjoy NHibernate injectability.

13 June 2009

Inflector.NET English and Spanish

In uNhAddIns are now available two implementation of famous Inflector.

For the implementation I started from Andrew Peters implementation (I don’t need to reinvent every thing and thanks to Andrew to allow me to use his code). After that I was working in some refactoring, extensions and so on in order to have what we may need to use for NHibernate.

So far both implementation are working pretty well and for table name is supporting even composed words as:

given CostomerOrder as class name the table name will be CostomerOrders.

The code is available here.

Now I’m starting the improvement of NHibernate’s INamingStrategy in order provide some more information to choose the right Table/Column name for a given Class/Property name. By the way the Inflector can be used outside INamingStrategy as, for example, in FluentNHibernate convention.

I’m needing some help to “massacre” the implementation so if you want play with uNhAddIns Inflector’s tests you are welcome (especially for the Spanish Inflector).

12 June 2009

typedef for generators

NHibernate 2.1.0Beta1 has another facility for your needs.

Now the <typedef> can be used even for generators:

<typedef name="HighLow" class="NHibernate.Id.TableHiLoGenerator, NHibernate">
<
param name="max_lo">99</param>
</
typedef>

<
class name="EntityCustomId">
<
id name="Id">
<
generator class="HighLow"/>
</
id>
<
property name="Name"/>
</
class>

Only as a reminder the <typedef> tag can be used for any other type definition (properties and collections types).

11 June 2009

ORM developer ? Complicated life!

How much complicated can be the professional life of a persistent layer developer ?

An example.

09 June 2009

DataBase; The Eliot Ness of IT ?

How many times we heard :

“I need to map this situation…bla…bla… Note: I can’t touch the DataBase schema.”

“I don’t like composite PK but I can’t change the DB schema.”

“I need to map this situation…bla…bla… Note: legacy DB.”

You can see phrases similar to those above in various NHibernate forums in various languages. The fact that questions are sent to a NH’s forum mean that somebody are developing a new .NET2.0 or .NET3.5 application using NHibernate with an existing DataBase (I’m hoping this is the only possible situation). If a company spent time and money to rewrite an obsolete application for sure is because it is needed.

Let me show another situation: In a new application the first base module was deployed in production. The team now has an existing application with an existing DB and is developing another module. In the second module we realize that some change is needed to the persistence we actually have in production. What the team should do ? Well…something normal for us is create a “migration step” for the DB. The same happen again and again in each sprint.

How much different is the situation when the existing DB is five, or more, years old ? (well… it is different but I’m not so sure that the difference is so big).

“I can’t change the DB because there are other applications using it”… my friend in this case the first step should be write a good service layer to serve “externals” applications.

Why, write a new .NET3.5 application, shouldn’t mean re-think the DB ? The DB is not a part of the old application ?

We are working in software, is the relational DB a piece of granite technology of the past century and nothing more ?

One of my preferred Mentor, illuminate me the way of ORM in these past years, is Scott W. Ambler. If you want help your DBA to come in the XXI century gives him some of Scott’s books. Waiting Christmas point him to this article and invite him to follow each singular link; perhaps you will win a friend.

Dear DBA we are not your enemies, we are both in the same ship.

08 June 2009

From where start to implements IDataBaseSchema

The auto-quote and auto import KeyWords features are now available in NHibernate but only for those dialects are providing an implementation of IDataBaseSchema.

If the NHibernate’s dialect, for your favorite RDBMS, does not provide an implementation of IDataBaseSchema, what can you do ?

Start point

First of all you need an easy way to know some “internals” of your DataProvider/RDBMS. The code to extract all information you are needing to implement a IDataBaseSchema is:

internal class Program
{
private static void Main(string[] args)
{

// Extract metadata for Oracle
CreateMetadataXml("System.Data.OracleClient", "User Id=NH; Password=nh");

// Extract metadata for MsSQL
CreateMetadataXml("System.Data.SqlClient", @"Data Source=localhost\SQLEXPRESS;Initial Catalog=NHTEST;Integrated Security=True");

Console.WriteLine("Work done!");
Console.ReadLine();
}

private static void CreateMetadataXml(string providerName, string connectionString)
{
DbProviderFactory factory = DbProviderFactories.GetFactory(providerName);

using (DbConnection conn = factory.CreateConnection())
{
try
{
conn.ConnectionString = connectionString;
conn.Open();

//Get MetaDataCollections and write to an XML file.
//This is equivalent to GetSchema()
DataTable dtMetadata = conn.GetSchema(DbMetaDataCollectionNames.MetaDataCollections);
dtMetadata.WriteXml(providerName + "_MetaDataCollections.xml");

//Get Restrictions and write to an XML file.
DataTable dtRestrictions = conn.GetSchema(DbMetaDataCollectionNames.Restrictions);
dtRestrictions.WriteXml(providerName + "_Restrictions.xml");

//Get DataSourceInformation and write to an XML file.
DataTable dtDataSrcInfo = conn.GetSchema(DbMetaDataCollectionNames.DataSourceInformation);
dtDataSrcInfo.WriteXml(providerName + "_DataSourceInformation.xml");

//data types and write to an XML file.
DataTable dtDataTypes = conn.GetSchema(DbMetaDataCollectionNames.DataTypes);
dtDataTypes.WriteXml(providerName + "_DataTypes.xml");

//Get ReservedWords and write to an XML file.
DataTable dtReservedWords = conn.GetSchema(DbMetaDataCollectionNames.ReservedWords);
dtReservedWords.WriteXml(providerName + "_ReservedWords.xml");
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
Console.WriteLine(ex.StackTrace);
}
}
}
}

The code above will create an XML file for each “matter” involved with IDataBaseSchema implementation.

What’s next

I don’t want to deep in details because each RDBMS may have different info so the only things I can tell you are:

  • There are some base classes that may work, as is, for your RDBMS : AbstractDataBaseSchema, AbstractTableMetadata, AbstractColumnMetaData, AbstractForeignKeyMetadata, AbstractIndexMetadata.
  • There are some implementations for various RDBMS where you can see which are the difference and where some information, extracted from the code above, was used : FirebirdMetaData.cs, MsSqlCeMetaData.cs, MsSqlMetaData.cs, MySQLMetaData.cs, OracleMetaData.cs, SQLiteMetaData.cs, SybaseAnywhereMetaData.cs.
  • Tests to pass are contained in NHibernate.Test.Tools.hbm2ddl namespace.

What you should do after implement IDataBaseSchema

Create a new JIRA ticket as:

  • Project : NHibernate
  • Issue Type: Improvement
  • Summary : New DataBase schema provider for “your preferred RDBMS”
  • Priority : Minor
  • Component : DataProviders / Dialects
  • Affects Version : “The last released”
  • Description : “Few words”
  • Attachment : this is the most important; send us your implementation!!

Thanks.

06 June 2009

Auto Quote Table/Column names

Since long time we have a very interesting request on NHibernate JIRA (NH-188).

If you are working in a multi-RDBMS application, you are annoyed, for sure, quoting a table-name or a column-name. As a very good persistent-layer this should be a NHibernate’s work.

I’m happy to announce that the problem is solved (even if, so far, is not done by default).

If you want that NH take the responsibility of properly quote table-name or column-name only where really needed now you can do it in two ways:

  1. Through configuration
  2. Explicitly by code

Through configuration

As you probably know NHibernate’s configuration has some property oriented to mapping-to-DLL tasks.

For schema integration you can use

<property name="hbm2ddl.auto">create-drop</property>

Allowed values for hbm2ddl are:

  • update : auto execute SchemaUpdate on BuildSessionFactory
  • create : auto execute SchemaExport on BuildSessionFactory
  • create-drop : auto execute SchemaExport on BuildSessionFactory recreating the schema
  • validate : auto execute SchemaValidator on BuildSessionFactory

The new property is:

<property name="hbm2ddl.keywords">auto-quote</property>

Allowed values are:

  • none : disable any operation regarding RDBMS KeyWords
  • keywords : (activated by Default)imports all RDBMS KeyWords where the NH-Dialect can provide the implementation of IDataBaseSchema (so far available for MsSQL, Oracle, Firebird, MsSqlCe, MySQL, SQLite, SybaseAnywhere)
  • auto-quote : imports all RDBMS KeyWords and auto-quote all table-names/column-names on BuildSessionFactory

Explicitly by code

When you have an instance of a configured configuration (just before call BuildSessionFactory) you can execute:

SchemaMetadataUpdater.QuoteTableAndColumns(configuration);

That’s all.

The advantage

Take a look to this mapping:

<class name="Order">
<
id type="int">
<
generator class="native"/>
</
id>
<
property name="Select"/>
<
property name="From"/>
<
property name="And"/>
<
property name="Column"/>
<
property name="Name"/>
</
class>

Well… now it is working fine without explicitly quote.

Enjoy NHibernate’s multi-RDBMS easy support.