Appearance
C# script
Runs your own C# code for every message that reaches the component. Use it to transform data between plugins, implement logic no plugin covers, or call .NET libraries (NuGet packages referenced in the flow configuration are available).
Pins
| Pin | Kind | Types | Description |
|---|---|---|---|
| Input | input | any | The data from the previous component. Its type is the type of the input connection. |
| Output | output | any | Whatever the script passes to Next / NextAsync. The generated template creates an object of the output connection's type. |
Configuration
| Field | Type | Default | Description |
|---|---|---|---|
Script | code (C#) | empty | The class body: the Execute (or ExecuteAsync) method plus any fields, properties and helper methods you need. Empty means the generated template is used. |
Singletone | boolean ("Singleton") | true | true: one script instance is reused for all calls, so fields keep their values between messages. false: a new instance per message. |
Asynchronous | boolean ("Async/Await") | false | true: the script derives from the async base class and must implement ExecuteAsync and call NextAsync. |
Behaviour
- The script is compiled into a class
Script_<componentId>in the flow namespace (for exampleviauser), deriving fromAbstractCshapScriptBase(orAbstractCshapScriptBaseAsyncwhenAsynchronousistrue). You write only the class body. usingstatements may be written anywhere in the script; they are moved to the top before compiling. Already imported:System,System.Linq,System.Text,System.Collections,System.Collections.Generic,System.Threading,System.Threading.Tasks,Viaducta.TypeSystem,Viaducta.TypeSystem.Metadata,Viaducta.Plugin.Design,Viaducta.Plugin.Design.Scripting,Viaducta.Plugin.Design.Debugger,Viaducta.Plugins.CsharpScriptand the flow namespace.- Method signatures:
- sync:
public override void Execute(IMetaData metaDataInstance, IViaType inputDataInstance) - async:
public override async Task ExecuteAsync(IMetaData metaDataInstance, IViaType inputDataInstance) - (
public void Execute(IMetaData ...withoutoverrideis accepted and fixed automatically, for old scripts.)
- sync:
- The generated template (created when
Scriptis empty, from the current connections):- casts the metadata:
var metaData = (<namespace>.FlowMetaData)metaDataInstance; - casts the input to the type of the input connection, e.g.
var input = (Customer)inputDataInstance;(AnyDatausesinputDataInstance.Cast<...>(),NoInputDataa direct cast, imported types their wrapper type); - creates the output from the output connection's type,
var output = new Invoice();, and callsNext(metaData, output);(without an output connection it passes the input on).
- casts the metadata:
Next(metaData, output)/await NextAsync(metaData, output)sends data to the components connected toOutput. Pass an object of the output connection's type. Other objects are accepted too, but anything that is not a Viaducta type is wrapped asViaducta.TypeSystem.Metadata.AnyData. CallNextzero, one or many times per input (filter, pass-through, split).Servicesgives access to the flow services:Services.Log.Information("...")/Verbose/Warning/Errorfor logging,Services.Messaging.UpdateStatusMessage("..."),Services.Messaging.UpdateProgress(0.5f),Services.Settings.Get("<package>", "<name>")for expression values,Services.Assets,Services.TypesFactory,Services.CancellationToken.- Exceptions thrown by the script go to the flow's normal error handling (the component has no error pin).
- With an empty
Scriptthe component shows a warning indicator: the template does nothing useful.
Notes
- Changing
Asynchronousdoes not rewrite an existing script: switch the method toExecuteAsync/NextAsync(or back) yourself, or clear the script to regenerate the template; otherwise compilation fails. - The template is generated from the connections that exist when it is created. Connect input and output first, then open the editor (or clear the script) to get the right casts.
- With
Singletone=truethe same instance serves concurrent messages; fields are shared between parallel executions, so keep them thread-safe or use local variables. - Do not hard-code secrets in the script; read them from settings (
Services.Settings) instead. - A script can group its output into a batch for batch consumers (Build array, Bulk insert, Write file):
await using var batch = Services.Batches.Begin(metaData, "label");then passbatch.MetaDatatoNext/NextAsyncand callawait batch.CompleteAsync()at the end; leaving the scope without completing fails the batch.
Example
Input connection type Customer, output connection type CustomerDto:
csharp
using System.Globalization;
public override void Execute(IMetaData metaDataInstance, IViaType inputDataInstance)
{
var metaData = (viauser.FlowMetaData)metaDataInstance;
var input = (Customer)inputDataInstance;
if (String.IsNullOrEmpty(input.Email)) return; // filter: nothing is sent on
var output = new CustomerDto();
output.Name = CultureInfo.InvariantCulture.TextInfo.ToTitleCase(input.Name ?? "");
output.Email = input.Email.ToLowerInvariant();
Services.Log.Information($"Customer {output.Email} mapped");
Next(metaData, output);
}Asynchronous variant (Asynchronous = true):
csharp
public override async Task ExecuteAsync(IMetaData metaDataInstance, IViaType inputDataInstance)
{
var metaData = (viauser.FlowMetaData)metaDataInstance;
var input = (Customer)inputDataInstance;
await Task.Delay(100, Services.CancellationToken);
await NextAsync(metaData, input);
}Http listener → Json deserializer → C# script → Database writer