Skip to main content

Introduction to CallBimlScriptWithOutput

Why a New Variant of CallBimlScript

CallBimlScript has long been the preferred way to factor reusable Biml into a callee that the caller invokes with parameters. The limitation is that the callee can only return Biml text. If the caller needs to know something the callee discovered, such as a column count, a derived name, or a list of computed objects, the only options have been to duplicate logic, parse the returned XML, lean on annotations, or fan out into many specialized callees.

CallBimlScriptWithOutput removes that limit. In addition to the Biml the callee emits, it can populate a dynamic object that flows back to the caller. Any value the callee can compute can be returned through that object.

A Minimal Setup

Start with a static Biml file that defines a simple table for the example to read.

<Biml xmlns="http://schemas.varigence.com/biml.xsd">
<Connections>
<OleDbConnection ConnectionString="server=local" Name="LedgerSrc" />
</Connections>
<Databases>
<Database Name="LedgerDb" ConnectionName="LedgerSrc" />
</Databases>
<Schemas>
<Schema Name="dbo" DatabaseName="LedgerDb" />
</Schemas>
<Tables>
<Table Name="JournalEntry" SchemaName="LedgerDb.dbo">
<Columns>
<Column Name="EntryId" />
<Column Name="Memo" />
</Columns>
</Table>
</Tables>
</Biml>

Caller and Callee in VB

The caller declares a dynamic variable, passes it to CallBimlScriptWithOutput, and reads properties off it after the call returns.

<#@ template language="VB" optionexplicit="False" #>
<Biml xmlns="http://schemas.varigence.com/biml.xsd">
<# Dim entryTable = RootNode.Tables(0)
Dim outBundle #>
<#=CallBimlScriptWithOutput("describe-table.biml", outBundle, entryTable) #>

<!-- Result from outBundle: -->
<!-- <#= outBundle.ColCount #> -->
<!-- <#= outBundle.Note #> -->
</Biml>

The callee receives the table and populates the output object. The name CustomOutput is required inside the callee. The caller can name its own variable anything.

<#@ template language="VB" #>
<#@ property type="AstTableNode" name="tbl" #>
<!-- Biml returned directly: -->
<!-- <DerivedColumns Name="ExampleDerived">...</DerivedColumns> -->

<!-- CustomOutput is populated for the caller: -->
<# CustomOutput.ColCount = tbl.Columns.Count
CustomOutput.Note = "Computed by the callee"
CustomOutput.Cols = tbl.Columns #>

The caller now has the column count, a free form note, and the original column collection available to drive any subsequent generation step.

The Same Pattern in C#

The C# version uses the standard out parameter syntax and a dynamic local.

<Biml xmlns="http://schemas.varigence.com/biml.xsd">
<# AstTableNode entryTable = RootNode.Tables[0];
dynamic outBundle; #>
<#=CallBimlScriptWithOutput("describe-table.biml", out outBundle, entryTable) #>

<!-- Result from outBundle: -->
<!-- <#= outBundle.ColCount #> -->
<!-- <#= outBundle.Note #> -->
</Biml>
<#@ property type="AstTableNode" name="tbl" #>
<!-- Biml returned directly: -->
<!-- <DerivedColumns Name="ExampleDerived">...</DerivedColumns> -->

<!-- CustomOutput is populated for the caller: -->
<# CustomOutput.ColCount = tbl.Columns.Count;
CustomOutput.Note = "Computed by the callee";
CustomOutput.Cols = tbl.Columns; #>

When to Reach for It

Anywhere a callee needs to communicate more than emitted Biml back to the caller, CallBimlScriptWithOutput is the cleanest option. Common cases include returning derived names that drive container or task naming on the caller side, returning collections of nodes that the caller iterates, and surfacing diagnostic information that the caller logs into annotations.