Using the Microsoft .NET Operator

This topic describes how to use the Microsoft® .NET operator. It explains how to configure the operator's Properties view, how to prepare code it calls, and how to load and debug that code. The .NET operator is available for use only on Microsoft Windows platforms. Likewise, applications that include this operator can run only in a Windows environment.

Introduction

The Microsoft .NET operator interfaces to .NET code ("assemblies," compiled as DLLs) that you provide as part of a StreamBase application. The operator loads a client .NET assembly, instantiates a class within that assembly, passes to it incoming tuples, and optionally emits tuples provided by the .NET client code in return.

To create .NET assemblies, use Microsoft Visual Studio, versioned as described in Supported Configurations. See Creating .NET Clients for details on creating client assembles.

When you configure a .NET operator's properties, you must enter the simple or string name or the location of the .NET assembly containing the code you wish to execute. In the Properties view, you must also provide the name of a class contained in the assembly that extends the StreamBase.SB.Operator class (defined in the StreamBase .NET Client API). You can specify zero or more input ports for use by the operator as well as zero or more output ports which your .NET code can use to return tuples to the application. For details, see Creating Applications Containing .NET Operators

Placing a .NET Operator on the Canvas

Select the .NET operator from the Insert an Operator or Adapter dialog, which you invoke with one of the following methods:

  • Drag the Adapters, Java Operators token from the Operators and Adapters drawer of the Palette view to the canvas.

  • Click in the canvas where you want to place the operator, and invoke the keyboard shortcut O V

  • From the top-level menu, invoke Insert>Operator>Java.

From the Insert an Operator or Adapter dialog that opens, select Microsoft .NET and double-click or press OK.

Properties View Settings

This section describes the properties you can set for a Microsoft .NET operator, using the various tabs of the Properties view in StreamBase Studio.

General Tab

Name: Use this required field to specify or change the name of this instance of this component. The name must be unique within the current EventFlow module. The name can contain alphanumeric characters, underscores, and escaped special characters. Special characters can be escaped as described in Identifier Naming Rules. The first character must be alphabetic or an underscore.

Operator: A read-only field that shows the formal name of the operator.

Class name: Shows the fully qualified class name that implements the functionality of this operator. If you need to reference this class name elsewhere in your application, you can right-click this field and select Copy from the context menu to place the full class name in the system clipboard.

Start options: This field provides a link to the Cluster Aware tab, where you configure the conditions under which this operator starts.

Enable Error Output Port: Select this checkbox to add an Error Port to this component. In the EventFlow canvas, the Error Port shows as a red output port, always the last port for the component. See Using Error Ports to learn about Error Ports.

Description: Optionally, enter text to briefly describe the purpose and function of the component. In the EventFlow Editor canvas, you can see the description by pressing Ctrl while the component's tooltip is displayed.

Operator Properties Tab

This section describes the properties on the Operator Properties tab in the Properties view for the .NET operator. Enter all text fields as string literals, not as expressions.

Assembly Name or Full Path

Specifies the .NET assembly to load in one of three ways:

  1. Using the simple or strong name of the assembly to load. This causes the .NET runtime to try to locate the assembly using the usual assembly search rules. Use this, for example, when your assembly is located in the GAC. This is a machine-specific location.

    MyAssembly
    MyAssembly, Version=1.0.1234.0, Culture="en-US", PublicKeyToken=b77a5c561934e089c
  2. Using the location of your assembly in the relative to the src/main/resources folder of your Studio project. This location allows you to place your assembly's DLL file and its associated app.config file in src/main/resources, or a subfolder thereof. In this case, the assembly is included in the EventFlow fragment's archive, and is thus available at deployment time.

  3. Using the location of your assembly on disk, either a full absolute path or a path relative to the node directory of the StreamBase Runtime node in which the EventFlow fragment is launched.

    C:\MyDirectory\MyAssembly.DLL
    ..\..\MyAssembly.DLL
      (for an assembly located at the root of the current 
       Studio workspace)
    ..\ProjName\src\bin\resources\MyAssembly.DLL
      (for an assembly located in the src/main/resources folder 
       of the Studio project ProjName)

    The node directory for a StreamBase Runtime node can be located as follows:

    • For a fragment launched in a node in StreamBase Studio, the node directory is in a folder named nodename.clustername at the root of the Studio workspace. An example node directory is A.sbuser, where sbuser is your OS login name.

    • It is possible for a Studio launch to specify an alternate node directory using the Parameters tab of the Run Configuration dialog.

    • For a fragment launched in a node at the command line with the epadmin command, the node directory is either:

      • Specified explicitly with the nodedirectory= parameter of the epadmin command, or

      • If not specified, in the current directory of the epadmin command with subfolder name nodename.clustername.

