Building a Middle Tier Component using NHibernate and Spring.NET
Introduction
I have had, professionally, a fair amount of time with Visual C++, Java, and recently, with .NET technologies. I have worked with the popular Spring framework and Hibernate in Java. And now, I have recently discovered that both of these technologies are available in .NET as well. But the Spring framework in Java is more feature rich than Spring.NET. Specifically, I like the integration part of Spring with Hibernate (spring.orm.Jar). That makes the manipulation of data using Hibernate, very easy and in an object oriented manner.
Background
Unlike the Spring framework for Java, Spring.Net has no integration with any ORM technologies. I mean, no ORM integration is found. At least, till now, I have not found anything. For Spring.Net, the declarative transaction demarcation was written by using COM+ services. For those who have a working knowledge with the Spring framework in Java, I think they will not prefer using the COM+ services. COM+ needs advanced knowledge when writing professional software. Moreover, COM+ requires a strong name for the assembly that contains the Serviced Component.
That’s why I did try to implement this integration, so that one can easily write a business layer component without using COM+ services.
I have borrowed the entire concept from the Spring framework for Java, which is an open source framework, while implementing the integration code. I have tried to maintain the same names for the classes and interfaces while implementing. But I have changed many methods with my own code. I have changed the behaviors of some classes too. So I am not claiming that my implementation is an exact copy of the Spring framework for Java. I have just implemented the Spring.Orm.dll as an integration between NHibernate and Spring.Net, like the spring.orm.jar in the Java world.
Article goals
This is my first submission of any kind, so keep non-constructive criticism to a minimum. I am 100% open to feedback, I don't claim to have all the answers, or claim to have the best methods to achieve this or that. Over the next couple in a series of articles, I'd like to demonstrate some n-tier frameworks utilizing NHibernate and Spring.NET.
Getting started
Before starting, I will assume that the reader has some basic knowledge about the following:
- What is NHibernate and how to configure and work with it? Learn from here.
- What is Inversion of Control? Learn from here.
- How to use Spring as an IOC container? Learn from here.
As you might know, NHibernate can be used with any relational database by configuration. On this occasion, I have used the Oracle 9i database as the data sore. Of course, you can change it to any other database by just configuring it in the App.Config file.
From the next section, we will build a middle tier component using Spring.Orm.dll, and we will watch hwo simple it is. And the design is really pluggable and elegant.
Here is the abstract design of our server component:
Step by Step
First of all, start Visual Studio .NET. Create a new console application. Give any name that is legitimate in your business platform. In this tutorial, I am using the name “SpringClient”.
Add references to the following assemblies that are given inside the binary distribution with this article:
- castle.dynamicproxy
- hashcodeprovider
- iesi.collections
- log4net
- nhibernate
- nunit.framework
- spring.aop
- spring.core
And certainly, add a reference to:
- spring.orm
Now, add an App.Config file to the project. And write the following:
<?xml version="1.0"encoding="utf-8" ?>
<configuration>
<configSections>
<sectionGroup name="spring">
<section name="context"
type ="Spring.Context.Support.ContextHandler, Spring.Core"/>
<section name="objects"
type="Spring.Context.Support.DefaultSectionHandler, Spring.Core" />
</sectionGroup>
<section name="nhibernate"
type="System.Configuration.NameValueSectionHandler, System,
Version=1.0.5000.0,Culture=neutral,
PublicKeyToken=b77a5c561934e089" />
<section name="log4net"
type="log4net.Config.Log4NetConfigurationSectionHandler,log4net" />
</configSections>
<spring>
<context><resource uri="config://spring/objects"/></context>
<objects>
<!—WRITE YOUR SPRING CONFIGURATION HERE-->
</objects>
</spring>
<nhibernate>
<add key="hibernate.show_sql" value="true"/>
<add key="hibernate.connection.provider"
value="NHibernate.Connection.DriverConnectionProvider" />
<!-- <add key="hibernate.dialect"
value="NHibernate.Dialect.MsSql2000Dialect" />
<add key="hibernate.connection.driver_class"
value="NHibernate.Driver.SqlClientDriver" />
<add key="hibernate.connection.connection_string"
value="Server=localhost;initial
catalog=nhibernate;Integrated Security=SSPI" />
-->
<!-- This is the System.Data.OracleClient.dll
provider for Oracle from MS -->
<add key="hibernate.dialect"
value="NHibernate.Dialect.Oracle9Dialect" />
<add key="hibernate.connection.driver_class"
value="NHibernate.Driver.OracleClientDriver" />
<add key="hibernate.connection.connection_string"
value="Data Source=ihis;User ID=system;Password=manager;" />
</nhibernate>
<!-- This section contains the log4net configuration settings -->
<log4net>
<!-- Define some output appenders -->
<appender name="rollingFile"
type="log4net.Appender.ConsoleAppender,log4net" >
<param name="File" value="log.txt" />
<param name="AppendToFile" value="true" />
<param name="RollingStyle" value="Date" />
<param name="DatePattern" value="yyyy.MM.dd" />
<param name="StaticLogFileName" value="true" />
<layout type="log4net.Layout.PatternLayout,log4net">
<param name="ConversionPattern"
value="%d [%t] %-5p %c [%x] <%X{auth}> - %m%n" />
</layout>
</appender>
<!-- Setup the root Category, add the
appenders and set the default priority -->
<root>
<priority value="DEBUG" />
<appender-ref ref="rollingFile" />
</root>
</log4net>
</configuration>
Tool much XML! Scared?
Don’t. I am explaining. Here in this configuration file, we have just configured the Spring’s IOC container, the NHibernate, and the Log4net for logging purposes.
Find out the comment “WRITE YOUR SPRING CONFIGURATION HERE” inside the XML. And in this area, you will configure the objects that will be loaded by the Spring IOC. If it seems to be too complex to you, don’t just leave. Continue, and you will be able to work it out even without knowing the details of the IOC at the moment. You need to change the DSN (in this example, I have used the DSN name “ihis”), user name, and the password in the database configuration block.
Add an XML file named “hibernate.cfg.xml” to your project. This will be used by NHibernate. Change the build action for this file to Embedded Resource.
Insert the following configuration into the file:
<?xml version="1.0" encoding="utf-8"?>
<hibernate-configuration xmlns="urn:nhibernate-configuration-2.0">
<session-factory name="NHibernate.Test"/>
</hibernate-configuration>
Our business logic layer will use the facility of declarative transaction demarcation. So we need a transaction manager. As for this moment, we are using the NHibernate for ORM, and we will use Spring.Orm.Hibernate.HibernateTransactionManager
as the transaction manager from the Spring.Orm assembly.
Let's configure the transaction manager now. Insert the following configuration block to the point which I have marked with the comment:
<object id="myTransactionManager"
type="Spring.Orm.Hibernate.HibernateTransactionManager, Spring.Orm">
<property name="SessionFactory">
<ref object="mySessionFactory"/>
</property>
</object>
For this example, we will implement a single business object for now. I have named it as Product
. So create a class named SpringClient.Utility.Product.UProductDTO
. The suffix DTO stands for “Data Transfer Object”. Insert the following code:
using System;
namespace SpringClient.Utility.Product
{
///
///
/// The Data transfer object for the Product entity.
///
/// Moim Hossain
///
[Serializable()]
public class UProductDTO
{
///
/// Constructor Method
///
public UProductDTO()
{
}
///
/// The Version Number for the Entity.
/// Hibernate will use this version number for ensuring the atomicity
/// of single data object.
///
private int versionNumber;
///
/// Get or set the Data Version Number
///
public int VersionNumber
{
get
{
return versionNumber;
}
set
{
versionNumber = value;
}
}
///
/// The Name of the Product
///
private string name;
///
/// Get or set the name of the Product
///
public string Name
{
get
{
return name;
}
set
{
name = value;
}
}
///
/// The ID [PRIMATY KEY] for the product object
///
private long id;
///
/// Get or set the Product ID
///
public long ID
{
get
{
return id;
}
set
{
id = value;
}
}
}
}
Now as we will persist the Product
instance into the data store, we need Hibernate mapping a file for this class. Add an XML file into the same namespace named, “UProductDTO.hbm.xml”. And change the Build action of this XML resource to Embedded Resources. The Spring framework always suggest the “”Programming to interface” coding practice. We are not an exception. So let’s write a Data Access Object interface for Product
.
using System;
namespace SpringClient.Utility.Product
{
///
/// The Interface for accessing the Data Store for the Product
/// The Suffix DAO is for Data Access Object.
///
/// This is a typical interface for
/// the Data Access Layer of an Application
///
/// Declares the CRUD for Product Object
///
/// Moim Hossain
///
public interface IUProductDAO
{
///
/// Create a Product.
///
/// The Product That will be Persisted
/// The Product The has saved to the Data Store.
/// Contains the ID (Generated by datastore)
UProductDTO Create(UProductDTO Product);
///
/// Retrive a Product from the Data Store by ID
///
/// The ID that will be searched into the Data store
/// The Retrived Data Object
UProductDTO Retrive( long ProductID );
///
/// Update a product Object
///
/// The Product Instance that should be Updated
void Update( UProductDTO Product );
///
/// Deletes a product
///
/// The product that will be Deleted
void Delete( UProductDTO Product );
///
/// Search for some object by matching a criteria
///
/// The Hibernate Query string
/// The parameter values for the query
/// The List of UProducts that has a match for the given query
UProductDTO[]FindProducts( string queryString ,
object[]Parameters );
}
}
Now we are going to implement the interface:
using System;
using System.Collections;
using Spring.Orm.Hibernate.Support;
namespace SpringClient.Utility.Product
{
///
/// The Implementation of the IUProductDAO.
///
/// This is a concrete Data Access Object for the Product object.
///
/// Implemented all the methods remains in DAO interface.
///
/// Inherited from for
/// the Data Acccess support provided by the Hibernate ORM.
///
/// Moim Hossain
///
public class UProductDAO : HibernateDaoSupport, IUProductDAO
{
///
/// Constructor
///
public UProductDAO()
{
}
#region IUProductDAO Members
///
/// Create a Product.
///
/// The Product That will be Persisted
/// The Product The has saved to the Data Store.
/// Contains the ID (Generated by datastore)
public UProductDTO Create(UProductDTO Product)
{
HibernateTemplate.Save( Product ) ;
return Product;
}
///
/// Retrive a Product from the Data Store by ID
///
/// The ID that will be searched into the Data store
/// The Retrived Data Object
public UProductDTO Retrive( long ProductID )
{
return HibernateTemplate.Load( typeof( UProductDTO ) ,
ProductID ) as UProductDTO;
}
///
/// Update a product Object
///
/// The Product Instance that should be Updated
public void Update( UProductDTO Product )
{
HibernateTemplate.Update( Product );
}
///
/// Deletes a product
///
/// The product that will be Deleted
public void Delete( UProductDTO Product )
{
HibernateTemplate.Delete( Product );
}
///
/// Search for some object by matching a criteria
///
/// The Hibernate Query string
/// The parameter values for the query
/// The List of UProducts that has a match for the given query
public UProductDTO[]FindProducts( string queryString ,
object[]Parameters )
{
IList resultList =
HibernateTemplate.Find( queryString ,
Parameters ) as IList;
if( null != resultList )
{
UProductDTO[]products =
new UProductDTO[resultList.Count];
resultList.CopyTo( products , 0 );
return products;
}
return null;
}
#endregion
}
}
Note that we have inherited our data access class from Spring.Orm.Hibernate.Support.HibernateDaoSupport
for easy access to the NHibernate methods through the HibernateTemplate
. Now it is time to write the business logic layer. Let’s write an interface for our product's business logic layer:
using System;
namespace SpringClient.Utility.Product
{
///
/// The Interface for the Product Service.
/// This is a interface for the Business Logic
/// Layer of Product Object.
///
/// Contains all the methods that are related
/// to the business issues of Product entity.
///
/// When Implemented, Concrete objects
/// will implement the Business Logic for the interaction
/// with product object.
///
/// Moim Hossain
///
public interface IUProductService
{
///
/// Get or set the Data Access Object for the Product object.
///
IUProductDAO ProductDAO
{
get;
set;
}
///
/// Save a Product
///
/// The Product to be persisted
/// Saved product
UProductDTO SaveProduct( UProductDTO Product );
///
/// Retrives a product from a data store
///
/// The ID of the product to be retrived
/// The retrived product
UProductDTO RetriveProduct( long ProductID );
///
/// Update a Product Instance
///
/// The Product to be Updated
void UpdateProduct( UProductDTO Product );
///
/// Deletes a product
///
/// The product to be deleted
void DeleteProduct( UProductDTO Product );
///
/// Search for some object by matching a criteria
///
/// The Hibernate Query string
/// The parameter values for the query
/// The List of UProducts that has a match for the given query
UProductDTO[]FindProducts( string queryString ,
object[]Parameters );
}
}
And now I will provide the implementation:
using System;
namespace SpringClient.Utility.Product
{
///
/// The Concrete Business Logic layer for the product Object.
///
/// Implemented the inteface.
///
/// Invoke by the service class for the Product Entity.
///
/// Moim Hossain
///
public class UProductServiceImpl : IUProductService
{
///
/// Constructor
///
public UProductServiceImpl()
{
}
///
/// The Product DAO Interface
///
private IUProductDAO productDAO;
#region IUProductService Members
///
/// Get or set the Data Access Object for the Product object.
///
public IUProductDAO ProductDAO
{
get
{
return productDAO;
}
set
{
productDAO = value;
}
}
///
/// Save a Product
///
/// The Product to be persisted
/// Saved product
public UProductDTO SaveProduct(UProductDTO Product)
{
return productDAO.Create( Product );
}
///
/// Retrives a product from a data store
///
/// The ID of the product to be retrived
/// The retrived product
public UProductDTO RetriveProduct( long ProductID )
{
return productDAO.Retrive( ProductID );
}
///
/// Update a Product Instance
///
/// The Product to be Updated
public void UpdateProduct( UProductDTO Product )
{
productDAO.Update( Product );
}
///
/// Deletes a product
///
/// The product to be deleted
public void DeleteProduct( UProductDTO Product )
{
productDAO.Delete( Product );
}
///
/// Search for some object by matching a criteria
///
/// The Hibernate Query string
/// The parameter values for the query
/// The List of UProducts that has a match for the given query
public UProductDTO[]FindProducts( string queryString ,
object[]Parameters )
{
return productDAO.FindProducts( queryString , Parameters );
}
#endregion
}
}
Note that UProductServiceImpl
inherits from no Spring specific class, but this class has the ability to propagate transactions on its methods. It is a crucial difference with COM+. In COM+, your business class must inherit the ServicedComponent
class. At this point, we have completed our business component development. Now, we will configure our business class for the Spring transaction support. Insert the following XML into the AppConfig.xml, just under the transaction manager configuration block that you inserted before a while:
<object id="mySessionFactory"
type="Spring.Orm.Hibernate.LocalSessionFactoryObject, Spring.Orm">
<property name="MappingResources">
<list>
<value>SpringClient.Utility.Product.UProductDTO, SpringClient</value>
</list>
</property>
<property name="ExportSchema" value="true"/>
</object>
Here we are building the Session Factory object. This is simply a proxy of the NHibernate Session factory. The ExportSchema
property is set to true
so that when you will run your server, the database tables that are required will be created for you. Set it to false
to prevent table creation. Now, add the following XML fragments:
<!--UProduct Hibernate+ Spring conf -->
<object id="UProductTarget"
type="SpringClient.Utility.Product.UProductServiceImpl, SpringClient">
<property name="ProductDAO">
<ref object="ProductDAO"/>
</property>
</object>
<object id="UProductService"
type="Spring.Transaction.Interceptor.
TransactionProxyFactoryObject, Spring.Orm">
<property name="TransactionManager">
<ref object="myTransactionManager">
</property>
<property name="Target">
<ref object="UProductTarget">
</property>
<property name="TransactionAttributes">
<name-values>
<add key="Save*" value="PROPAGATION_REQUIRES_NEW"/>
<add key="Update*" value="PROPAGATION_REQUIRED"/>
<add key="Delete*" value="PROPAGATION_REQUIRED"/>
</name-values>
</property>
</object>
What is it? First, we are configuring a proxy for our business class that will provide transaction support. Here, we are specifying the transaction propagation as well. We are specifying to Spring that we need a “Requires New” propagation for the “Save
” method and a “Required” propagation mode for the “Update
” and “Delete
” methods. Note that you can specify the method name by using a regular expression.
Now, it is time to think how you are establishing the communication between the client application and your business class? It’s entirely your choice. You can select web services, remoting - any thing you like. For the sake of simplicity, I have implemented the example using .NET Remoting. So, let’s write a class for exposing the remoting objects. This class also plays a role as a façade for the IOC loaded objects.
using System;
using Spring.Context;
using Spring.Context.Support;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Http;
namespace SpringClient.Service
{
///
/// A Service factory used to resolve
/// and provide the service for a service name.
/// This class is tightly coupled with Spring Ioc.
///
/// Responsible for Constructing
/// the Spring Context and retains a static instance
/// of the spring context.
///
/// Moim Hossain
///
public class ServiceFactory
{
///
/// preventing the Out side world from
/// creating an instance of this class.
///
private ServiceFactory()
{
}
///
/// Expose the Services
///
public static void ExposeServices( params Type[]services)
{
HttpServerChannel channel = new HttpServerChannel(8080);
ChannelServices.RegisterChannel( channel );
foreach( System.Type service in services )
{ // Iterate through all the services
System.Runtime.Remoting.RemotingConfiguration.
RegisterWellKnownServiceType(
service , "ProductService",
System.Runtime.Remoting.
WellKnownObjectMode.SingleCall
);
}
}
///
/// Provide the service object by the name.
///
/// The Service Name to be resolved
/// The Service Object
public static object ProvideService( string serviceName )
{
if( null == m_AppContext )
throw new ApplicationException(
"Spring not initialized yet.", null );
// return the service
return m_AppContext[serviceName];
}
///
/// The Spring Context reference
///
private static IApplicationContext m_AppContext = null;
///
/// Constructs the Spring Container
///
public static void BuildSpringContext( )
{
// Make this method work as Single-ton fashion.
if( null != m_AppContext ) return ;
// No need to Build again
try
{ // Get the Handler for the spring Context
m_AppContext = ContextRegistry.GetContext();
if( null != m_AppContext )
{ // Success !
System.Console.WriteLine("Spring" +
" Initialized Successfully.");
}
}
catch(Exception ex )
{
System.Console.WriteLine( "Failed to" +
" Initialize the Spring context." );
System.Diagnostics.Trace.WriteLine( ex.Message );
}
}
}
}
Let’s write the service class that will expose the business access interface to the extrenal world:
using System;
using System.Web.Services;
using SpringClient.Service;
namespace SpringClient.Utility.Product
{
///
/// This the class with which the client will interact.
/// For the Sake of Simplicity
/// I have made this class as a simple C# Class.
///
/// But when implemented in a enterprise
/// level software, this class will be implemented as a
/// Remoting Server, derived from .
/// Or, as a Web service contains some web method.
///
/// Moim Hossain
///
public class RpcUProductService : MarshalByRefObject
{
///
/// Constructor
///
public RpcUProductService()
{
}
///
/// The Service Name that will be used
/// to resolve the Business Service Interface.
///
private string SERVICE_NAME = "UProductService";
///
/// Save a product
///
/// The product to be saved.
/// The Saved product.
/// Thows if any error occured while
public UProductDTO SaveProduct( UProductDTO Product )
{
return ( ServiceFactory.ProvideService(SERVICE_NAME)
as IUProductService ).SaveProduct( Product );
}
///
/// Retrive a product by the id
///
/// The Id of the product
/// the product
/// Thows if any error occured while
public UProductDTO RetriveProduct( long ProductID )
{
return ( ServiceFactory.ProvideService(SERVICE_NAME)
as IUProductService ).RetriveProduct( ProductID );
}
///
/// Update a product
///
/// The product to be updated
/// Thows if any error occured while
public void UpdateProduct( UProductDTO Product )
{
( ServiceFactory.ProvideService(SERVICE_NAME)
as IUProductService ).UpdateProduct( Product );
}
///
/// Delete a product
///
/// The product to be deleted
/// Thows if any error occured while
public void DeleteProduct( UProductDTO Product )
{
( ServiceFactory.ProvideService(SERVICE_NAME)
as IUProductService ).DeleteProduct( Product );
}
///
/// Search for some object by matching a criteria.
///
/// This method is tightly coupled
/// with Hibernate specific query string.
/// But You can easily provide some
/// strong type method to encapsulate the
/// hibernate specific code inside your service layer.
///
/// The Hibernate Query string
/// The parameter values for the query
/// The List of UProducts that has a match for the given query
/// ///
///
/// // I am Searching Only those
/// // objects that matchs with a given ID
/// // For More Inforamtion about writing
/// // Hibernate Query string and about Hibernate
/// // please search at http://nhibernate.sourceforge.net/.
/// string QueryString = "select UProduct from" +
/// " UProduct in class UProductDTO where UProduct.ID = ?";
/// // The Given ID is 1.
/// object[]Parameters = new Object[]{ Convert.ToInt64(1) };
///
/// UProductDTO[]Results =
/// new RpcUProductService().FindProducts(
/// QueryString , Parameters ); ///
///
///
public UProductDTO[]FindProducts( string queryString ,
object[]parameters )
{
return ( ServiceFactory.ProvideService( SERVICE_NAME )
as IUProductService ).FindProducts( queryString,
parameters );
}
}
}
Now, change the main method of your application as:
using System;
using SpringClient.Service;
using SpringClient.Utility.Product;
namespace SpringClient
{
///
/// The Test Class
///
/// Moim Hossain
///
public class MainClass
{
///
/// The main entry point for the application.
///
[STAThread]
static void Main(string[] args)
{
// log4net.Config.XmlConfigurator.Configure();
ServiceFactory.BuildSpringContext();
ServiceFactory.ExposeServices( typeof(
SpringClient.Utility.Product.RpcUProductService) );
System.Console.WriteLine("Service Exported. " +
"Server is running.");
System.Console.ReadLine();
}
}
}
It’s done. You have completed a business tier with declarative transaction facilities. Here, I am providing a screenshot of my project structure.
Writing a client for testing our server
Let’ create a Windows application named RemoteClient. Create a web reference to the remoting component. Select Add Web Reference from the Project menu, and write the following on the URL box: http://localhost:8080/ProductService?WSDL. I am sssuming you are running the server program in the same machine. Otherwise, insert the server machine IP address in place of localhost. Give a name for the service. I have given “ProductService”, for this example.
Now, you can invoke the methods you have written in your product service, as follows:
try
{
RemoteClient.ProductService.RpcUProductServiceService rpc =
new RemoteClient.ProductService.RpcUProductServiceService();
UProductDTO product = new UProductDTO();
product.name = "MyProduct";
rpc.SaveProduct( product ); // Saved !!!
}
catch(Exception Ex )
{
Console.WriteLine( Ex.Message );
}
That is all. Simple. Isn't it?
Point of interest for further development
The Spring framework for Java provides some more attractive features that I have not implemented in this version, such as save point management, converting Hibernate specific exceptions to Spring's Common Data Access Exceptions, and some other features that are also very important. However, I hope I will implement those parts whenever possible.
Conclusion
I have read and tried to follow the designs from the Spring framework for Java while writing this. But in many areas, I had to break the standard because of language inconsistencies and some other stuff. However, thanks to the Spring framework team.
In order to execute the business component, you must need the assemblies-containing Spring and NHibernate modules. Although I have given these assemblies with this article, you can download these from the following links:
I hope you will enjoy this article. And I will appreciate all kinds of suggestions and proposals from anyone. Thanks.
About moim
I have completed my BSc (engineering) Computer science and Engineering from Asian University of Bangladesh at 2003. After that I am working as a Senior Software programmer in a reputed software establishment. I have extensive experience in Visual C++ 6.0, J2EE and .NET Technologies. Any one can contact with me at moim023@yahoo.com.
Click here to view moim's online profile. |
欢迎大家扫描下面二维码成为我的客户,扶你上云