CodeSmith Quick Start Guide

CodeSmith Quick Start Guide

 

The goal of this document is to get you up to speed as quickly as possible on how you can use CodeSmith to generate anything from a single collection to an entire tier of your application. In this guide I am going to walk you through using the templates that are included with CodeSmith, how to create a simple CodeSmith template, and then how to create a database driven template.

 

CodeSmith is a full featured template-based code generation tool, using an ASP.NET like syntax you can build and define a template which can be used to generate code. Something important to understand about CodeSmith is that what you are really generating is text, so technically you could use CodeSmith to generate any sort of language or text it does not necessarily even have to be code.

 

Before looking at how to create your own templates, let’s walk through using the templates that are included with CodeSmith. To start open up the CodeSmith Explorer by going to Start à Program Files à CodeSmith à CodeSmith Explorer, this is shown in the screenshot below.

 

 

The CodeSmith Explorer is used to display the templates that you currently have available to you; the sample templates are loaded by default. Some of the most useful sample templates are under the Collections header, double click the ArrayList.cst template to open it, this is shown in the screenshot below.

 

 

This is the template property screen, where you can view and specify the various properties for the template. For the ArrayList template we need to specify the name of the ArrayList we want to create and the item type that we want to store in the ArrayList.  (We can also optionally specify any namespaces that we need included in the ArrayList) In this example we specify CarList as the ClassName and Car as the ItemType. Once the properties are filled out simply click Generate and the template will generate a strongly typed ArrayList for you, this is shown in the screenshot below.

 

 

The window on the right shows the output of the template which is a strongly typed ArrayList for the Car class.  You can then either copy & paste this code into Visual Studio, or whatever editor you use, and compile it.

 

This example shows how to use existing templates, and for some people that is all they will use CodeSmith for, but the majority of people will want to write their own templates and that is what this next section is about.

 

Writing your first template

 

The real value of CodeSmith when compared to other code generation tools is that with CodeSmith you can write your own code generation templates that specify what you want to generate, how it should be generated, and even the number of tabs or spaces in the code. This gives you ultimate control over the output of your templates, something that is impossible with most code generation tools.

 

During the development of CodeSmith one of the main goals was to create a template syntax that mimics ASP.NET is almost everyway, if you are familiar with ASP.NET then you will find a huge number of similarities between the two. This is important to know because if you are trying to figure out how to accomplish a task in a template the majority of times you can do exactly what you would do in ASP.NET. The main differences between ASP.NET and CodeSmith are that CodeSmith does not support data-binding or server controls.

 

You have a couple of different options on what tool you want to use to write templates, if you are using the professional version of CodeSmith you can use CodeSmith Studio which offers a rich environment for authoring CodeSmith templates. If you are not using the professional version or your trial period has expired then you can use any text editor to create your templates.

 

The example template that we are going to build is very simple; it will accept a couple different strings and then generate a class including a special comment header.  This is a very simply example, but it will illustrate the basics of building CodeSmith templates. I have always found that the best way to design a template is to first create the desired output of the template; following is the output for this template.

 

///

// file: MyClass.cs

// Description: Enter summary here after generation.

// ---------------------

// Copyright © 2003 Our Client

// ---------------------

// History

//    11/30/2003    Developer's Name    Original Version

///

 

using System;

 

namespace MyNamespace

{

      /// <summary>

      /// Summary description for MyClass.

      /// </summary>

      public class MyClass

      {

            public MyClass()

            {

                  //

                  // TODO: Add constructor logic here

                  //

            }

      }

}

 

This is what we want the end result of our template to be, but with parts of this output specified each time the template is run. So now that we know what we want to generate let’s start building the template that will generate this output.

 

The first step to building a template is opening a new blank text file and then adding a CodeTemplate directive to the top of the file. The CodeTemplate directive tells CodeSmith that this file is a template, specifies the language that the template is written in, the language that the template is generating, and a template description. Here is the CodeTemplate directive for this template:

 

<%@ CodeTemplate Language="C#" TargetLanguage="C#"

      Description="Generates a class including a special informational header" %>

 

First we set the language of the template to C#, then we set the target language of the template to C#, and finally we create a description for this template. There are additional properties to the CodeTemplate directive which are covered elsewhere in the documentation.

 