Class Name

Specifies the fully qualified name of the .NET class to be loaded from the assembly. This class is assumed to extend StreamBase.SB.Operator (which is defined in the StreamBase .NET Client API, found either in $STREAMBASE_HOME\bin or $STREAMBASE_HOME\bin64).

MyNamespace.MyClassName
.NET App Configuration File Name

Use this to specify the location of a .NET app.config file to load. If this is left empty, the operator looks for a file named assembly_name.config located alongside the target .NET assembly. For example, if your target assembly is at C:\MyDir\MyAssembly.dll, the operator looks for a configiration file named C:\MyDir\Myassembly.dll.config.

Note

The .NET CLR only allows one app.config file to be loaded per AppDomain, and it must be loaded before any attempts are made to read application settings from user code. Consequently, it is recommended that you either make sure all of your application's .NET operator instances can use the same configuration file, or that you load instances with different configuration needs in separate AppDomains (using the Run In Separate AppDomain property, describe immediately below).

Run In Separate AppDomain

When this setting is unchecked (which is the default), instances of the .NET operator are loaded in the CLR's default AppDomain. This is done to maximize performance and to facilitate the sharing of static data between operator instances. If this option is checked, the instance is instead loaded in a separate AppDomain, isolating it from the other instances. This allows the instance to use its own app.config file and to keep its static data private. However, running in a separate domain forces the CLR to use cross-domain marshaling to access the instance, thereby increasing the performance overhead when making StreamBase-to-.NET transitions, compared to instances running in the default AppDomain.

Number Of Input Ports

Use this to configure the number of input ports you wish the operator to have. Valid values are 0 to 10, default is 1.

Number Of Output Ports

Use this to configure the number of output ports you wish the operator to have. Valid values are 0 to 10, default is 1. The number you enter here determines the number of schemas available for configuration on the Edit Schema tab (see Edit Schemas Tab).

Log Level

Use this to set the operator to produce more or less verbose console output, independently of the STREAMBASE_LOG_LEVEL global setting.

Edit Schemas Tab

This tab contains the definition of the operator's output ports. The number of output schemas available here for definition depends on the value entered for the Number Of Output Ports setting on the Operator Properties tab as defined above. The other schemas (say, #7 through #10 if 6 was entered in Number Of Output Ports) are grayed out and their definitions are not used.

Cluster Aware Tab

Use the settings in this tab to enable this operator or adapter for runtime start and stop conditions in a multi-node cluster. During initial development of the fragment that contains this operator or adapter, and for maximum compatibility with releases before 10.5.0, leave the Cluster start policy control in its default setting, Start with module.

Cluster awareness is an advanced topic that requires an understanding of StreamBase Runtime architecture features, including clusters, quorums, availability zones, and partitions. See Cluster Awareness Tab Settings on the Using Cluster Awareness page for instructions on configuring this tab.

Concurrency Tab

Use the Concurrency tab to specify parallel regions for this instance of this component, or multiplicity options, or both. The Concurrency tab settings are described in Concurrency Options, and dispatch styles are described in Dispatch Styles.

Caution

Concurrency settings are not suitable for every application, and using these settings requires a thorough analysis of your application. For details, see Execution Order and Concurrency, which includes important guidelines for using the concurrency options.

Interfacing .NET Code

Your organization may already have some .NET libraries that you want a .NET operator to invoke. To do this, write a separate assembly (or add some code to your existing assembly) to serve as the entry point for the operator whenever it receives a StreamBase tuple. You can also use this wrapper code to extract values from the incoming tuple before invoking your other libraries, so that those libraries need not be StreamBase-aware.

The .NET class used by the .NET operator to invoke your code is expected to have a public default constructor and to extend StreamBase.SB.Operator (which is defined in the StreamBase .NET Client API). This class contains several methods that can be overridden to perform useful work (for example to receive tuples or get notified when the operator is suspended), and others that can be used to interact directly with the StreamBase application (for example, to send new tuples or log messages using the server's logging facilities). To see a complete definition of the Operator class and its members refer to the .NET API Documentation.

The .NET class used by the .NET operator to invoke your code is expected to have a public default constructor and to extend StreamBase.SB.Operator (which is defined in the StreamBase .NET Client API). This class contains several methods that can be overridden to perform useful work (for example to receive tuples or get notified when the operator is suspended), and others that can be used to interact directly with the StreamBase application (for example, to send new tuples or log messages using the server's logging facilities). To see a complete definition of the Operator class and its members refer to the .NET API Documentation.

Creating Applications Containing .NET Operators

You create and configure .NET Operators like other operators available in StreamBase Studio, by dragging an instance of the operator from the Operators and Adapters palette to the canvas. You then set its properties in its Operator Properties tab, as the following illustration depicts.

The key properties to specify are the full path (or the .NET strong name) of the assembly to load, and the fully qualified name of the class inside this assembly that extends the StreamBase.SB.Operator class. When your application runs, the .NET Operator loads the Microsoft .NET runtime environment into the application’s process, loads the specified assembly, and instantiates the given class. As the .NET operator receives tuples, it passes them on to the .NET object for processing and optionally emits tuples of its own to the application for further processing.

The StreamBase .NET Client API library (StreamBase.SB.Client.dll) is included in the StreamBase base product installation. The StreamBase.SB.Operator class contains several methods that are either called by the operator when interesting events occur (for example, when a tuple is received on one of the input ports) or can be called by the client code to interact with the StreamBase application (for example, to send new tuples on one of the output ports). You can find complete documentation of the .NET Client API library in StreamBase Studio help.

Example of a .NET operator Assembly

A .NET operator can manage multiple input and output ports with complex schemas. However, to illustrate the process of creating one, let's look at a simple example with one input, one output, and basic schemas. Suppose you have code that calculates π (pi) to the Nth digit, and you want to invoke this code as a .NET operator in StreamBase. This is a C# method that you have compiled into an class library you called CalculatorAssembly.DLL. Its signature is:

string MyCalculatorClass.GetPi(int n) 

As you are defining an operator, you must specify the number of desired input and output ports and their associated schemas, which the .NET Operator must use to communicate with the application. For the purpose of computing a single string, all you need is one input port receiving a single field of type int (call it numDigits), and one output port emitting a string field (call it answer).

To begin, in Microsoft Visual Studio create a project to develop StreamBase client code in C# to extend StreamBase.SB.Operator. Your project should:

  • Be set to produce a class library

  • Include a reference to StreamBase.SB.Client.DLL (located in %STREAMBASE_HOME%\bin or %STREAMBASE_HOME%\bin64)

  • Include a reference to CalculatorAssembly.DLL (to use MyCalculatorClass)

  • Target the full .NET 4.8 Framework Profile as opposed to the Client Profile (this is true for any StreamBase .NET Client API code)

Name the resulting assembly MyDotNETOperator.DLL. The implementation of the StreamBase.SB.Operator subclass is as follows (error checking is omitted for clarity):

using StreamBase.SB; 
namespace MyNamespace { 

    public class MyDotNETOperator : Operator { 
        MyCalculatorClass calc = null; 
        public override void Init() { 

        // Create a calculator instance 
        calc = new MyCalculatorClass(); 
        } 
        public override void ProcessTuple(int port, Tuple tuple) { 

            // Get the input value from the tuple 
            tuple.GetInt(“numDigits”); 

            // Calculate the answer 
            string answer = calc.GetPi(numDigits); 

            // Place the answer in a new tuple 
            Tuple returnTuple = GetOutputSchema(0).CreateTuple(); 

            // Send the tuple on the operator’s output port #0 
            SendOutput(0, returnTuple); 
        } ProcessTuple calls 

        // The class needs a public default constructor
        // that can be compiler-generated or explicitly defined, 
        // which will be used by the .NET Operator to create the instance 
        public MyDotNETOperator() { } 
    }     
} 

Compile the MyDotNETOperator.DLL assembly and place it alongside CalculatorAssembly.DLL in a directory you might call C:\MyAssemblies\. Include the accompanying .PDB files if you will be debugging your code. This is all the code that your .NET operator needs.

Now create your StreamBase EventFlow application. In StreamBase Studio:

  1. Use File>New>StreamBase Project to create a Studio project of type EventFlow Fragment that you can call myPi. This creates an empty EventFlow mode in src/main/eventflow/packagename named myPi.sbapp.

  2. In the EventFlow Editor, create an input stream, call it precision, and give it a schema consisting of one int field named numDigits.

  3. Drag a .NET Operator from the palette, name it myPi, and connect it to the input stream.

  4. Add an output stream and connect it to the operator.

  5. Double-click the .NET operator to open its Properties view.

  6. Enter the full path to the assembly. For example, C:\MyAssemblies\CalculatorAssembly.DLL.

  7. Enter the fully qualified class name: MyNamespace.MyDotNETOperator.

  8. Click the Ports tab and set the number of input ports and output ports to 1 each.

  9. Click the Schemas tab and define the output port's schema to a string field named pi.

  10. Save your completed myPi.sbapp application.

  11. Click StreamBase Studio’s Run button to open your application.

When your application starts, the .NET Runtime Environment loads along with all the assemblies, creating an instance of MyDotNETOperator. Enter a value for numDigits in the Manual Input view and click Send Data.

The tuple goes to the .NET assembly code, where method processTuple calls your algorithm to calculate the value of pi to the specified precision. Method SendOutput returns the result to your myPi .NET operator, which emits it to its output stream as a string tuple. Verify the result in the Output Streams view.

Configuring and Referencing a .NET Assembly

When writing .NET code to be used by the .NET operator, remember to consider the following details:

.NET Framework target

As always when writing StreamBase-enabled .NET applications you must target the full Framework 4.8, not the Framework 4.8S Client Profile.

How the Runtime Environment Locates Assemblies

The .NET operator’s ability to locate and load your assembly depends on several factors. The .NET runtime environment does not look in the path defined in the PATH environment variable. Instead, it has its own rule set for determining locations to examine when looking for assemblies. The process slightly differs depending on how you specify the operator's Assembly Name property:

  • If you specify the assembly’s exact location on disk (such as C:\MyDir\MyAssembly.DLL):

    • If a relative path is specified, the current node directory is used as a reference. If the assembly is not located in the specified directory the load fails.

  • If you specify the assembly’s simple name (“MyAssembly”) or fully qualified name (“MyAssembly, Version=1.0, …”):

    • Normal .NET loading rules apply. If the assembly is not found using these rules the load fails.

  • If your assembly references another assembly and normal .NET loading rules fail to locate it, the directory containing the operator’s assembly is probed. If the referenced assembly is found there it is loaded. Otherwise the load fails.

To avoid having to diagnose unnecessary failures, whenever possible place all the assemblies in a specific directory whenever you can and specify the full path to that directory as the operator’s Assembly Name property.

For more details, see the Microsoft MSDN Online document How the Runtime Locates Assemblies.

.NET Operator Sample

On Microsoft Windows platforms the StreamBase installation comes with a sample demonstrating the use of this operator. To load the sample in StreamBase Studio, select File>Import Samples and Community Content and look under the Extending StreamBase section for Microsoft .NET Operator.