There are several ways to render the template; the first
option is to place static text in the body. Static text gets rendered as is to
the final output window. The second option is to use ASP scripting tags (<%
%>) in the body of the template to perform more advanced output
capabilities, which I will talk about later. The third option is to define
methods and variables in the following script section, similar to inline ASPX
script blocks. Let us look at each option individually.
The first option is embedded static text in the script. For
instance, the following can be embedded in the script.
Listing 5
Create procedure dbo.AddItem
(
ItemKey uniqueidentifier,
ItemName varchar(50)
)
As
-- Proc body
This text gets rendered to the output window as is, which is
not useful. However, with scripting, it is possible to make this more
functional. Let us take a look; the following is a stored procedure template I
created for custom procs so that it formats it the way I want it to.
Listing 6
create procedure <%= GetTableName(true) %>_AddNew
(
<% GenerateParameters(INSERT_MODE, true, 4); %>
)
as
set nocount on
insert into <%=GetTableName(true) %>
(
<% GenerateColumns(INSERT_MODE, false, 4); %>
)
values
(
<% GenerateParameters(INSERT_MODE, false, 4); %>
)
set nocount off
GO
The template above generates an insertion stored procedure. It
uses several methods (explained later) to write content to the screen. At the
top, the template gets the name of the table via the GetTableName method and
embeds it into the procedure name. Next, GenerateParameters creates the list of
input parameters, and the next section creates the body of the proc.
The mix of static content and dynamically generated text
gives the code generation process a nicer touch; however, adding script
sections makes this template even more functional. For instance, the methods
below render script two different ways.
Listing 7
<script runat="template">
//Returns a string to be rendered in the script tags
public string GetTableName(bool includeOwner)
{
return includeOwner ? string.Format("{0}.{1}", this.SourceTable.Owner,
this.SourceTable.Name) : this.SourceTable.Name;
}
//Renders the script inline using Response.Write
public void GenerateColumns(string mode, bool includeAssignments, int indent)
{
ColumnSchemaCollection columns = this.GetColumns(mode);
for (int i = 0; i < columns.Count; i++)
{
this.GenerateIndent(indent);
Response.Write(this.GetColumnName(column, false));
if (includeAssignment)
Response.Write(" = " + this.GetColumnName(column, true));
if (i != columns.Count - 1)
Response.Write(",\n");
}
if (mode != UPDATE_MODE)
Response.Write("\n");
}
</script>
In the first method (GetTableName) method, the script to render
is returned as a string, which renders to the output window through the
following call.
Listing 8
<%= GetTableName(true) %>
As you can see, it follows the conventions of ASP by using
the <% %> tags, as well as the equals sign to render the returned string
directly to the resulting output. Note that in this usage, there is no ending
semicolon.
In the second method, the output is rendered inline using
another similar ASP convention: Response.Write. A list of column objects
returned by the GetColumns method are iterated through and rendered to the screen
using Response.Write(). The ColumnSchema object is a specialized object in
CodeSmith that represents a column in a database. These columns are rendered by
outputting their name; however, if includeAssignments is set to true, it also
writes a variable assignment in the form "Name = @Name" as you would
see it in an update statement.
To invoke a method that does not return a string, use the
statement below. Note the use of a semicolon in this instance.
Listing 9
<% GenerateColumns(INSERT_MODE, true, 4); %>
Again, this method uses Response.Write to render the output,
which works in a different way than returning a string and formatting the
statement within the static text. Using Response.Write means you control the format
of the text by manually inserting tabs, new lines, and other tricks.
It is possible to define constants within the template, and
these constants are available both in the script tags and in the dynamic script
block. In addition, variables and properties can be defined within the script
section that work the same way as you would see in C#.
The CodeSmith library has a bunch of objects to make script
generation easier, such as the StringCollection that takes a comma-separated
list of objects and converts it to a collection, or the SchemaExplorer
namespace that contains a bunch of objects that can connect to a database. CodeSmith
also uses a series of attributes to setup the design-time environment, like you
see with control development in Windows or ASP.NET.