The next step is to declare the various properties that we will specify each time that the template is run. For this template we want to specify the name of the namespace, class, as well as the developer’s name. Following are the property declarations for these three properties:

 

<%@ Property Name="NameSpace" Type="String"

      Category="Context"

      Description="The namespace to use for this class" %>

 

<%@ Property Name="ClassName" Type="String"

      Category="Context"

      Description="The name of the class to generate" %>

 

<%@ Property Name="DevelopersName" Type="String"

      Category="Context"

      Description="The name to include in the comment header" %>

 

Each of the property directives includes the name of the property, which is then used to reference the value of this property throughout the rest of the template as well as the type of the property. We have also set a category and description for each property; these will be used when the template is opened in CodeSmith to identify the properties.

 

The next step is to modify the text of the template so that when these properties are specified the text will be inserted in the correct area. To do this we use an ASP.NET like syntax and the name of the property, as you can see here:

 

///

// file: <%=ClassName%>.cs

 

This value will be inserted into the text, as you can see the syntax is identical to ASP.NET. We then need to go through the rest of our desired output and replace the hard coded values with the name of the property. Following is this example template in its entirety.

 

<%@ CodeTemplate Language="C#" TargetLanguage="C#"

      Description="Generates a class including a special informational header" %>

 

<%@ Property Name="NameSpace" Type="String"

      Category="Context"

      Description="The namespace to use for this class" %>

 

<%@ Property Name="ClassName" Type="String"

      Category="Context"

      Description="The name of the class to generate" %>

 

<%@ Property Name="DevelopersName" Type="String"

      Category="Context"

      Description="The name to include in the comment header" %>

     

///

// file: <%=ClassName%>.cs

// Description: Enter summary here after generation.

// ---------------------

// Copyright © <%= DateTime.Now.Year %> Our Client

// ---------------------

// History

//    <%= DateTime.Now.ToShortDateString() %>    <%= DevelopersName%>    Original Version

///

 

using System;

 

namespace <%=NameSpace %>

{

      /// <summary>

      /// Summary description for <%=ClassName %>.

      /// </summary>

      public class <%=ClassName %>

      {

            public <%=ClassName %>()

            {

                  //

                  // TODO: Add constructor logic here

                  //

            }

      }

}

 

As you can see we have referenced the properties in a number of different places, we have also used the DateTime object to populate a couple of the dates for us. Now we need to load this template into the CodeSmith explorer, to do this you simply need to open up the CodeSmith explorer, click the open folder icon and select the directory where your template is located. You will then see a list of any templates in that directory and can double click on the template to load it. When this template is loaded we will see the properties declared as shown here:

 

 

As you can see the three properties we declared are loaded, including the category and description. CodeSmith uses the specified properties and our template code to generate the following content when the Generate button is pressed:

 

///

// file: MyClass.cs

// Description: Enter summary here after generation.

// ---------------------

// Copyright © 2003 Our Client

// ---------------------

// History

//    12/2/2003    Mr. Smith    Original Version

///

 

using System;

 

namespace MyNameSpace

{

      /// <summary>

      /// Summary description for MyClass.

      /// </summary>

      public class MyClass

      {

            public MyClass()

            {

                  //

                  // TODO: Add constructor logic here

                  //

            }

      }

}

           

The generated output can then be copied and pasted into Visual Studio, or whatever compiler you happen to use, and compile it into your project. This is an extremely simple example of what CodeSmith can do, but it illustrates some very important points:

 

  • A template requires a CodeTemplate declaration.

  • A template can have any number of properties and these properties are declared using property declarations.

  • CodeSmith uses ASP.NET like syntax to insert property values into the output of the template.

  • Templates are 100% custom, you can generate any sort of text using CodeSmith.

 

 

This example fails to really illustrate the full power of CodeSmith though, continue on to the next section to see how you can dynamically build code based on your database objects.

 

Writing a Database Driven Template

Now that we have taken care of the basics of working with CodeSmith we can move along to learning how to build what you probably downloaded CodeSmith for in the first place. Database Access Logic is probably the most redundant section of any application, and using CodeSmith you can automate a decent portion of your data access layer. In the last section you saw how to create a simple template built off of supplied parameters, but in this section we are going to look at how to build templates using a component of CodeSmith called the SchemaExplorer. The SchemaExplorer is an assembly that provides a number of classes that can be used to browse your database schema. Using the SchemaExplorer you can browse the tables and stored procedures of your database and retrieve the data types, identity columns, column names, and much more.

 

