Generating code with AJGenesis using NHibernate hbm files

I was working on generating C# classes, using as starting point .hbm NHibernate mapping files. As usual, I wrote an example with AjGenesis, my open source code generation engine.

You can download a first example from my Skydrive:

Examples > AjGenesis >

(the code is in the trunk, in the current change set, under examples\NHibernateMappinp:

but if you want to go directly to the example, the Skydrive download I mentioned has all you need to run this demo, including AjGenesis trunk code compiled to binaries).

After expanding the file, you have this content:


To create C# classes, execute at command prompt:

GenerateClasses AjFirstExample
GenerateClasses AjTest

To create a .NET project with the .cs and .hbm files, run:

GenerateProject AjFirstExample
GenerateProject AjTest

The generated files are created under Build folder.

The two example projects are AjFirstExample, with two simple plain mappings, and AjTest, with a more interesing mapping, with bags and many to one relations.

Currently, each project is described by a simple Project.xml:

<Project Name="AjTest">

This is one of the mapping files in Projects\AjTest\Mappings, Department.hbm:

<?xml version="1.0" encoding="utf-8" ?> 
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
  <class name="Department" table="departments">
    <id name="Id" column="Id" type="Int32">
      <generator class="native"/>
    <property name="Description" type="String"/>
    <bag name="Employees" lazy="true" inverse="true" cascade="all">
      <key column="IdDepartment"/>
      <one-to-many class="AjTest.Entities.Employee, AjTest.Entities"/>

This is the generated code for this mapping, Department.generated.cs:

