Want to show your appreciation? Please to my charity.

Friday, April 30, 2010

SpringExtension: Extends DbProvider to Provide Hook to Connection Open and Close

This is inspired by the need of populating ODP.Net's OracleConnection.ClientId property with current thread identity when Spring.Net's ADO Support is used in the data access layer.

The major difference between Java’s DataSource and its Spring.Net’s counterpart, DbProvider, is that the connection returned from DbProvider is not open. With Java’s  DataSource, you can easily make a wrapper to set up the connection before it’s used by the application code. But things become difficult with DbProvider, you just cannot do this. If you open the connection in the wrapper, the subsequent call to open the connection by the Spring.Net framework will fail (you wish it will check the status before calling open? nope, that’s not the case).

But there is a workaround. We can use an event handler to setup the connection at the moment Spring.Net framework opens the connection. Although IDbConnection doesn't provide any mean to let you react when a database connection is opened and/or closed, but the System.Data.Common.DbConnection object, which is the base class of most ADO implementation, has an event called StateChange. This gives us the hook to extend the DbProvider so that application code can react on connection open and/or close event. We can do many things with this, for example call a stored procedure to setup the connection options, tell database the current thread’s identity name, even setup the in-memory database for unit testing and etc.

The good new is we have implemented the hook in SpringExtension, so you don’t have to re-do the work. Using SpringExtension, you can easily achieve what I have described in my earlier post about setting oracle CLIENT_IDENTIFIER database session information, by just below configuration settings. No c# program needs to be written.

  <object id="DbProvider" type="Spring.Data.Common.ExtendedDbProvider, Spring.Data.Extension">
    <property name="TargetDBProvider" ref="TargetDbProvider"/>
    <property name="ConnectionStateListener">
      <object type="Spring.Data.Support.OdpNetClientIdentifierSetter,  Spring.Data.Extension"/>
    </property>
  </object>
  
  <db:provider id="TargetDbProvider" provider="OracleODP-2.0" connectionString="${DB.IMM.ConnectionString}"/>

You can further customize the action by replacing the OdpNetClientIdentifierSetter with any implementation of IDbConnectionStateListener, defined in SpringExtension as:

    /// <summary>
    /// Interface to receive the database connection state change.
    /// </summary>
    /// <author>Kenneth Xu</author>
    public interface IDbConnectionStateListener
    {
        /// <summary>
        /// To be called when the <paramref name="connection"/> state changed
        /// from <paramref name="original"/> to <paramref name="current"/>.
        /// </summary>
        /// <param name="connection">
        /// The database connection that its state has changed. 
        /// </param>
        /// <param name="original">
        /// The original state of the connection before change.
        /// </param>
        /// <param name="current">
        /// Current state of the connection.
        /// </param>
        void AfterStateChange(IDbConnection connection, ConnectionState original, ConnectionState current);
    }

Or if all you care is open and/or close event, you can extend DbConnectionStateListenerBase by override any of the below two virtual methods:

        /// <summary>
        /// This method is called after database is opened.
        /// </summary>
        /// <param name="connection">
        /// The connection that was just opened.
        /// </param>
        public virtual void AfterConnectionOpen(IDbConnection connection)
        {
            
        }
 
        /// <summary>
        /// This method is called after database is closed.
        /// </summary>
        /// <param name="connection">
        /// The connection that was just closed.
        /// </param>
        public virtual void AfterConnectionClose(IDbConnection connection)
        {
            
        }

Welcome comments and suggestions

Wednesday, April 28, 2010

SpringExtension: Declarative Transaction Management and Unit Test

Declarative transaction management is one of the compelling reasons to using Spring.Net. But there are still cases where you need to programmatically rollback a transaction without throwing an exception. Spring.Net let you do so with below method call:

TransactionInterceptor.CurrentTransactionStatus.SetRollbackOnly();

The problem with this call emerges when you unit test your business objects containing such statement. You got an exception when you run your test because there is no transaction context exists.

The solution to this problem is dependency injection. We need to introduce an interface that can give us the transaction status.

    public interface ITransactionStatusProvider
    {
        ITransactionStatus CurrentTransactionStatus { get; }
    }

We can then use the interface to rollback current transaction in our business objects:

   1:      public class BusinessObject
   2:      {
   3:          ITransactionStatusProvider TransactionStatusProvider { get; set; }
   4:          public void BusinessMethod()
   5:          {
   6:              // Do some work.
   7:              if (something is wrong)
   8:              {
   9:                  TransactionStatusProvider.CurrentTransactionStatus.SetRollbackOnly();
  10:              }
  11:          }
  12:      }

In line 3, we declared a property so the provider can be injected; In line 9, we used the provider to rollback the transaction in error condition.

Since both provider and status are interfaces, we can easily mock them in our unit test:

   1:          [Test] public void BusinessMethodRollBackTransactionWhenSomethingIsWrong()
   2:          {
   3:              var mockProvider = MockRepository.GenerateMock<ITransactionStatusProvider>();
   4:              var mockStatus = MockRepository.GenerateMock<ITransactionStatus>();
   5:              mockProvider.Stub(p => p.CurrentTransactionStatus).Return(mockStatus);
   6:   
   7:              var sut = new BusinessObject {TransactionStatusProvider = mockProvider};
   8:              // sut.SetUpWrongCondition
   9:              sut.BusinessMethod();
  10:   
  11:              mockStatus.AssertWasCalled(x=>x.SetRollbackOnly());
  12:          }

The last thing left is to write a real implementation that actually works with the Spring.Net’s declarative transaction management:

    public class TransactionInterceptorStatusProvider : ITransactionStatusProvider
    {
        public ITransactionStatus CurrentTransactionStatus
        {
            get { return TransactionInterceptor.CurrentTransactionStatus; }
        }
    }

We then use Spring.Net’s dependency inject feature to set the the BusinessObject.TransactionStatusProvider to an instance of TransactionInterceptorStatusProvider. Problem resolved!

This is part of SpringExtension, both source and binary are available on Google Code.