BimlScript Control Nuggets
What Control Nuggets Do
A flat Biml file describes a fixed set of objects. A BimlScript file adds embedded code that decides what to write before the Biml is generated. Control nuggets are the part of BimlScript that holds that code. They wrap a block of C# (or VB) inside the markers '<#' and '#>' and run when the Biml engine compiles the template. Nothing inside a control nugget appears in the output directly. Instead, the code inside the nugget controls which surrounding Biml fragments get written.
Three patterns cover most of the day to day use of control nuggets: loops, conditionals, and calls to external metadata.
A Loop That Generates One Package per Table
Looping is the most common use of a control nugget. The loop opens with a control nugget, writes one or more Biml fragments inside the loop body, and closes with another control nugget:
<Biml xmlns="http://schemas.varigence.com/biml.xsd">
<Packages>
<# foreach (var stagingTable in RootNode.Tables.Where(t => t.Schema.Name == "staging")) { #>
<Package Name="Load_<#=stagingTable.Name#>" ConstraintMode="Linear">
<Tasks>
<ExecuteSQL Name="TruncateTarget" ConnectionName="WarehouseConn">
<DirectInput>TRUNCATE TABLE staging.<#=stagingTable.Name#></DirectInput>
</ExecuteSQL>
</Tasks>
</Package>
<# } #>
</Packages>
</Biml>
The first control nugget opens the loop. The second one closes it. Everything between the two opens and closes is repeated once per table in the filtered collection.
A Conditional That Skips Certain Tables
Control nuggets can also wrap a fragment in an 'if' so the fragment only appears when a condition is true:
<# foreach (var srcTable in RootNode.Tables) { #>
<Package Name="Process_<#=srcTable.Name#>">
<Tasks>
<# if (srcTable.HasAnnotation("UseFastLoad")) { #>
<Dataflow Name="FastLoad">
<!-- bulk load configuration -->
</Dataflow>
<# } else { #>
<Dataflow Name="StandardLoad">
<!-- row by row configuration -->
</Dataflow>
<# } #>
</Tasks>
</Package>
<# } #>
The opening and closing braces of the 'if' and the 'else' each live in their own control nugget. The intermediate Biml fragments belong to whichever branch surrounds them.
Pulling External Metadata Into the Template
Control nuggets are not limited to looping over Biml objects already in the project. They can call into any .NET API. A common pattern is to read a list of source tables from a metadata table and use the result to drive the loop:
<#
var metadataConn = SchemaManager.CreateConnectionNode(
"MetaConn",
"Data Source=MetadataServer;Initial Catalog=ETLCatalog;Integrated Security=SSPI;Provider=SQLNCLI11;");
var driverList = ExternalDataAccess.GetDataTable(
metadataConn.ConnectionString,
"SELECT SourceSchema, SourceTable, TargetTable FROM cfg.LoadDriver WHERE IsActive = 1");
#>
<Biml xmlns="http://schemas.varigence.com/biml.xsd">
<Packages>
<# foreach (System.Data.DataRow driverRow in driverList.Rows) { #>
<Package Name="Load_<#=driverRow["TargetTable"]#>">
<!-- ... -->
</Package>
<# } #>
</Packages>
</Biml>
The first control nugget block runs once at compile time, fetches the driver list, and stores it in a variable. The second nugget loops over the result and emits one Biml package per row.
Mental Model
Treat a control nugget as a hole in the Biml document where the code inside picks which Biml fragments to write next. Whatever is outside the nuggets goes through to the output as written. Whatever is inside the nuggets is C# that runs at compile time and never appears in the generated artifact.