As an example of how to build a template using the SchemaExplorer we are going to build a template that will automatically generate a stored procedure based on the columns of a table. Before we start to build the template let’s create what the desired output of the template will be, this way we can use this output as the beginning of our template. Here is the desired output of this template:

 

-----------------------------------------------------------------

-- Date Created: Thursday, December 04, 2003

-- Created By:   Generated by CodeSmith

-----------------------------------------------------------------

 

CREATE PROCEDURE dbo.UpdateOrders

      @OrderID int,

      @CustomerID nchar(5),

      @EmployeeID int,

      @OrderDate datetime,

      @RequiredDate datetime,

      @ShippedDate datetime,

      @ShipVia int,

      @Freight money,

      @ShipName nvarchar(40),

      @ShipAddress nvarchar(60),

      @ShipCity nvarchar(15),

      @ShipRegion nvarchar(15),

      @ShipPostalCode nvarchar(10),

      @ShipCountry nvarchar(15)

AS

 

UPDATE [Orders] SET

      [CustomerID] = @CustomerID,

      = @EmployeeID,

      [OrderDate] = @OrderDate,

      [RequiredDate] = @RequiredDate,

      [ShippedDate] = @ShippedDate,

      [ShipVia] = @ShipVia,

      [Freight] = @Freight,

      [ShipName] = @ShipName,

      [ShipAddress] = @ShipAddress,

      [ShipCity] = @ShipCity,

      [ShipRegion] = @ShipRegion,

      [ShipPostalCode] = @ShipPostalCode,

      [ShipCountry] = @ShipCountry

WHERE

     

      [OrderID] = @OrderID

 

This is a stored procedure that will update the orders table in the Northwind database, since this procedure is fairly generic it is a perfect target for code generation. The goal of this template is to read the schema of the table and then automatically generate this stored procedure based off that schema, of course the template needs to work on any table that we point it at, not just this one. 

 

The first step is to create our template and add the CodeTemplate directive with the name and description of our template:

 

<%@ CodeTemplate Language="C#" TargetLanguage="T-SQL"

      Description="Generates a update stored procedure." %>

 

Then we need to load the assembly that contains the SchemaExplorer, a class that makes it easy to examine our database, using the Assembly tag.

 

<%@ Assembly Name="SchemaExplorer" %>

 

This will load the SchemaExplorer assembly and make its classes available to our template, we also need to import the SchemaExplorer namespace using the import tag.

 

<%@ Import Namespace="SchemaExplorer" %>

 

With the assembly and import tags complete we now need to add a property to our template, since we are going to be reading from a table we are going to create a property of the TableSchema type:

 

<%@ Property Name="SourceTable" Type="SchemaExplorer.TableSchema"

      Category="Context"

      Description="Table that the stored procedures should be based on." %>

 

This property will allow us to select a database and table when executing this template, we can reference this property to examine the table and build our template based on the contents and properties of the table.

 

The next step in building our template is to start writing the section of our template that will actually output text. The first section is the header of the file which can be seen below.

 

-----------------------------------------------------------------

-- Date Created: <%= DateTime.Now.ToLongDateString() %>

-- Created By:   Generated by CodeSmith

-----------------------------------------------------------------

 

This is similar to the code in the last example, we are using the DateTime object to generate the date created for our script.  Next we need to create the first line of procedure script:

 

CREATE PROCEDURE dbo.Update<%= SourceTable.Name %>

 

On this line we use the Name property of the SourceTable parameter that we defined earlier, this will insert the name of the table we are examining, so in this case since we are pointing to the Orders table the name of our stored procedure will be UpdateOrders.

 

The next step is to create the parameter list for the stored procedure, because we are building an update procedure we need a parameter for each of the columns in the table. Using the TableSchema object we can loop through the columns in the table and read the name of the column as well as the datatype of the column, using these values to create our list.

 

      <% for (int i = 0; i < SourceTable.Columns.Count; i++) { %>

      <%= GetSqlParameterStatement(SourceTable.Columns[i]) %><% if (i < SourceTable.Columns.Count - 1) { %>,<% } %>

      <% } %>

AS

 

In the first line we start our loop using the Columns.Count property to determine how many columns are in the table. In the second line we call a method called GetSqlParameterStatement passing the current column to this method then we add a comma if this is not the last column in the table.

 

