Click here to Skip to main content
Click here to Skip to main content

Template-Based Code Generation with SmartCode

, 25 Dec 2006
Rate this:
Please Sign up or sign in to vote.
SmartCode is a template based code generator.This tutorial describes the process of building a templates to SmartCode

SmartCode IDE

Overview

As described in part one of this series, SmartCode uses a template engine to transform some kind of "model" into compilable code. In this second part of the series, I will show how to work SmartCode and teach how to build a template to generate stored procedures.

Introduction

Most of the real-world code generators, both commercial and open source releases, use script based templates and template engines to transform data from a format to another. In the .NET Framework, usually templates are written in ASP.NET style <% %> syntax. And using .NET class, associated to an specific data model to transform this input, in an formatted output, specified by the template

SmartCode is a very flexible, powerful and open source code generator. The most new features of SmartCode is that does not use ASP.NET style.
The Templates are created in C# or VB.NET (or theoretically in any .NET language in which a dynamic-link library (DLL) may be created) and compiled as dlls. This is a very powerful paradigm, to allow you using Visual Studio to create, compile and debug the templates.

Template Libraries

Code-generation templates are organized in dlls. For a DLL to be considered an SmartCode library, it has to expose a certain interface, which will be specified in detail in the next section. To facilitate the creation of this interface, the SmartCode source package comes with base classes that already declare the necessary properties and methods, and which may be used to create libraries and templates by the use of class inheritance.

SmartCode in Action

In SmartCode Each Template look like this (in VB.NET):

    Imports System
    Imports SmartCode.Template

    Public Class Sample3
        Inherits TemplateBase

        Public Sub New()
            MyBase.Name = "Sample3"
            MyBase.CreateOutputFile = True
            MyBase.Description = "This template demonstrates " & _ 
                                 "using static and dynamic contents."
            MyBase.OutputFolder = "Samples\Sample3"
            MyBase.IsProjectTemplate = False
        End Sub

        Public Overloads Overrides Function OutputFileName() As String
            Return Table.Code + "_Sample3.txt"
        End Function

        Public Overloads Overrides Sub ProduceCode()
            WriteLine("Table: " + Table.Name)
            For Each column As SmartCode.Model.Column In Table.AllColumns
                WriteLine("Column: {0}: {1}", column.Name, column.NetDataType)
            Next
        End Sub
    End Class

The code listed is a sample template that will write out all of the column names from any table.
I we apply this template, to the SQL Server Northwind Employees table, the resulting output would be.

  
    Table:          : Employees
    EmployeeID      : System.Int32      
    LastName        : System.String   
    FirstName       : System.String    
    Title           : System.String    
    TitleOfCourtesy : System.String    
    BirthDate       : System.DateTime 
    HireDate        : System.DateTime 
    Address         : System.String    
    City            : System.String    
    Region          : System.String    
    PostalCode      : System.String    
    Country         : System.String    
    HomePhone       : System.String    
    Extension       : System.String    
    Photo           : System.Byte[]   
    Notes           : System.String    
    ReportsTo       : System.Int32      
    PhotoPath       : System.String    
     

Getting Started

As described SmartCode uses a template engine to transform some kind of model, the SmartCode Model, into output code, given the output specified by a template

Internal Core Model

The internal core model is the core of SmartCode generator architecture. This contains a set of classes that make it easily manipulate the public methods and properties, to generate outputs.
The next figure show the basic Internal Core Model

SmartCode Classes

In order to add the SmartCode engine in the game, you class must inherits from a class TemplateBase, the engine, using reflection features, calls the Run Method defined by:

System.Collections.ArrayList Run(SmartCode.Model.Domain domain, 
                                 SmartCode.Model.NamedObject entity)

Passing the context domain and entity objects, defined by each selected object in the Setting Code Generation Dialog.

SmartCode
Where "Delete Row by primary Key", "Insert One Row" and the other one are Templates and the objects below each node are the Entities assigned.

The Run method return to Engine an ArrayList of values between them, the Output Code and Filename, to create the output file contents.

Now that it's clear how that SmartCode works, we can concentrate on the implementation of the TemplateBase abstract class

Templates interface

