This white paper explores the power of automatic programming and its application on NHibernate Technology, allowing human-programmers to write their code on a higher level of abstraction ensuring homogenous and Error-Free code.
Tracking down redundant code and implementing a generic algorithm that generates such code is the Key factor in Automatic-Programming.
It takes sole discretion and independent judgement of the Developer to trace similar code-patterns in their application and making all efforts in reducing the overall Project-Development-Time by automating such process.
2. 1 Abstract
This white paper explores the power of automatic Programming and its application on NHibernate
Technology, allowing human-programmers to write their code on a higher level of abstraction
ensuring Homogenous and Error-Free code.
Tracking down Redundant code and Implementing a Generic algorithm that generates such code is
the Key factor in Automatic-Programming.
It takes sole discretion and independent judgement of the Developer to trace similar code-patterns
in their application and making all efforts in reducing the overall Project-Development-Time by
automating such process.
2 Overview of NHibernate
NHibernate is an Object Relational Manager(ORM) persistence engine in the form of a Framework.
It loads business objects from a database and saves changes from those objects back to the
database. NHibernate uses mapping files to guide its translation from the database to business
objects and back again. Mapping simply specifies which tables in the database go with which
classes in the business model. Conventionaly we create a separate mapping file for each class,
keeping the mapping files short and easy to read.
For example, if we have an employee_details table then the TableColumn-to-ClassProperty
Mapping details goes into EmployeeDetails.hbm.xml and the BusinessObject Properties goes into
the EmployeeDetails.cs Class.
Figure 1. Architecture Diagram of NHibernate Framework
3. 3 Overview of Automatic Programming
When computer-programs are written by a machine, the process is called automatic programming.
Artificial Intelligence researchers are interested in studying automatic programming for two basic
reasons:
• First, it would be highly useful to have a powerful automatic programming systems that could
receive casual and imprecise specifications for a desired target program and then correctly
generate that program
• Second, automatic programming is widely believed to be a necessary component of any
intelligent system and is therefore a topic for fundamental research in its own right.
4 Redundant Codes in NHibernate : A pain in the neck
Lets say, you are on a rapid development project which uses NHibernate as its ORM and SQL-
Server as its database.
Your database has around 50 Tables and each Table has around 20 Columns.
For mapping the Business Model to NHibernate one would need the following items for each table :
• A BO class file ( eg: EmployeeDetails.cs )
• A XML Mapping file ( eg: EmployeeDetails.hbm.xml )
So you have to create 50 such BO class files and another 50 XML Mapping files, manually typing-In
every Column-to-Property mapping and keeping Proper Naming Conventions in mind.
Sounds Nightmarish and Error-Prone to every Developer !!
There are many Third-party software vendors providing Licensed code-generator tools for
NHibernate, but they are not always customizable to the project demands.
Why not start coding your own code-generators for your proejct that could be as per your
conventions and thereby saving Time and Money ??
5 Automatic Programming meets NHibernate
Using SQL-Server as coding interface, one can auto-generate codes for the BO class File and the
XML Mapping File required for the DotNet application.
Lets explore more on how it could be done.
4. 5.1 Analysing similar Code Patterns in XML Mapping File
Conventionally a XML mapping file for the table employee_details looks like the figure given below :
Figure 2. Conventional XML Mapping File
Analysis :
• The opening tags ( depicting the version, encoding ) of any mapping file is standard and
redundant for all xml files.
• The namespace and assembly attributes are constants for every Project file.
• The naming convention of Property is maintained uniform
( i.e. the first character of a column-name is in Upper-Case and every character
following an Underscore is in UpperCase. Such that a column, employee_name has
the property EmployeeName ).
• The Primary-key ( employee_id ) is declared in a special way.
• The Not-Nullable columns are appended with not-null=”true” attribute.
5. 5.2 Analysing similar Code Patterns in BO Class File
Similarly, the BO class file for the table employee_details would look like the figure given below :
Figure 3. Conventional BO Class File
Analysis :
• The References used are standard and Redundant.
• The Name of the Namespace is most likely constant for every Project file.
• The Properties of the class are declared in a similar format with proper Naming Conventions.
( i.e. the column employee_name becomes _employeeName as a Private variable
and EmployeeName as a Public variable ).
• The data-type of each property are reflected from the data-type of the columns in the
employee_details table.
6. 6 SQL-Server as a Code Generation Platform
All we need is a magical stored-procedure ( each for XML mapping file and BO class file ) that
would take the Table-Name and the Class-Name as its input parameters, spilling out the XML
mapping file and BO class file contents as its Output.
Eg: The mapping file and the BO class generated for employee_details table in the previous figures
were auto-generated by these stored procedures.
Well, lets explore more through the tools of SQL-Server that will make code-generaton possible.
6.1 Printing the Code :
Sql-Server uses the PRINT command functionality to display messages. We could use this
command to print our C# code.
Eg: print '<?xml version="1.0" encoding="utf-8" ?>'
print '<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"'
print 'using System;'
print 'using System.Collections.Generic;'
print 'using System.Linq;'
print 'using System.Text;'
Note : Remember the times when you run a query and SQL Server Management Studio returned
message like (8 row(s) affected) and (2 row(s) affected) etc. We need to get rid of these messages
while generating our C# code. This could be done by SET NOCOUNT ON command.
6.2 Cursor Usage :
We could use a Cursor to loop through all the columns of a given table, thereby printing xml-
mapping configuration and BO-class property description for each column by browsing through their
underlying properties which includes the data-type of the column, whether the column is nullable,
and whether the column is a Primary Key.
6.3 Finding the Primary Key of the Table :
As you might have noticed, NHibernate’s xml-mapping has a specific syntax for declaring the
Primary-key mapping. We could get hold of the Primary-key by using the below query in our
stored-procedure.
SELECT B.COLUMN_NAME FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS A,
INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE B WHERE CONSTRAINT_TYPE = 'PRIMARY KEY'
AND A.CONSTRAINT_NAME = B.CONSTRAINT_NAME and A.TABLE_NAME = ‘Table_Name_Comes_Here’
go;
7. 6.4 Translating the Name of column to a Generic Property Name :
Conventionally for a column named employee_name, Its private property should be
_employeeName and its public property should be EmployeeName.
The sql commands that you need are upper , substring and len.
Eg : Lets split the column-name ( employee_name ) by Underscores.
We now have have ‘employee’ and ‘name’ as split items.
For generating the private property, append an Underscore to the first split item
( i.e employee becomes _employee ) and for remaining split items just convert the
first character to upper-case.
Finally joining them together gives _employeeName as output.
NOTE : Keeping code-modularity in mind, one could make use of SQL-Functions that take
Column-name ( employee_name ) as its parameter and return back the
Private / Public property name.
7 Skeletal Framework of Stored Procedure
7.1 Stored Procedure for generating BO Class File :
CREATE PROCEDURE [dbo].[sp_codegen_dynamic_property_generator]
(
@table_name varchar(100),
@class_name varchar(100)
)
AS
BEGIN
print 'using System;'
print 'using System.Collections.Generic;'
print 'using System.Linq;'
print 'using System.Text;'
print 'namespace YourNameSpace'
print '{'
print ' public class ' + @class_name
print '{'
SET NOCOUNT ON
-- Get the Column-Name , Nullable-Type and Data-Type of the Table into a Temporary table
select COLUMN_NAME,IS_NULLABLE,DATA_TYPE into #temp from Information_Schema.Columns
where table_name = @table_name
-- Here we need Two Cursors, one will generate all the Private variables
-- and the other cursor would generate all the Public Variables.
-- dbo.fn_getPrivateName and dbo.fn_getPublicName are the two Functions that convert a -
-- column name to Private and Public Property names respectively.
8. -- This CURSOR Will generate all the Private Variable entities of the given table.
DECLARE @column_name_Private varchar(100), @is_nullable_Private varchar(50),
@data_type_Private varchar(50), @charindex_Private int, @note_Private int, @abc_Private
varchar(20)
DECLARE CursorManagerPrivate CURSOR
for
select column_name,is_nullable,data_type
from #temp
Open CursorManagerPrivate
Fetch Next From CursorManagerPrivate into
@column_name_Private, @is_nullable_Private, @data_type_Private
while @@fetch_status = 0
begin
if(@data_type_Private in ('nvarchar','nchar','char','varchar','sql_variant'))
begin
print 'private string +dbo.fn_getPrivateName(@column_name_Private,'_') +';'
end
else if(@data_type_Private in ('numeric'))
begin
if(@is_nullable_Private = 'NO')
begin
print 'private decimal '+ dbo.fn_getPrivateName(@column_name_Private,'_')+';'
end
else
begin
print 'private decimal? '+ dbo.fn_getPrivateName(@column_name_Private,'_')+';'
end
end
---------
--------- similar implementation for other data-types like datetime, bit, int etc.
---------
fetch next from CursorManagerPrivate into
@column_name_Private,@is_nullable_Private,@data_type_Private
end
close CursorManagerPrivate
Deallocate CursorManagerPrivate
-- This CURSOR Will generate all the Public Variable entities of the given table.
DECLARE @column_name varchar(100), @is_nullable varchar(50), @data_type varchar(50),
@charindex int, @note int, @abc varchar(20), @column_name2 varchar(100)
DECLARE CursorManagerPublic CURSOR
for
select column_name,is_nullable,data_type
from #temp
Open CursorManagerPublic
Fetch Next From CursorManagerPublic into @column_name,@is_nullable,@data_type
while @@fetch_status = 0
begin
if(@data_type in ('nvarchar','nchar','char','varchar','sql_variant'))
begin
Print 'public virtual string ' + dbo.fn_getPublicName(@column_name,'_')
print '{'
print ' get { return '+ dbo.fn_getPrivateName(@column_name,'_')+';}'
print ' set { '+ dbo.fn_getPrivateName(@column_name,'_')+'= value;}'
print '}'
end
9. else if(@data_type in ('numeric'))
begin
if(@is_nullable = 'NO')
begin
print 'public virtual decimal '+ dbo.fn_getPublicName(@column_name,'_')
print '{'
print ' get { return '+ dbo.fn_getPrivateName(@column_name,'_')+';}'
print ' set { '+ dbo.fn_getPrivateName+'= value;}'
print '}'
end
else
begin
print 'public virtual decimal? '+ dbo.fn_getPublicName(@column_name,'_')
print '{'
print ' get { return '+ dbo.fn_getPrivateName(@column_name,'_') +';}'
print ' set { '+ dbo.fn_getPrivateName(@column_name,'_') +'= value;}'
print '}'
print ''
end
end
---------
--------- similar implementation for other data-types like datetime, bit, int etc.
---------
fetch next from CursorManagerPublic into @column_name,@is_nullable,@data_type
end
close CursorManagerPublic
Deallocate CursorManagerPublic
print '}'
print '}'
SET NOCOUNT OFF
END
7.2 Stored Procedure for generating XML Mapping File :
CREATE PROCEDURE [dbo].[sp_codegen_dynamic_hbm_xml_generator]
(
@table_name varchar(100),
@class_name varchar(100)
)
AS
BEGIN
SET NOCOUNT ON
-- Get the Column-Name , Nullable-Type and Data-Type of the Table into a Temporary table
select COLUMN_NAME,IS_NULLABLE,DATA_TYPE into #temp from Information_Schema.Columns
where table_name = @table_name
-- Get the Primary-key of the Table and store it in a variable @Primary_column
SELECT @Primary_column = B.COLUMN_NAME FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS A,
INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE B
WHERE CONSTRAINT_TYPE = 'PRIMARY KEY' AND A.CONSTRAINT_NAME = B.CONSTRAINT_NAME
and A.TABLE_NAME = @table_name
-- Get the primary-key details from the Temporary table, #temp, then remove the
-- primary-key entry from #temp since we no longer will be needing it
select @COLUMN_NAME_PRIMARY = COLUMN_NAME, @IS_NULLABLE_PRIMARY = IS_NULLABLE,
@DATA_TYPE_PRIMARY=DATA_TYPE from #temp where COLUMN_NAME = @Primary_column
10. -- Get the Public property name of the primary-column by
-- using a Function and then store it in a variable @public_name
-- The primary-key mapping should be the first entry in the mapping file
print '<?xml version="1.0" encoding="utf-8" ?>'
print '<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"'
print 'namespace="YourNameSpace" assembly="YourAssemblyName">'
print '<class name="'+@class_name+'" table="'+@table_name+'">'
print '<id name="'+@public_name+'">'
print '<column name="' + @COLUMN_NAME_PRIMARY + '" sql-type="'
+ @DATA_TYPE_PRIMARY+'" not-null="true"/>'
print '<generator class="assigned"/>'
print '</id>'
-- Start a Cursor ( say CursorManager ) to iterate through the Temporary table( #temp )
-- and foreach column print its corresponding mapping
-- START OF CURSOR
print '<property name="'+@column_name2+'">'
if(@is_nullable = 'NO')
begin
print ' <column name="'+@Initial_column_name+'" not-null="true" />'
end
else
begin
print ' <column name="'+@Initial_column_name+'" />'
end
print '</property>'
fetch next from CursorManager into @column_name,@is_nullable,@data_type
end
close CursorManager
Deallocate CursorManager
-- END OF CURSOR
print '</class>'
print '</hibernate-mapping>'
SET NOCOUNT OFF
END
8 Advantages of such Algorithm
• Code uniformity and Predictable Code
• Reusable
• Development Time saver
• Less prone to manual errors
9 Conclusion
The above process of NHibernate code-generation could be taken to the next level of sophistication
by adding more validations in our stored-procedures, thereby generating Mapping Relationships (
one-to-many, many-to-may etc.) for xml-mapping file etc.
This idea of code-generation can also be implemented upon Silverlight, to generate XAMLs and
ViewModels.
Virtuously, it is highly unlikely that anyone would discover a “golden rule” that could eliminate all the
obstacles in automating a software development life cycle. However we developers could make all
efforts towards chipping away our development work from many directions.
11. Refrences
- Excerpt from Biermann, A. 1992. Automatic Programming. In Encyclopedia of Artificial
Intelligence. 2nd edition, Stuart C. Shapiro, editor, 18 - 35. New York: John Wiley &
Sons.
- NHibernate architecture:
http://nhforge.org/blogs/nhibernate/archive/2009/06/04/architecture-diagram-
rework.aspx
- Citation: Rich, C.; Waters, R.C., “Approaches to Automatic Programming”, Advances in
Computers, Vol. 37, pp. 1-57, September 1993 ()
- Excerpt from David Veeneman, NHibernate Made Simple
http://www.codeproject.com/Articles/21122/NHibernate-Made-Simple
About the Author :
Deepak Kumar Sahu is an enthusiastic DotNet Web Application Developer
from Orissa, India currently living in Pune, India and working for Capgemini.
He has a Bachelors degree on Computer Engineering from Orissa Engineering College, India.
He enjoys Coding, Music and Food.
The Author can reached at deepak.a.sahu@capgemini.com and gudguy.deepak@gmail.com