Building Your Own Extension Method for Biml
Why Add Extension Methods
Biml ships with a deep object model: connections, packages, dataflows, tables, columns, and so on. Most generation patterns can be expressed by walking those objects with a 'foreach' loop and a few 'if' checks. When the same conversion shows up in three or four templates, however, repeating the same loop in every file becomes a maintenance burden. A C# extension method moves the logic into one place. After that, every template can call it with a single line.
A common case: a project has many flat file formats and needs a matching SQL Server staging table for each one. The conversion is mechanical (every flat file column becomes a table column with the same data type, length, precision, and scale), but the loop is long enough to clutter a template. A 'ToAstTableNode' extension method on 'AstFlatFileFormatNode' removes the clutter.
Where Extension Methods Live
In a BimlStudio project, add a new C# code file to the project. The file has to declare the namespace 'Varigence.Languages.Biml' (or open it with a 'using' directive) so the extension method's 'this' parameter resolves to the right node type. The method itself is a standard C# extension on a static class.
A skeleton looks like this:
using System;
using Varigence.Languages.Biml;
using Varigence.Languages.Biml.FileFormat;
using Varigence.Languages.Biml.Table;
public static class FormatExtensions
{
public static AstTableNode ToAstTableNode(
this AstFlatFileFormatNode flatFileFormat,
AstSchemaNode targetSchema)
{
// body goes here
}
}
The 'this' keyword on the first parameter is what makes the call site read as if 'ToAstTableNode' were a method on 'AstFlatFileFormatNode' itself.
The Conversion Body
The body creates a new 'AstTableNode' under the target schema, copies the flat file format's name onto the table, and walks the 'Columns' collection to add a matching table column for every flat file column:
public static AstTableNode ToAstTableNode(
this AstFlatFileFormatNode flatFileFormat,
AstSchemaNode targetSchema)
{
var newTable = new AstTableNode(targetSchema.GetRootNode())
{
Name = flatFileFormat.Name,
Schema = targetSchema
};
foreach (var sourceColumn in flatFileFormat.Columns)
{
var tableColumn = new AstTableColumnNode(newTable)
{
Name = sourceColumn.Name,
DataType = sourceColumn.DataType,
Length = sourceColumn.Length,
Precision = sourceColumn.Precision,
Scale = sourceColumn.Scale
};
newTable.Columns.Add(tableColumn);
}
return newTable;
}
Adjust the property mapping if the flat file format carries other attributes the table needs (nullability, identity, default values).
Calling the Method from a BimlScript Template
Once the code file is part of the project, any template in the project can call the extension as if it were a built in method:
<#@ template language="C#" tier="2" #>
<#
var stagingSchema = RootNode.Schemas["staging"];
foreach (var format in RootNode.FileFormats.OfType<AstFlatFileFormatNode>())
{
var stagingTable = format.ToAstTableNode(stagingSchema);
stagingSchema.Tables.Add(stagingTable);
}
#>
A four line template now produces one staging table per flat file format. Every additional template that needs the same conversion gets it for free, with no copy paste of the loop.
When to Reach for an Extension Method
Reach for an extension method when the same transformation appears in more than one template, when the transformation deserves its own unit tests, or when the inline version is long enough to obscure the structure of the surrounding template. Anything shorter than a few lines is usually fine to keep inline.