A DLL file is considered an SmartCode Template library, whenever it directly or not inherits from a class SmartCode.Template.TemplateBase, and has implemented the following properties:

  • Name: The name of the template. For example: "Sample3". MyBase.Name = "Sample3" (VB.NET)
  • Description: The description of what the template does. For example: "This template demonstrates using static and dynamic contents".

    MyBase.Description = "This template demonstrates using" & _ 
                         " static and dynamic contents."
  • OutputFolder: The relative path of the folder in which to place the file with the generated code. It is relative because, as explained in the section on code-generation results, before generating code SmartCode Studio asks in which folder to place the generated files. For example: "Samples\Sample3". MyBase.OutputFolder = "Samples\Sample3"
  • CreateOutputFile: Whether a file is to be created as part of the code-generation process. Although it is typical for a template to generate code to be placed in a single file, there may be certain templates that need to generate multiple files.

    MyBase.CreateOutputFile = True
    IsProjectTemplate: If template is created for run for each entity or only for project.
    MyBase.IsProjectTemplate = False 'Run once, only by Project

TemplateBase is an abstract class you must Implement the next Methods

  • OutputFileName: The actual name of the file in which to store the generated code. This property would only be available after the ProduceOutString method(see below) is called. For example: "Client_Sample3.sql".

        Public Overloads Overrides Function OutputFileName() As String
            Return Table.Code + "_Sample3.txt"
        End Function
  • ProduceCode: The routine that generates the code and places it in the internal StringBuilder property "code". To generate the code, this method uses the internal properties "Entity" and "Domain", which SmartCode will set prior to invoking it. Entity and Domain are properties of types Table and Domain, respectively.

        Public Overloads Overrides Sub ProduceCode()
           WriteLine("Table: " + Table.Name)
           For Each column As SmartCode.Model.Column In Table.AllColumns
                WriteLine("Column: {0}: {1}", column.Name, column.NetDataType)
           Next
        End Sub
                

When you are done with the code sign the assembly, and add to the Global Assembly Cache to be available to SmartCode Studio.

Writing Our Template

Now that it's clear how that Templates libraries structure, we can begin writing our template. A good idea to start with our template is build how the output we like. Here is an example GetRowByPrimaryKey stored procedure for the Products table from Northwind database.

All code is available in source.zip package of SmartCode

CREATE PROCEDURE usp_Products_GetRowByPrimaryKey
(
     @ProductID int 
)
AS
SELECT 
    [Products].[ProductID] as ProductID,
    [Products].[ProductName] as ProductName,
    [Products].[SupplierID] as SupplierID,
    [Products].[CategoryID] as CategoryID,
    [Products].[QuantityPerUnit] as QuantityPerUnit,
    [Products].[UnitPrice] as UnitPrice,
    [Products].[UnitsInStock] as UnitsInStock,
    [Products].[UnitsOnOrder] as UnitsOnOrder,
    [Products].[ReorderLevel] as ReorderLevel,
    [Products].[Discontinued] as Discontinued,
    [T0].[CategoryName] as CategoryID_CategoryName, 
    [T0].[Description] as CategoryID_Description,
    [T1].[CompanyName] as SupplierID_CompanyName, 
    [T1].[ContactName] as SupplierID_ContactName  
    FROM [Products]      LEFT OUTER JOIN 
    Categories AS T0  ON  T0.[CategoryID] = 
               [Products].[CategoryID]      LEFT OUTER JOIN 
    Suppliers AS T1  ON  T1.[SupplierID] = [Products].[SupplierID] 
    WHERE 
    (
          [ProductID] = @ProductID 
    )        
    

To Build the list of primary key parameters, we can get these values from Table.PrimaryKeyColumns() (IList<column>) Method, this is an List of Column that represent each Primary Key in the current context object. Here is an simple example of code to extract the information

    string inputParameters = String.Empty;
    foreach (Column column in Table.PrimaryKeyColumns())
    {
        inputParameters += "    " + Common.GetSqlParameterLine(column);
    }
    inputParameters = Common.Substring(inputParameters, 
                      Environment.NewLine.Length + 1);
    WriteLine(" ({0} {1} {0})", Environment.NewLine, inputParameters);

Where GetSqlParameterLine is an rutine defined in Common class that returns the type of the field as a T-SQL type, like varchar(length-of-field).

There is a method worth noting, called WriteLine, in the class TemplateBase. In the core libraries, it is used to write the generated code into the StringBuilder code property, line by line. It appends a new-line character to the string it receives as a parameter and then adds it to the value of the code property. There is also a parameter-less WriteLine, which just adds a new-line character to the value of the code property.

