Skip to main content

Common Patterns

Reusable BimlScript patterns for common data engineering tasks. Each pattern includes working code examples you can adapt for your projects.

Staging Layer Pattern

When to Use

  • Loading data from source systems into a staging area
  • Creating a consistent landing zone before transformation
  • Maintaining source data structure with audit columns

Design Principles

  • Temporary storage: Non-persistent, truncated before each load
  • No transformations: Same attributes as source
  • Audit columns: Add identity, load date for tracking

Code Example

Step 1: Import source metadata

<#@ template tier="10" #>
<#@ import namespace="Varigence.Biml.CoreLowerer.SchemaManagement" #>
<#
var sourceConnection = RootNode.OleDbConnections["Source"];
var includedSchemas = new List<string>{"Sales", "Person", "Production"};
var tables = sourceConnection.GetDatabaseSchema(
includedSchemas,
null,
ImportOptions.ExcludeForeignKey | ImportOptions.ExcludeViews
).TableNodes;
#>

<Biml xmlns="http://schemas.varigence.com/biml.xsd">
<Tables>
<# foreach (var table in tables) { #>
<Table Name="<#= table.Name #>" SchemaName="Staging.<#= table.Schema #>">
<Columns>
<# foreach (var column in table.Columns) { #>
<#= column.GetBiml() #>
<# } #>
<!-- Audit columns -->
<Column Name="StageID" DataType="Int32" IsNullable="false" IdentityIncrement="1" IdentitySeed="1" />
<Column Name="LoadDateTime" DataType="DateTime2" IsNullable="false" />
</Columns>
</Table>
<# } #>
</Tables>
</Biml>

Step 2: Generate DDL deployment package

<#@ template tier="20" #>
<Biml xmlns="http://schemas.varigence.com/biml.xsd">
<Packages>
<Package Name="DeployStaging" ConstraintMode="Parallel">
<Tasks>
<# foreach (var table in RootNode.Tables.Where(t => t.Schema.Name == "Staging")) { #>
<ExecuteSQL Name="Create_<#= table.Name #>" ConnectionName="Target">
<DirectInput><#= table.GetDropAndCreateDdl() #></DirectInput>
</ExecuteSQL>
<# } #>
</Tasks>
</Package>
</Packages>
</Biml>

Truncate and Load Pattern

When to Use

  • Full refresh of staging or dimension tables
  • Small to medium datasets where full reload is acceptable
  • Scenarios where data lineage isn't required

Code Example

