Introduction
Typically modern application architectures (web, SOA) use layering patterns like inheritance, Data Transfer Objects (DTO), Interfaces, Data builders and a Domain layer (e.g. Hibernate, EF). Furthermore, these layers needs to be automtically tested, with things like faking the interfaces with dummy implementations (e.g. FakeItEasy). However, coding an even small sized entity set and keep it consistent is a real lot of repetitive, dull and error-prone task. Wouldnt it be nice to generate all the plumbing? Heres a pragmatical approach that supports generating DTO's, interfaces, domains etc, and even a more advanced generated wrapper layer. It uses the xmlns:xs="http://www.w3.org/2001/XMLSchema" format as basis XML, from which with a set of XSLT 3.0 (Saxon) transfomations c# code is generated, as an example. Basically, with as much as 20-100 lines of code per pattern your done. It is kept compact, simple and clean. But as the patterns descibed above are used everywhere (Java, c++ etc), any type of code can be generated by supplying your own set of XSLT's.
Background
Of course this isnt a full blown (UML) graphical code-generation platform. But these platforms typically require steep learing curves, and code generators are complex. Another point to make here: It is not the intention to generate any business logic. The idea is to generate the data oriented classes and interfaces, and extend that with manual code.
And though the demo presented uses c# to generate c#, any other combination is possible, as long as your platform supports XML and XSLT. Now, why use XSLT 3.0, and not use XSLT 1.0? The answer is, that it can be done in XSLT 1.0 as well. I started there. However, I think the XSLT 3.0 {$..} processor makes XSLT's lots more readable. And although I like XSLT a lot, it can be tricky for others to read, and maintain yourselves after a while :-(. As far as I know Microsoft .NET does not support XSLT 3.0 yet, so I used Saxon HE version 9.8.
The patterns used
As first demonstration I want to generate the DTO layer, then the interface layer. Because I dont want the DTO to implement the interface, I decided to present a wrapper pattern that wraps the DTO in a wrapper that does implement the interface.
The idea is this:
IA interfaceA = new AWrapper(new A());
var name = interfaceA.Name;
var id = interfaceA.Id;
Instead of coding against the implementation of class A:
A instanceA = new A();
var name = instanceA.Name;
var id = instanceA.Id;
Because this is impossible, as A does not implement interface IA:
IA interfaceA = new new A();
Last but not least, a full blown small set of entities (actually, two) is used to demonstrate generating a DTO and domain layer with inheritance, interfaces and wrappers.
Three Articles
A set of three examples is provided, in three sequential articles
- Example 1 : This article. In this example we present the idea and small example code, of generating code with XSLT. class A inherits from BaseA, and IA inherits from IBaseA;
- Example 2 : Wrapper example pattern further explained and wrapper codes generated. This shows using a interface on an implementation without that interface, using complex types and associations;
- Example 3 : Full blown example with wrappers with interfaces on DTO and domain, databuilders and unit tests.
- Using wrappers with inheritance
- Using associations to complex types
- Using one interface to both DTO and domain instances by using the wrapped implementation
Using the code
As I did not want to make it complex to use (dependencies on C# or Visual Studio Versions) I decided to make the generation Powershell based.
So, the powershell supplied runs the XSLTs on the XSD and generates your code. As I wanted to supply a Unit test set with faking and shouldly, I also included a C# project.
Example 1
XSD example class A inherits from BaseA:
UML:
XSD:
<textarea rows="20" cols="100">
<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:complexType name="BaseA">
<xs:sequence>
<xs:element name="Id" type="xs:int" />
</xs:sequence>
</xs:complexType>
<xs:complexType name="A">
<xs:complexContent>
<xs:extension base="BaseA">
<xs:sequence>
<xs:element name="Name" type="xs:string"/>
</xs:sequence>
</xs:extension>
</xs:complexContent>
</xs:complexType>
</xs:schema>
</textarea>
C# DTO to be generated:
namespace dtos
{
public class BaseA
{
public int Id {get;set;}
}
public class A : BaseA
{
public string Name {get;set;}
}
}
C# interface to be generated:
namespace interfaces
{
public interface IBaseA
{
int Id {get;set;}
}
public interface IA : IBaseA
{
string Name {get;set;}
}
}
Wrapper code
This code will be generated in the second and last article. However, code will look like this:
class ABaseWrapper : IABase
{
public ABaseWrapper(ABase Instance)
{
this.Instance = Instance;
}
private ABase Instance;
public int Id
{
get { return Instance.Id; }
set { Instance.Id = value; }
}
}
class AWrapper : ABaseWrapper, IA
{
public AWrapper(A instance) : base(instance)
{
this.Instance = instance;
}
private A Instance;
public int Name
{
get { return Instance.Name; }
set { Instance.Name = value; }
}
}
Generate DTO XSD
Basically, we need to iterate the XSD:
- xs:complexType -> generate class
- xs:element -> generate property
Ad 1) Generate classes
<textarea rows="20" cols="170">
<xsl:template match="@* | node()">
<xsl:variable name="classes"><xsl:apply-templates select="//xs:complexType"/></xsl:variable>
namespace dtos
{{
{$classes}
}}
</xsl:template>
<!--$classes-->
<xsl:template match="xs:complexType">
<xsl:variable name="baseClassName"><xsl:apply-templates select=".//xs:extension"/></xsl:variable>
<xsl:variable name="propertyBlock"><xsl:apply-templates select=".//xs:element"/></xsl:variable>
<xsl:variable name="className" select="@name"/>
public class {$className} {$baseClassName}
{{
{$propertyBlock}
}}
</xsl:template>
</textarea>
Ad 2) Generate properties of class
<textarea rows="25" cols="90">
<!--$propertyBlock-->
<xsl:template match="xs:element">
<xsl:variable name="propertyType"><xsl:apply-templates select="@type"/></xsl:variable>
<xsl:variable name="propertyName" select="@name"/>
<xsl:text>public {$propertyType} {$propertyName} {{ get;set;}}</xsl:text>
</xsl:template>
<!--$propertyType-->
<xsl:template match="@type">
<xsl:choose>
<xsl:when test=".">
<xsl:choose>
<xsl:when test=". = 'xs:string'">string</xsl:when>
<xsl:when test=". = 'xs:int'">int</xsl:when>
<xsl:when test=". = 'xs:decimal'">decimal</xsl:when>
<xsl:when test=". = 'xs:long'">long</xsl:when>
<xsl:when test=". = 'xs:boolean'">bool</xsl:when>
<xsl:when test=". = 'xs:dateTime'">DateTime</xsl:when>
<xsl:otherwise>{.}</xsl:otherwise>
</xsl:choose>
</xsl:when>
</xsl:choose>
</xsl:template>
</textarea>
Generate Interfaces XSD
As it is almost a verbatim copy of the DTO layer, I leave this code in the demo zip and dont list it here. Of course, you may want to re-use parts of XSLT's in other parts. I did not do any work on that.
GenerateInterfaces.xslt
Powershell executing the XSLT
$dir = $PSScriptRoot
cd $dir
Add-Type -Path $dir\..\packages\Saxon-HE.9.8.0.14\lib\net40\saxon9he-api.dll
$XSLFileName = "GenerateDtoClasses.xslt"
$XSLFileInputPath = "$dir\$XSLFileName"
$XMLFileName = "CLassAAndBSchema.xsd"
$XMLInputFilePath = "$dir\$XMLFileName"
$OutPutFileNamePath = "DtoClasses.cs"
$XMLOutputFile = "$dir\$OutPutFileNamePath"
ApplyXslt $XSLFileInputPath $XMLInputFilePath $XMLOutputFile
Function ApplyXslt($XSLFileInput, $XMLInputFile, $XMLOutputFile)
{
$processor = New-Object Saxon.Api.Processor
$compiler = $processor.NewXsltCompiler()
#$uri = [System.Uri]$XSLFileInput
$executable = $compiler.Compile([System.Uri]$XSLFileInput)
$transformer = $executable.Load30();
$serializer = New-Object Saxon.Api.Serializer
$outStream = New-Object IO.FileStream($XMLOutputFile, [System.IO.FileMode]::Create, [IO.FileAccess]::Write)
$serializer.SetOutputStream($outStream)
$inputStream = New-Object IO.FileStream($XMLInputFile, [System.IO.FileMode]::Open, [IO.FileAccess]::Read)
$transformer.ApplyTemplates($inputStream, $serializer);
$inputStream.Close()
$outStream.Close()
}
Points of Interest
As mentioned before, running the code from C# or XSLT 1.0 will do as well. But as Powershell does the job, I used that.
Furthermore, note that the {$param}
as XSLT 3.0 feature only works in combi with expand-text="yes"
and xsl:output method="text"
<textarea rows="10" cols="50">
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
version="3.0"
expand-text="yes">
<xsl:output method="text" omit-xml-declaration="yes"/>
</textarea>
Last but not least : C# has a very powerfull (that is, this is my coloured opinion) support for code generation in the partial keyword for class and interface. So, generated code can be extended with manual code. I use that in the last article. If you plan to generate java, you may end up having to find your way around that problem.
Running Example_1 code
Run Powershell:
Example_1\GenerateDtoClasses.ps1
Example_1\GenerateInterfaces.ps1
History
This is a first draft of article 1. I split the article in three parts, so readers can digest a simple case and the wrapper pattern first, before seeing the "real monster :-)".