Now we need build the body of the Store procedure, we can split up into 4 parts:

  • The local columns defined in products table
    foreach (Column columm in Table.AllColumns())
    {
        selectStm.AppendFormat("    [{0}].[{1}] as {2},{3}", Table.Name, 
                  columm.Name, columm.Code, Environment.NewLine);
    }
    
  • The list of values (LOV) an very nice feature added to SmartCode Model, that allow you lookup for values in parent tables, as shows the next image:

    SmartCode LOV

        int i = 0;
        foreach (Reference reference in Table.InReferences){
            foreach (ReferenceJoin join in reference.Joins){
                //Look for LOV
                foreach (Column lovColumn in join.LOV){
                    if (lovColumn.Name != join.ParentColumn.Name){
                        selectStm.AppendFormat("    [T{0}].[{1}] as {2}_{3},{4}", 
                           i, lovColumn.Name, join.ChildColumn.Code, 
                           lovColumn.Code, Environment.NewLine);
                    }
                }
            }
            i += 1;
        }
        
        //Remove the '\n,'
        selectStm = new StringBuilder(Common.Substring(selectStm.ToString(), 
                                      Environment.NewLine.Length + 1));
        //Write the code
        WriteLine(selectStm.ToString());
        
  • The From Part, you can access to external 'InReferences' to build a more complex FROM.
        i = 0;
        string alignment = "";
    
        foreach (Reference reference in Table.InReferences)
        {
            if  (reference.Alignment == Reference.AlignmentType.Inner)
                 alignment = " INNER JOIN ";
            else if (reference.Alignment == Reference.AlignmentType.Left) 
                 alignment = " LEFT OUTER JOIN ";
            else if (reference.Alignment == Reference.AlignmentType.Right) 
                 alignment = " RIGHT OUTER JOIN ";
    
            selectStm.AppendFormat("    {0} {1} AS T{2} ",alignment + 
                      Environment.NewLine , reference.ParentTable.Name, i);  
    
            string onJoin = "";
            foreach (ReferenceJoin join in reference.Joins)
            {
                onJoin += String.Format(" ON  T{0}.[{1}] = [{2}].[{3}] AND", i, 
                          join.ParentColumn.Name, Table.Name, 
                          join.ChildColumn.Name);
                onJoin += Environment.NewLine;
            }
            //Remove the last AND
            onJoin = Common.Substring(onJoin, Environment.NewLine.Length + 3);
            selectStm.Append(onJoin);
    
            i += 1;
        }
        
  • And the Where Part
                
        string keyCondition = String.Empty;
        foreach (Column column in Table.PrimaryKeyColumns())
        {
            keyCondition += String.Format(" [{0}] = @{1} AND {2}", 
                            column.Name, column.Code, Environment.NewLine);
        }
        keyCondition = Common.Substring(keyCondition, 
                      (Environment.NewLine.Length + 5));
    
        WriteLine("    WHERE {0}({0} {1} {0})", Environment.NewLine, 
                  keyCondition);         
    
    
    
  • Project Level Templates

    In the last sections we have learn to generate code running templates assigned to entities, But else in the practices usually we have to generate code for all objects contained in the domain e.g. the list of all tables in the database; a simple way to make this easier is create a new the template with next code:

     
        foreach (Table table in Domain.ConnectionInfo.Tables)
        {
            WriteLine(table.Name);
        }
       
    And assign the template to any, and only one, entity and run it.

    With SmartCode we have the option to assign a template to project level, to run only one time, each time that we run the code generation, we can found this template in the second tab called "Project Templates", in the main "Setting Code Generation" dialog, as shows the next figure:

    SmartCode Project Level templates

    To be available the template in this tab we need to assign true to the property IsProjectTemplate. The next code show how to build a project level template, notice that AllStoreProcedures call other templates to build the code to create only one file with the code for DeleteRowByPrimaryKey, Insert and Update for each table of the Domain.

     
        public class AllStoreProcedures : TemplateBase
        {
            // Constructor.
            public AllStoreProcedures()
            {
                this.IsProjectTemplate = true;
                this.createFile = true;
                this.description = "Generates all stored procedure for all entities in 
                                    the domain";
                this.name = "AllStoreProcedures";
                this.outputFolder = @"Stored Procedures\AllStoreProcedures";
            }
    
            public override string OutputFileName()
            {
                return Domain.Name + "_AllSps.sql";
            }
    
            public override void ProduceCode()
            {
    
                foreach (Table table in Domain.ConnectionInfo.Tables)
                {
                    if (table.IsTable && table.PrimaryKeyColumns().Count > 0)
                    {
                        RunTemplate(new DeleteRowByPrimaryKey(), table);
                        RunTemplate(new Insert(), table);
                        RunTemplate(new Update(), table);
                    }
                }
            }
    
            private void RunTemplate(TemplateBase template, Table table)
            {
                System.Collections.ArrayList results = template.Run(Domain, table);
    
                //The code prop, containts the output string. append the results, 
                //look for Run 
                base.code.Append(results[0]);
            }
        }
       

    The Core Template Libraries

    The SmartCode package comes with a Beta core sets of template libraries for generating code in C#, that use stored procedures to access and modify data.

    • Stored Procedures Libraries: These templates generate SQL Server 2000 scripts for creating stored procedures that insert, delete, update, and search for records in tables.
    • Common Data Libraries (TypedDataset): This template generate code for XSD to Render to TypedDataSet objects used to transport data between the tiers
    • Data Tier Libraries: These templates generate code for supporting the data access classes
    • Business Tier Libraries: These templates generate code for supporting the access to Data Tier

    Last changes

    Bug fix in DataAccess layer of Core Templates Library

    Conclusion

    In this article we have view the huge benefits of using templates to avoid repetitive coding tasks. SmartCode Model provide the Object Model, SmartCode Studio provide the IDE to customize your Model and you.... provide the Imagination, Creativity and Inspiration.

    Maybe there are several spelling mistakes, English is hard for me, I hope in future revision catch the mistakes.

    License

    This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

    A list of licenses authors might use can be found here

    Share

    About the Author

    Danilo Mendez
    Web Developer
    United States United States
    Danilo is the creator of SmartRules, a Business Rules Engine. He is an industry consultant working primarily with companies interested in implementing dynamic rules programming concepts to add flexibility to their architectures on web, CE, and desktop platforms. He operates his own website, Kontac, where you will find more information.
     
    To contact Danilo, email him at danilo.mendez@gmail.com.

    Comments and Discussions

     
    GeneralSQL Server 2005 Table Schema Pinmemberjmontesc3-Nov-08 11:27 
    GeneralVery interesting. Idea for extending the concept Pinmemberzulu26-Apr-08 10:27 
    Generalwow wow Pinmembercommandant_.5-Jun-07 12:41 
    QuestionWhy not asp style template Pinmemberw3Nima9-Feb-07 6:30 
    AnswerRe: Why not asp style template [modified] PinmemberDanilo Mendez9-Feb-07 7:21 
    GeneralRe: Why not asp style template Pinmemberw3Nima10-Feb-07 7:38 
    QuestionWhat happened to the download PinmemberRaibeart2-Jan-07 5:15 
    AnswerRe: What happened to the download PinmemberDanilo Mendez2-Jan-07 5:19 
    GeneralRe: What happened to the download PineditorPaul Conrad3-Jan-07 18:20 
    GeneralWOW Pinmembergggggg25-Dec-06 1:28 
    QuestionWhat changed? PinmemberKen Blood1-Dec-06 10:34 
    AnswerRe: What changed? PinmemberDanilo Mendez1-Dec-06 12:00 
    QuestionRe: What changed? Pinmemberno cars go11-Mar-07 0:34 
    QuestionSmartCode as sourceforge project? PinmemberArgiris Kirtzidis25-Nov-06 13:38 
    AnswerRe: SmartCode as sourceforge project? [modified] PinmemberDanilo Mendez25-Nov-06 18:29 
    GeneralOh, Just What I Need! Pinmembernikytta24-Nov-06 9:13 
    GeneralRe: Oh, Just What I Need! PinmemberRory Becker24-Nov-06 10:19 
    GeneralRe: Oh, Just What I Need! Pinmembernikytta24-Nov-06 10:28 
    GeneralRe: Oh, Just What I Need! PinmemberRory Becker24-Nov-06 10:36 
    GeneralRe: Oh, Just What I Need! PinmemberRichard Parsons17-Aug-07 8:40 
    GeneralMyGeneration PinmemberCamBendy3-Dec-06 19:42 

    General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

    Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

    | Advertise | Privacy | Mobile
    Web04 | 2.8.141029.1 | Last Updated 25 Dec 2006
    Article Copyright 2006 by Danilo Mendez
    Everything else Copyright © CodeProject, 1999-2014
    Terms of Service
    Layout: fixed | fluid