Click here to Skip to main content
15,894,036 members
Articles / Programming Languages / PowerShell
Article

Pragmatic approach to generate code like DTO's, interfaces and wrappers

7 Nov 2018Ms-PL 3K   22   1  
Generate multiple code patterns (in any language, but C# is demo-ed) using XSD and several XSLTs for DTO, interface, wrappers and builder patterns

Image 1

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

  1. 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;
  2. 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;

    Image 2

  3. Example 3 : Full blown example with wrappers with interfaces on DTO and domain, databuilders and unit tests.
    1. Using wrappers with inheritance
    2. Using associations to complex types
    3. 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:

Image 3

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:

  1. xs:complexType -> generate class
  2. 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 :-)".

License

This article, along with any associated source code and files, is licensed under The Microsoft Public License (Ms-PL)


Written By
Architect Roos ICT Architectures
Netherlands Netherlands
Roland is an experienced, hands-on architect on modern (micro-)services orientation.
All modern OO-oriented platforms, C# DotNet, Java Spring Boot, Python and C++ have his interest. He is experienced in all those platforms.
Architectures, patterns, concepts and frameworks are more important than tooling and languages, after all. You should apply a tool, platform or language in a modern micro-service, because it fits best to the problem at hand. Not because you're most familiar with itSmile | :) .

Comments and Discussions

 
-- There are no messages in this forum --