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 AjTestTo create a .NET project with the .cs and .hbm files, run:
GenerateProject AjFirstExample GenerateProject AjTestThe 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 /QThe 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 ifIt 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 forYou 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 forThe 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
[...] (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
[...] (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
[...] 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
[...] 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
[...] 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