Monday, 23 January 2012

T4 Example: Code Generation

This simple T4 example shows how to generate a C# enum class from an external resource, in this case a SQL database.

First of all, let’s create our database:

USE [master]
GO

CREATE DATABASE [T4Demo]
GO

USE [T4Demo]
GO

CREATE TABLE [dbo].[AssetClass](
[Id] [int] IDENTITY(1,1) NOT NULL,
[Description] [varchar](50) NOT NULL,
CONSTRAINT [PK_AssetType] PRIMARY KEY CLUSTERED ( [Id] ASC )
)
GO

INSERT [AssetClass] VALUES ('Equity');
INSERT [AssetClass] VALUES ('Fixed Income');
INSERT [AssetClass] VALUES ('Cash');
GO

Next we’ll create a C# console application and add a T4 text template file:

<#@ template debug="false" hostspecific="true" language="C#" #>
<#@ output extension=".cs" #>
<#@ assembly name="System.Data" #>
<#@ import namespace="System.Data.SqlClient" #>
<#@ import namespace="System.Text.RegularExpressions" #>
<#@ import namespace="System.IO" #>

public enum <#= Path.GetFileNameWithoutExtension(Host.TemplateFile) #>
{
<#
if(Validate())
{
BuildEnum();
}
#>
}

<#+

private bool Validate()
{
if(string.IsNullOrEmpty(Server)) { Error("No server was specified"); }
if(string.IsNullOrEmpty(Database)) { Error("No database was specified"); }
if(!IntegratedSecurity)
{
if(string.IsNullOrEmpty(User)) { Error("No user id was specified"); }
if(string.IsNullOrEmpty(Pwd)) { Error("No password was specified"); }
}

if(string.IsNullOrEmpty(Table)) { Error("No table was specified"); }
if(string.IsNullOrEmpty(ValueColumn)) { Error("The value column was specified"); }
if(string.IsNullOrEmpty(TextColumn)) { Error("The text column was specified"); }

return !this.Errors.HasErrors;
}

private void BuildEnum()
{
using (SqlConnection conn = new SqlConnection(BuildConnStr()))
{
SqlCommand cmd = conn.CreateCommand();
cmd.CommandText = string.Format(
"select {0}, {1} from {2}",
ValueColumn,
TextColumn,
Table);
cmd.CommandType = System.Data.CommandType.Text;

conn.Open();
SqlDataReader rdr = cmd.ExecuteReader();

PushIndent("\t");
while (rdr.Read())
{
string enm = string.Format("{0} = {1},",
CreateValidIdentifier(rdr[TextColumn].ToString()),
rdr[ValueColumn].ToString());
WriteLine(enm);
}

WriteLine("Unknown = 0");
PopIndent();
rdr.Close();
conn.Close();
}
}

// Strips spaces and full stops
private string CreateValidIdentifier(string input)
{
string pattern = @"[\.\[\]\s]";
Regex regex = new Regex(pattern, RegexOptions.None);
return regex.Replace(input, "");
}

private string BuildConnStr()
{
SqlConnectionStringBuilder csb = new SqlConnectionStringBuilder();
csb.DataSource = Server;
csb.InitialCatalog = Database;
csb.IntegratedSecurity = IntegratedSecurity;

if(!IntegratedSecurity)
{
csb.UserID = User;
csb.Password = Pwd;
}

return csb.ConnectionString;
}

private string Server = ".";
private string Database = "T4Demo";
private bool IntegratedSecurity = true;
private string User = "";
private string Pwd = "";
private string Table = "dbo.AssetClass";
private string ValueColumn = "Id";
private string TextColumn = "Description";

#>

Now save the file and you'll have your own up-to-date enum class driven from your database:

public enum AssetClass
{
Equity = 1,
FixedIncome = 2,
Cash = 3,
Unknown = 0
}

You can also set up Visual Studio to always regenerate the file on each build if you prefer.


Source code:  http://stevenhollidge.com/blog-source-code/T4CodeGenerationEnums.zip

No comments:

Post a Comment