<#@ template tier="30" #>
<Biml xmlns="http://schemas.varigence.com/biml.xsd">
<Packages>
<# foreach (var table in RootNode.Tables) { #>
<Package Name="TruncateLoad_<#= table.Schema #>_<#= table.Name #>" ConstraintMode="Linear">
<Tasks>
<!-- Step 1: Truncate target -->
<ExecuteSQL Name="TruncateTarget" ConnectionName="Target">
<DirectInput>TRUNCATE TABLE [<#= table.Schema #>].[<#= table.Name #>]</DirectInput>
</ExecuteSQL>

<!-- Step 2: Load data -->
<Dataflow Name="LoadData">
<Transformations>
<OleDbSource Name="Source" ConnectionName="Source">
<DirectInput>SELECT * FROM [<#= table.Schema #>].[<#= table.Name #>]</DirectInput>
</OleDbSource>

<!-- Add load timestamp -->
<DerivedColumns Name="AddAuditColumns">
<Columns>
<Column Name="LoadDateTime" DataType="DateTime2" Length="8">(DT_DBTIMESTAMP2,7)GETDATE()</Column>
</Columns>
</DerivedColumns>

<OleDbDestination Name="Destination" ConnectionName="Target">
<ExternalTableOutput Table="[<#= table.Schema #>].[<#= table.Name #>]" />
</OleDbDestination>
</Transformations>
</Dataflow>
</Tasks>
</Package>
<# } #>
</Packages>
</Biml>

Metadata-Driven Generation

When to Use

  • Enterprise-scale solutions with many tables
  • Need to filter or customize by table metadata
  • Centralized control over package generation

Code Example: Using Annotations

<#@ template tier="10" #>
<Biml xmlns="http://schemas.varigence.com/biml.xsd">
<Tables>
<Table Name="Customer" SchemaName="dbo">
<Annotations>
<Annotation Tag="LoadType">Full</Annotation>
<Annotation Tag="Priority">High</Annotation>
</Annotations>
<Columns>
<Column Name="CustomerID" DataType="Int32" IsNullable="false" />
<Column Name="Name" DataType="String" Length="100" />
</Columns>
</Table>
<Table Name="OrderHistory" SchemaName="dbo">
<Annotations>
<Annotation Tag="LoadType">Incremental</Annotation>
<Annotation Tag="Priority">Low</Annotation>
</Annotations>
<Columns>
<Column Name="OrderID" DataType="Int32" IsNullable="false" />
<Column Name="OrderDate" DataType="DateTime" />
</Columns>
</Table>
</Tables>
</Biml>

Generate packages based on annotations:

<#@ template tier="20" #>
<Biml xmlns="http://schemas.varigence.com/biml.xsd">
<Packages>
<# foreach (var table in RootNode.Tables) {
var loadType = table.GetTag("LoadType") ?? "Full";
var priority = table.GetTag("Priority") ?? "Normal";
#>
<Package Name="Load_<#= table.Name #>_<#= loadType #>" ConstraintMode="Linear">
<Annotations>
<Annotation Tag="Priority"><#= priority #></Annotation>
</Annotations>
<Tasks>
<# if (loadType == "Full") { #>
<ExecuteSQL Name="Truncate" ConnectionName="Target">
<DirectInput>TRUNCATE TABLE [<#= table.Schema #>].[<#= table.Name #>]</DirectInput>
</ExecuteSQL>
<# } #>
<Dataflow Name="LoadData">
<!-- Data flow content -->
</Dataflow>
</Tasks>
</Package>
<# } #>
</Packages>
</Biml>

Incremental Load Pattern

When to Use

  • Large tables where full refresh is too slow
  • CDC (Change Data Capture) scenarios
  • Delta detection based on timestamp or version columns

Code Example: Using High-Water Mark

<#@ template tier="30" #>
<Biml xmlns="http://schemas.varigence.com/biml.xsd">
<Packages>
<# foreach (var table in RootNode.Tables.Where(t => t.GetTag("LoadType") == "Incremental")) { #>
<Package Name="IncrementalLoad_<#= table.Name #>" ConstraintMode="Linear">
<Variables>
<Variable Name="LastLoadDate" DataType="DateTime" EvaluateAsExpression="true">
(DT_DATE)"1900-01-01"
</Variable>
<Variable Name="CurrentLoadDate" DataType="DateTime" EvaluateAsExpression="true">
GETDATE()
</Variable>
</Variables>

<Tasks>
<!-- Get last successful load date -->
<ExecuteSQL Name="GetLastLoadDate" ConnectionName="Target" ResultSet="SingleRow">
<DirectInput>
SELECT ISNULL(MAX(LoadDateTime), '1900-01-01') AS LastLoadDate
FROM [<#= table.Schema #>].[<#= table.Name #>]
</DirectInput>
<Results>
<Result Name="LastLoadDate" VariableName="User.LastLoadDate" />
</Results>
</ExecuteSQL>

<!-- Load new/changed records -->
<Dataflow Name="LoadDelta">
<Transformations>
<OleDbSource Name="GetChanges" ConnectionName="Source">
<DirectInput>
SELECT *
FROM [<#= table.Schema #>].[<#= table.Name #>]
WHERE ModifiedDate > ?
</DirectInput>
<Parameters>
<Parameter Name="0" VariableName="User.LastLoadDate" />
</Parameters>
</OleDbSource>

<OleDbDestination Name="InsertChanges" ConnectionName="Target">
<ExternalTableOutput Table="[<#= table.Schema #>].[<#= table.Name #>]" />
</OleDbDestination>
</Transformations>
</Dataflow>
</Tasks>
</Package>
<# } #>
</Packages>
</Biml>

Error Handling Pattern

When to Use

  • Production packages requiring robust error handling
  • Logging failed rows for later analysis
  • Graceful failure with notifications

Complete Error Handling Walkthrough

This pattern implements comprehensive error handling with:

  • Data flow error row redirection
  • Task-level event handlers
  • Centralized error logging

Step 1: Create Error Logging Table

CREATE TABLE dbo.ErrorLog (
ErrorLogID INT IDENTITY(1,1) PRIMARY KEY,
PackageName NVARCHAR(255),
TaskName NVARCHAR(255),
ErrorCode INT,
ErrorDescription NVARCHAR(MAX),
ErrorColumnName NVARCHAR(255),
SourceRowData NVARCHAR(MAX),
CreatedDate DATETIME2 DEFAULT GETDATE()
);

Step 2: Data Flow Error Redirection

<Dataflow Name="LoadData">
<Transformations>
<OleDbSource Name="Source" ConnectionName="Source">
<DirectInput>SELECT * FROM SourceTable</DirectInput>
</OleDbSource>

<DataConversion Name="ConvertTypes">
<Columns>
<Column SourceColumn="Amount" TargetColumn="AmountDecimal" DataType="Decimal" Precision="18" Scale="2"/>
</Columns>
<ErrorHandling ErrorRowDisposition="RedirectRow" TruncationRowDisposition="RedirectRow"/>
</DataConversion>

<OleDbDestination Name="Destination" ConnectionName="Target">
<ExternalTableOutput Table="TargetTable"/>
<ErrorHandling ErrorRowDisposition="RedirectRow"/>
</OleDbDestination>

<!-- Error output path -->
<OleDbDestination Name="ErrorDestination" ConnectionName="Target">
<InputPath OutputPathName="ConvertTypes.Error"/>
<ExternalTableOutput Table="dbo.ErrorLog"/>
</OleDbDestination>
</Transformations>
</Dataflow>

Step 3: Event Handler for Task Failures

<Package Name="LoadWithErrorHandling">
<Events>
<Event Name="OnError" EventType="OnError">
<Tasks>
<ExecuteSQL Name="LogError" ConnectionName="Target">
<DirectInput>
INSERT INTO dbo.ErrorLog (PackageName, TaskName, ErrorCode, ErrorDescription)
VALUES (?, ?, ?, ?)
</DirectInput>
<Parameters>
<Parameter Name="0" VariableName="System.PackageName" DataType="String"/>
<Parameter Name="1" VariableName="System.SourceName" DataType="String"/>
<Parameter Name="2" VariableName="System.ErrorCode" DataType="Int32"/>
<Parameter Name="3" VariableName="System.ErrorDescription" DataType="String"/>
</Parameters>
</ExecuteSQL>
</Tasks>
</Event>
</Events>

<Tasks>
<!-- Your main tasks here -->
</Tasks>
</Package>

Error Row Disposition Options

DispositionBehaviorUse Case
FailComponentStop package on errorCritical data, no tolerance for errors
RedirectRowSend to error outputLog errors, continue processing valid rows
IgnoreFailureSkip row, continueNon-critical data, acceptable data loss

Code Example: Complete Error Handling Package

<#@ template tier="30" #>
<Biml xmlns="http://schemas.varigence.com/biml.xsd">
<Packages>
<# foreach (var table in RootNode.Tables) { #>
<Package Name="Load_<#= table.Name #>" ConstraintMode="Linear">
<Variables>
<Variable Name="ErrorCount" DataType="Int32">0</Variable>
<Variable Name="ErrorMessage" DataType="String"></Variable>
</Variables>

<Tasks>
<Dataflow Name="LoadData">
<Transformations>
<OleDbSource Name="Source" ConnectionName="Source">
<DirectInput>SELECT * FROM [<#= table.Schema #>].[<#= table.Name #>]</DirectInput>
</OleDbSource>

<!-- Redirect errors -->
<OleDbDestination Name="Destination" ConnectionName="Target">
<ErrorHandling ErrorRowDisposition="RedirectRow" />
<ExternalTableOutput Table="[<#= table.Schema #>].[<#= table.Name #>]" />
</OleDbDestination>

<!-- Capture error rows -->
<FlatFileDestination Name="ErrorOutput" ConnectionName="ErrorFile">
<InputPath OutputPathName="Destination.Error" />
</FlatFileDestination>
</Transformations>
</Dataflow>
</Tasks>

<Events>
<Event EventType="OnError">
<Tasks>
<ExecuteSQL Name="LogError" ConnectionName="Target">
<DirectInput>
INSERT INTO dbo.ErrorLog (PackageName, TaskName, ErrorCode, ErrorDescription)
VALUES ('<#= table.Name #>', ?, ?, ?)
</DirectInput>
<Parameters>
<Parameter Name="0" VariableName="System.SourceName" DataType="String" />
<Parameter Name="1" VariableName="System.ErrorCode" DataType="Int32" />
<Parameter Name="2" VariableName="System.ErrorDescription" DataType="String" />
</Parameters>
</ExecuteSQL>
</Tasks>
</Event>
</Events>
</Package>
<# } #>
</Packages>
</Biml>

Master Workflow Pattern

When to Use

  • Orchestrating multiple child packages
  • Controlling parallel vs sequential execution
  • Grouping packages by schema or priority

Code Example

<#@ template tier="50" #>
<Biml xmlns="http://schemas.varigence.com/biml.xsd">
<Packages>
<Package Name="Master_LoadAll" ConstraintMode="Linear">
<Tasks>
<!-- Execute high-priority packages first -->
<Container Name="HighPriority" ConstraintMode="Parallel">
<Tasks>
<# foreach (var pkg in RootNode.Packages.Where(p => p.GetTag("Priority") == "High")) { #>
<ExecutePackage Name="Execute_<#= pkg.Name #>">
<Package PackageName="<#= pkg.Name #>" />
</ExecutePackage>
<# } #>
</Tasks>
</Container>

<!-- Then execute normal priority -->
<Container Name="NormalPriority" ConstraintMode="Parallel">
<Tasks>
<# foreach (var pkg in RootNode.Packages.Where(p => p.GetTag("Priority") != "High")) { #>
<ExecutePackage Name="Execute_<#= pkg.Name #>">
<Package PackageName="<#= pkg.Name #>" />
</ExecutePackage>
<# } #>
</Tasks>
</Container>
</Tasks>
</Package>
</Packages>
</Biml>

Quick Reference: Common Extension Methods

MethodPurposeExample
GetDatabaseSchema()Import tables from connectionconnection.GetDatabaseSchema()
GetBiml()Convert object to Biml XMLcolumn.GetBiml()
GetDropAndCreateDdl()Generate DDL scripttable.GetDropAndCreateDdl()
GetColumnList()Get comma-separated columnstable.GetColumnList()
GetTag()Read annotation valuetable.GetTag("LoadType")

Next Steps