using System;
using System.Collections.Generic;
using Iesi.Collections.Generic;
namespace AjTest.Entities 
  public class Department {
    public int Id { get; set; }
    public string Description { get; set; }
    public IList<Employee> Employees { get; set; }
    public Department() 
      this.Employees = new List<Employee>();

Lets take a look to generation process. This is GenerateProject.cmd:

@echo off
set ProjectName=%1%
if "%1%"=="" set ProjectName=AjFirstExample
Bin\AjGenesis.Console.exe Projects\%ProjectName%\Project.xml Tasks\AddMappings.ajg Tasks\BuildCSharp.ajg
xcopy Libraries\*.* Build\%ProjectName%\CSharp\Src\Libraries /Y /Q

The main line is the one containing AjGenesis.Console.exe invocation. Project.xml is loaded in memory. The AddMapping.ajg task is loaded and executed, and then, BuildCSharp.ajg task is processed. AddMapping.ajg code (written in AjBasic, the dinamic language currently used by AjGenesis):

' Add mappings from directory if not specified in Project model
if not Project.Mappings then
  Project.Mappings = CreateList()
  di = new System.IO.DirectoryInfo("Projects/${Project.Name}/Mappings")
  for each fi in di.GetFiles("*.hbm.xml")
    filename = fi.Name
    Project.Mappings.Add(filename.Substring(0, filename.Length - 8))
  end for
end if

It adds the name of mapping files contained in the Mapping folder of the project. A more interesing task is GenerateCSharp.ajg. First, it loads the NHibernate library to use its hbm parser:

include "Utilities/Utilities.tpl"
include "Utilities/FileUtilities.tpl"
include "Utilities/TypeUtilities.tpl"
include "Templates/CSharp/UtilitiesCs.tpl"
include "Templates/CSharp/CSharpFunctions.tpl"
parser = new NHibernate.Cfg.MappingSchema.MappingDocumentParser()

Then, it creates the solution and project objects:

if not Project.BuildDir then
  Project.BuildDir = "Build/${Project.Name}/CSharp"
end if
message "Creating Directories..."
message "Defining Solution and Projects..."
Project.Solution = CreateObject()
Project.Solution.Guid = "FAE04EC0-301F-11D3-BF4B-00C04F79EFBC"
Project.Solution.Projects = CreateList()
message "Defining Entities Project..."
PrjEntities = CreateObject()
PrjEntities.Includes = CreateList()
PrjEntities.Guid = CreateGuid()
PrjEntities.COMGuid = CreateGuid()
Project.Entities = CreateList()

Then, it iterates on each hbm file, to get information about the entities to generate:

for each MappingName in Project.Mappings
  filename = "Projects/${Project.Name}/Mappings/${MappingName}.hbm.xml"
  mapping = parser.Parse(OpenAsStream(filename))
  for each hbmclass in mapping.Items where IsType(hbmclass, "HbmClass")
    Entity = CreateObject()
    Entity.ClassName =
    Entity.Namespace = mapping.namespace
    ' Namespace as default project name for Entities Project
    if not PrjEntities.Name then
      PrjEntities.Name = mapping.namespace
      PrjEntities.Directory = "${Project.BuildDir}/Src/${PrjEntities.Name}"
    end if
    Entity.Properties = CreateList()
    if hbmclass.Id then
      Property = CreateObject()
      Property.Name =
      Property.Type = HbmTypeToCSharp(hbmclass.Id.type1, Entity.Namespace)
    end if
    for each item in hbmclass.Items
      if IsType(item, "HbmProperty") then
        Property = CreateObject()
        Property.Name =
        Property.Type = HbmTypeToCSharp(item.type1, Entity.Namespace)
      end if
      if IsType(item, "HbmManyToOne") then
        Property = CreateObject()
        Property.Name =
        Property.Type = HbmTypeToCSharp(item.class, Entity.Namespace)
      end if
      if IsType(item, "HbmSet") then
        Property = CreateObject()
        Property.Name =
        Property.IsSet = true
        Property.Type = HbmTypeToCSharp(item.Item.class, Entity.Namespace)
      end if
      if IsType(item, "HbmBag") then
        Property = CreateObject()
        Property.Name =
        Property.IsList = true
        Property.Type = HbmTypeToCSharp(item.Item.class, Entity.Namespace)
      end if
    end for    
  end for
end for

You can extend the capabilities, processing more tags (I should write an example using Meta tags), and detecting more NHibernate mapping idioms. Now, the task generates the code:

for each Entity in Project.Entities
  TransformerManager.Transform("Templates/CSharp/Entity.tpl", "${PrjEntities.Directory}/${Entity.ClassName}.generated.cs", Environment)
end for

The tasks not only generates the .cs files, but it creates a solution and a C# project, copying and embedding the original mapping files:

for each MappingName in Project.Mappings
  filename = "Projects/${Project.Name}/Mappings/${MappingName}.hbm.xml"
  targetfilename = "${PrjEntities.Directory}/${MappingName}.hbm.xml"
  System.IO.File.Copy(filename, targetfilename, true)
end for
for each CsProject in Project.Solution.Projects where CsProject.ProjectType<>"Web"
  FileManager.CreateDirectory(CsProject.Directory & "/Properties")
  TransformerManager.Transform("Templates/CSharp/CsProject.tpl", "${CsProject.Directory}/${CsProject.Name}.csproj", Environment)
  TransformerManager.Transform("Templates/CSharp/AssemblyInfoCs.tpl", "${CsProject.Directory}/Properties/AssemblyInfo.cs", Environment)
end for
TransformerManager.Transform("Templates/Solution.tpl", "${Project.BuildDir}/Src/${Project.Name}.sln", Environment)

This is the generated solution:

Next steps

I should work in these point:

– Generate a more complete solution (with NHibernate infrastructure, Web Presentation, etc…) as in others AjGenesis examples.

– Support more NHibernate mapping options

– Use meta tags

But now, you are able to play with this example. You can change the templates to generate more artifacts, as Visual Basic .NET source files.

Thanks to @fabiomaulo for pointing me to the NHibernate hbm parser capabilities!

Angel “Java” Lopez

5 thoughts on “Generating code with AJGenesis using NHibernate hbm files

  1. Pingback: Generando código con AjGenesis usando archivos de mapeo de NHibernate - Angel "Java" Lopez

  2. Pingback: ALT.NET Hispano – Blog » Blog Archive » Generando código con AjGenesis usando archivos de mapeo de NHibernate

  3. Pingback: Models for Code Generation in AjGenesis « Angel “Java” Lopez on Blog

  4. Pingback: Generating a solution with AjGenesis using NHibernate Hbm files « Angel “Java” Lopez on Blog

  5. Pingback: Generando una solución con AjGenesis usando archivos hbm de NHibernate - Angel "Java" Lopez

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s