We also need to write the GetSqlParameterStatement method, which should accept a Column object and then return a string containing the correct parameter name and data type as we want it to appear in our output. To include code directly in the template we use script tags, just like you would if you were including code in an ASP.NET page, the only different is that when writing code inside a template you need to add runat=”template” to the script tag. Following is the implementation of our method:

 

<script runat="template">

public string GetSqlParameterStatement(ColumnSchema column)

{

      string param = "@" + column.Name + " " + column.NativeType;

 

      switch (column.DataType)

      {

            case DbType.Decimal:

            {

                  param += "(" + column.Precision + ", " + column.Scale + ")";

                  break;

            }

            default:

            {

                  if (column.Size > 0)

                  {

                        param += "(" + column.Size + ")";

                  }

                  break;

            }

      }

 

      return param;

}

</script>

 

This methods accepts the ColumnSchema object that we pass from our for loop then builds the parameter name by concatenating an @ symbol, the name of the column, and the data type. The switch statement is used to add some type specific text to the end of the parameter name, for instance on a decimal type we need to add the precision and scale. Here is what the output of this section will be when executed by CodeSmith:

 

-----------------------------------------------------------------

-- Date Created: Saturday, December 06, 2003

-- Created By:   Generated by CodeSmith

-----------------------------------------------------------------

 

CREATE PROCEDURE dbo.UpdateOrders

      @OrderID int,

      @CustomerID nchar(5),

      @EmployeeID int,

      @OrderDate datetime,

      @RequiredDate datetime,

      @ShippedDate datetime,

      @ShipVia int,

      @Freight money,

      @ShipName nvarchar(40),

      @ShipAddress nvarchar(60),

      @ShipCity nvarchar(15),

      @ShipRegion nvarchar(15),

      @ShipPostalCode nvarchar(10),

      @ShipCountry nvarchar(15)

AS

 

Next we need to create the actual SQL statement where we will update our table with the supplied parameters. To do this we will again loop through the columns collection, but instead of looping through the normal columns collection we are going to loop through the NonPrimaryKeyColumns collection. We do this because we only want to update non primary key columns, we will be using the primary key columns in the where clause of update statement.

 

UPDATE [<%= SourceTable.Name %>] SET

      <% for (int i = 0; i < SourceTable.NonPrimaryKeyColumns.Count; i++) { %>

      [<%= SourceTable.NonPrimaryKeyColumns[i].Name %>] = @<%= SourceTable.NonPrimaryKeyColumns[i].Name %><% if (i < SourceTable.NonPrimaryKeyColumns.Count - 1) { %>,<% } %>

      <% } %>

 

In this for statement we build our SET list, including the column name and the parameter name, here is the output from this section when executed by CodeSmith.

 

UPDATE [Orders] SET

      [CustomerID] = @CustomerID,

      = @EmployeeID,

      [OrderDate] = @OrderDate,

      [RequiredDate] = @RequiredDate,

      [ShippedDate] = @ShippedDate,

      [ShipVia] = @ShipVia,

      [Freight] = @Freight,

      [ShipName] = @ShipName,

      [ShipAddress] = @ShipAddress,

      [ShipCity] = @ShipCity,

      [ShipRegion] = @ShipRegion,

      [ShipPostalCode] = @ShipPostalCode,

      [ShipCountry] = @ShipCountry

 

The last part of our template that we need to write is the where clause for the update statement. Again we are going to loop through a collection of columns, but in this case we are going to loop through the primary key columns for this table and generate a where clause.

 

WHERE

      <% for (int i = 0; i < SourceTable.PrimaryKey.MemberColumns.Count; i++) { %>

      <% if (i > 0) { %>AND <% } %>

      [<%= SourceTable.PrimaryKey.MemberColumns[i].Name %>] = @<%= SourceTable.PrimaryKey.MemberColumns[i].Name %>

      <% } %>

 

Here is the output of this section when run with CodeSmith.

 

WHERE

     

      [OrderID] = @OrderID

 

Here is a look at the complete template.

 

<%@ CodeTemplate Language="C#" TargetLanguage="T-SQL"

      Description="Generates a update stored procedure." %>

 

<%@ Property Name="SourceTable" Type="SchemaExplorer.TableSchema"

      Category="Context"

      Description="Table that the stored procedures should be based on." %>

 

