Angel \”Java\” Lopez on Blog

November 22, 2009

Generating code with AJGenesis using NHibernate hbm files

Filed under: .NET, AjGenesis, C Sharp, Code Generation, NHibernate — ajlopez @ 9:50 am

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 > NHibernateMappingExample01.zip

(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">
</Project>

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"
  assembly="AjTest.Entities"
  namespace="AjTest.Entities"
  >
  <class name="Department" table="departments">
    <id name="Id" column="Id" type="Int32">
      <generator class="native"/>
    </id>
    <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"/>
    </bag>  
  </class>
</hibernate-mapping>

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
Include("Utilities/Utilities.tpl")
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("Utilities/NHibernateUtilities.tpl")
include "Templates/CSharp/UtilitiesCs.tpl"
include "Templates/CSharp/CSharpFunctions.tpl"
AssemblyManager.LoadFrom("Libraries/NHibernate.dll")
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..."
FileManager.CreateDirectory(Project.BuildDir)
FileManager.CreateDirectory("${Project.BuildDir}/Sql")
FileManager.CreateDirectory("${Project.BuildDir}/Src")
FileManager.CreateDirectory("${Project.BuildDir}/Src/Libraries")
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.Solution.Projects.Add(PrjEntities)
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()
    
    Project.Entities.Add(Entity)
  
    Entity.ClassName = hbmclass.name
    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}"
      FileManager.CreateDirectory(PrjEntities.Directory)
    end if
    
    Entity.Properties = CreateList()
    
    if hbmclass.Id then
      Property = CreateObject()
      Property.Name = hbmclass.Id.name
      Property.Type = HbmTypeToCSharp(hbmclass.Id.type1, Entity.Namespace)
      Entity.Properties.Add(Property)
    end if
    
    for each item in hbmclass.Items
      if IsType(item, "HbmProperty") then
        Property = CreateObject()
        Property.Name = item.name
        Property.Type = HbmTypeToCSharp(item.type1, Entity.Namespace)
        Entity.Properties.Add(Property)
      end if
      
      if IsType(item, "HbmManyToOne") then
        Property = CreateObject()
        Property.Name = item.name
        Property.Type = HbmTypeToCSharp(item.class, Entity.Namespace)
        Entity.Properties.Add(Property)
      end if
      if IsType(item, "HbmSet") then
        Property = CreateObject()
        Property.Name = item.name
        Property.IsSet = true
        Property.Type = HbmTypeToCSharp(item.Item.class, Entity.Namespace)
        Entity.Properties.Add(Property)
      end if
      if IsType(item, "HbmBag") then
        Property = CreateObject()
        Property.Name = item.name
        Property.IsList = true
        Property.Type = HbmTypeToCSharp(item.Item.class, Entity.Namespace)
        Entity.Properties.Add(Property)
      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)
  PrjEntities.Includes.Add(CreateFileCs("${Entity.ClassName}.generated"))
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)
  PrjEntities.Includes.Add(CreateFileType(MappingName,"hbm.xml"))
end for
for each CsProject in Project.Solution.Projects where CsProject.ProjectType<>"Web"
  FileManager.CreateDirectory(CsProject.Directory)
  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

http://www.ajlopez.com

http://twitter.com/ajlopez

5 Comments »

  1. [...] (English version of this post Generating code with AjGenesis using NHibernate hbm files) [...]

    Pingback by Generando código con AjGenesis usando archivos de mapeo de NHibernate - Angel "Java" Lopez — November 23, 2009 @ 9:21 am

  2. [...] (English version of this post Generating code with AjGenesis using NHibernate hbm files) [...]

    Pingback by ALT.NET Hispano – Blog » Blog Archive » Generando código con AjGenesis usando archivos de mapeo de NHibernate — December 12, 2009 @ 4:41 pm

  3. [...] Generating Code with AjGenesis using NHibernate Hbm Mapping Files [...]

    Pingback by Models for Code Generation in AjGenesis « Angel “Java” Lopez on Blog — February 27, 2010 @ 12:46 pm

  4. [...] Generating Code with AjGenesis Using NHibernate Hbm Files [...]

    Pingback by Generating a solution with AjGenesis using NHibernate Hbm files « Angel “Java” Lopez on Blog — July 4, 2010 @ 9:06 pm

  5. [...] Generating Code with AjGenesis Using NHibernate Hbm Files [...]

    Pingback by Generando una solución con AjGenesis usando archivos hbm de NHibernate - Angel "Java" Lopez — July 5, 2010 @ 2:10 pm


RSS feed for comments on this post. TrackBack URI

Leave a Reply

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

WordPress.com Logo

You are commenting using your WordPress.com 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

The Shocking Blue Green Theme. Create a free website or blog at WordPress.com.

Follow

Get every new post delivered to your Inbox.

Join 57 other followers

%d bloggers like this: