Try fast search NHibernate

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.

9 comments:

  1. Yikes! it might be very useful.
    Till now we did simple inner exception tracking with If's. Up till now it works more or less, since all our customer happen to use the same kind of database.

    But what if we had had to use different type of DB for different client!

    ReplyDelete
  2. do you think that can be possible to have the implementations of ISQLExceptionConverter for the most used db in the next nhb release?

    i don't know when you have planned to release it
    and if it's possible to put them into the nhibernate assembly

    anyhow thanks, nhibernate rocks ;)

    ReplyDelete
  3. There is no plan to have different implementation in NH because that mean have a strong reference to each DataProvider assembly and redefine all Exceptions.

    ReplyDelete
  4. Is there the possibility to convert NHibernate Exceptions too? (eg. StaleObjectException) In our project, the business logic does not have a reference to NHibernate and could therefore not catch this exceptions.

    ReplyDelete
  5. Hi!

    Exception converter abstraction is a cool feature. I have some problems using it. It seems that when I call ISession.Flush(), converter is not used by NHibernate. The same problem is described here: http://stackoverflow.com/questions/1524167/custom-exception-using-nhibernate-isqlexceptionconverter

    What am I doing wrong?

    ReplyDelete
  6. Isolate the case in a test.
    If you can recreate the situation that is a bug and you can attach your test in a JIRA.

    ReplyDelete
  7. While posting the test case I've found that patch is already posted: http://nhjira.koah.net/browse/NH-2020

    ReplyDelete
  8. Hi Fabio,

    I've followed this article to implement ADO Exception Management in my application, but it isn't working. Please, can you tell me if I have to consider additional something?

    I've also posted this issue in the Nhibernate Hispano group.

    Thanks in advanced.

    ReplyDelete