<%@ Assembly Name="SchemaExplorer" %>

 

<%@ Import Namespace="SchemaExplorer" %>

     

<script runat="template">

public string GetSqlParameterStatement(ColumnSchema column)

{

      string param = "@" + column.Name + " " + column.NativeType;

 

      switch (column.DataType)

      {

            case DbType.Decimal:

            {

                  param += "(" + column.Precision + ", " + column.Scale + ")";

                  break;

            }

            default:

            {

                  if (column.Size > 0)

                  {

                        param += "(" + column.Size + ")";

                  }

                  break;

            }

      }

 

      return param;

}

</script>

 

-----------------------------------------------------------------

-- Date Created: <%= DateTime.Now.ToLongDateString() %>

-- Created By:   Generated by CodeSmith

-----------------------------------------------------------------

 

CREATE PROCEDURE dbo.Update<%= SourceTable.Name %>

      <% for (int i = 0; i < SourceTable.Columns.Count; i++) { %>

      <%= GetSqlParameterStatement(SourceTable.Columns[i]) %><% if (i < SourceTable.Columns.Count - 1) { %>,<% } %>

      <% } %>

AS

 

UPDATE [<%= SourceTable.Name %>] SET

      <% for (int i = 0; i < SourceTable.NonPrimaryKeyColumns.Count; i++) { %>

      [<%= SourceTable.NonPrimaryKeyColumns[i].Name %>] = @<%= SourceTable.NonPrimaryKeyColumns[i].Name %><% if (i < SourceTable.NonPrimaryKeyColumns.Count - 1) { %>,<% } %>

      <% } %>

WHERE

      <% for (int i = 0; i < SourceTable.PrimaryKey.MemberColumns.Count; i++) { %>

      <% if (i > 0) { %>AND <% } %>

      [<%= SourceTable.PrimaryKey.MemberColumns[i].Name %>] = @<%= SourceTable.PrimaryKey.MemberColumns[i].Name %>

      <% } %>

 

When this template is loaded into CodeSmith you will see the SourceTable property including a button on the right that you can use to select the table to run this stored procedure against, as seen in the following screenshot.

 

 

To select a table click the button on the right, you will then see the following screen.

 

 

On this screen you can configure your data source  (by clicking on the ellipse button next to the current data source), and then select the table you want to execute this template against. After selecting the table and clicking generate the following text will be generated.

 

-----------------------------------------------------------------

-- Date Created: Saturday, December 06, 2003

-- Created By:   Generated by CodeSmith

-----------------------------------------------------------------

 

CREATE PROCEDURE dbo.UpdateOrders

      @OrderID int,

      @CustomerID nchar(5),

      @EmployeeID int,

      @OrderDate datetime,

      @RequiredDate datetime,

      @ShippedDate datetime,

      @ShipVia int,

      @Freight money,

      @ShipName nvarchar(40),

      @ShipAddress nvarchar(60),

      @ShipCity nvarchar(15),

      @ShipRegion nvarchar(15),

      @ShipPostalCode nvarchar(10),

      @ShipCountry nvarchar(15)

AS

 

UPDATE [Orders] SET

      [CustomerID] = @CustomerID,

      = @EmployeeID,

      [OrderDate] = @OrderDate,

      [RequiredDate] = @RequiredDate,

      [ShippedDate] = @ShippedDate,

      [ShipVia] = @ShipVia,

      [Freight] = @Freight,

      [ShipName] = @ShipName,

      [ShipAddress] = @ShipAddress,

      [ShipCity] = @ShipCity,

      [ShipRegion] = @ShipRegion,

      [ShipPostalCode] = @ShipPostalCode,

      [ShipCountry] = @ShipCountry

WHERE

     

      [OrderID] = @OrderID

       

 

This template can be run against any table in any database and an update stored procedure will be generated.

 

This template demonstrates some of the power that CodeSmith is capable of and should demonstrate how to build a template that takes advantage of the SchemaExplorer. There are many more properties and objects in the SchemaExplorer and you should refer to the SchemaExplorer documentation for more information.

 

Conclusion

Hopefully this quick start guide has provided you with enough information and examples to get you started working with CodeSmith. CodeSmith can be a very powerful and time saving tool, hopefully you find as many uses for CodeSmith in your projects as we have in ours.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值