Reason #52356 I love using Castle’s ActiveRecord is that CRUD is automatically handled for you, which is a major time saver. I’m always looking for any edge to speed up the development process or automate redundant tasks, and that little drive led me to Codesmith Tools. What’s so cool about Codesmith? The fact that once you define a table — and all major databases are supported — clicking a single button can generate your class file, controller methods, stored procedures, or views.
I started using Codesmith with ASP.NET web forms, and when I first migrated to ASP.NET MVC, was unclear how/where to incorporate Codesmith into this new MVC environment. Well it was easier than I thought! Below I’m going to break down the Codesmith template file (.cst) I use to generate my ActiveRecord classes, which is done in c#:
Codesmith template references
The following section appears at the top of the template:
<%@ CodeTemplate Language="C#" TargetLanguage="C#" Description="Generates a very simple business object." %> <%@ Property Name="SourceTable" Type="SchemaExplorer.TableSchema" Category="Context" Description="Table that the object is based on." %> <%@ Property Name="Namespace" Type="System.String" Default="" Optional="False" Category="MethodInfo" %> <%@ Assembly Name="SchemaExplorer" %> <%@ Assembly Name="System.Data" %> <%@ Import Namespace="SchemaExplorer" %> <%@ Import Namespace="System.Data" %>
The most important rows to notice are the second and third. The SourceTable property tells Codesmith that you are going to choose a table which the rest of the code will reference. The Namespace property, which is tagged as optional, is simply a property I made up. In the code below, you will see how it is used. It is just a placeholder for the template, and you can have as many as you want.
Codesmith template
The next section is the template itself. It will look just like Classic ASP or MVC. In a nutshell, it is a block of text, with Codesmith markup appearing in between <% %> tags.
First we add the using namespaces our c# repository file will need to reference:
using System; using System.Collections.Generic; using System.Linq; using System.Web; using NHibernate; using NHibernate.Criterion; using Castle.ActiveRecord;
Since this is an ActiveRecord class, and we use ICriterion for our queries, we include the NHibernate and ActiveRecord references. Next up is our namespace wrapper. You’ll notice the markup that references the Namespace property at the top of the page:
namespace <%= Namespace %>
{
Whatever you type into the textbox will pop in between the tags. Next up is the real meat: the class definition. You’ll notice all the ActiveRecord attributes above each property, as well as some methods, and finally the closing } for the namespace:
[ActiveRecord("<%= GetClassName(SourceTable) %>")]
public class <%= GetClassName(SourceTable) %> : ActiveRecordBase<<%= GetClassName(SourceTable) %>>
{
public <%= GetClassName(SourceTable) %>()
{
}
#region Properties
<% for (int i = 0; i < SourceTable.PrimaryKey.MemberColumns.Count; i++) { %>
[PrimaryKey(PrimaryKeyType.Native, "<%= SourceTable.PrimaryKey.MemberColumns[i].Name %>")]
public <%= GetCSharpVariableType(SourceTable.PrimaryKey.MemberColumns[i]) %> <%= SourceTable.PrimaryKey.MemberColumns[i].Name %> { get; set; }
<% } %>
<% for (int i = 0; i < SourceTable.NonPrimaryKeyColumns.Count; i++) { %>
[Property]
public <%= GetCSharpVariableType(SourceTable.NonPrimaryKeyColumns[i]) %> <%= GetPropertyName(SourceTable.NonPrimaryKeyColumns[i]) %> { get; set; }
<% if (i < SourceTable.NonPrimaryKeyColumns.Count - 1) Response.Write("\r\n"); %>
<% } %>
#endregion
#region Methods
public static void DeleteAll()
{
DeleteAll( typeof(<%= GetClassName(SourceTable) %>) );
}
public static <%= GetClassName(SourceTable) %>[] Find(ICriterion[] query, Order[] order)
{
return <%= GetClassName(SourceTable) %>.FindAll(order, query);
}
#endregion
}
}
The final section is a series of functions used by the template which extracts class names, database column types from the table, etc.:
<script runat="template">
public string GetPropertyName(ColumnSchema column)
{
string propertyName = column.Name;
if (propertyName == column.Table.Name + "Name") return "Name";
if (propertyName == column.Table.Name + "Description") return "Description";
if (propertyName.EndsWith("TypeCode")) propertyName = propertyName.Substring(0, propertyName.Length - 4);
return propertyName;
}
public string GetCSharpVariableType(ColumnSchema column)
{
if (column.Name.EndsWith("TypeCode")) return column.Name;
switch (column.DataType)
{
case DbType.AnsiString: return "string";
case DbType.AnsiStringFixedLength: return "string";
case DbType.Binary: return "byte[]";
case DbType.Boolean: return "bool";
case DbType.Byte: return "byte";
case DbType.Currency: return "decimal";
case DbType.Date: return "DateTime";
case DbType.DateTime: return "DateTime";
case DbType.Decimal: return "decimal";
case DbType.Double: return "double";
case DbType.Guid: return "Guid";
case DbType.Int16: return "short";
case DbType.Int32: return "int";
case DbType.Int64: return "long";
case DbType.Object: return "object";
case DbType.SByte: return "sbyte";
case DbType.Single: return "float";
case DbType.String: return "string";
case DbType.StringFixedLength: return "string";
case DbType.Time: return "TimeSpan";
case DbType.UInt16: return "ushort";
case DbType.UInt32: return "int";
case DbType.UInt64: return "ulong";
case DbType.VarNumeric: return "decimal";
default:
{
return "__UNKNOWN__" + column.NativeType;
}
}
}
public string GetClassName(TableSchema table)
{
if (table.Name.EndsWith("s"))
{
return table.Name.Substring(0, table.Name.Length - 1);
}
else
{
return table.Name;
}
}
</script>
When executed, the above code creates your class file, ready for copying and pasting into your project, and fully crud-enabled. A brilliant little feature Codesmith includes is a running counter of how many lines of code it has generated for you, along with the ability to set your hourly rate and however many lines of code you can manually generate per hour, so you can see your ROI.
