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 may 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 InsertOperatorJava.

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, which must be unique in the current EventFlow module. The name must contain only alphabetic characters, numbers, and underscores, and no hyphens or other special characters. The first character must be alphabetic or an underscore.

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

Class: 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 with application: If this field is set to Yes (default) or to a module parameter that evaluates to true, this instance of this operator starts as part of the JVM engine that runs this EventFlow module. If this field is set to No or to a module parameter that evaluates to false, the operator instance is loaded with the engine, but does not start until you send an sbadmin resume command, or until you start the component with StreamBase Manager.

Enable Error Output Port: Select this check box 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 component's purpose and function. In the EventFlow 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 two 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. You should use this for example when your assembly is located in the GAC.

    Example 1. Examples

    MyAssembly

    MyAssembly, Version=1.0.1234.0, Culture="en-US", PublicKeyToken=b77a5c561934e089c


  2. Using the location of your assembly on disk, either an absolute path or a path relative to your StreamBase project.

    Example 2. Examples

    C:\MyDirectory\MyAssembly.DLL

    ..\MyAssembly.DLL (for an assembly located one directory up from the StreamBase Server's current working directory)

    MyAssembly.DLL (for an assembly located directly in the StreamBase project's directory)


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).

Example 3. Example

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 will look 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 will look for a config 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 will be 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 will instead be 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.

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.

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 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.0 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 FileNewStreamBase Project to create a Studio project that you can call myPi.sbapp.

  2. 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. Click the .NET operator and go to its Properties tab.

  6. Enter the full path to the assembly: 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 launch 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 Debug Perspective’s Manual Input pane 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 Application Output pane.

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.0, not the Framework 4.0 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 working directory of the StreamBase application is used as a reference. If the assembly is not located in the specified directory the load will fail.

  • 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 will fail.

  • 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 will be loaded. Otherwise the load will fail.

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.

Debugging Target Assembly Problems

It is not difficult to debug .NET code in Microsoft Visual Studio, provided that debugging symbols are available. The compiler produces symbol tables for an assembly as .PDB files, which you should place in the same directory as the .DLL files they refer to.

  1. Open the target assembly’s solution in Visual Studio.

  2. Place a breakpoint in the desired location (for example, within your implementation of Operator.ProcessTuple())

  3. In Debug menu, select Attach To Process, choose sbd-java.exe in the list of processes, and then click Attach.

The debugger loads the process and installs the breakpoint. From then on, debugging proceeds as with any other .NET application.

If you need to debug your operator’s Init() method, you must take additional steps. Because Init() is called just after the application loads, there might not be enough time to attach the .NET debugger before it runs. To successfully enter Init():

  1. Start your StreamBase Server with the --suspend option (for example, sbd --suspend myPi.sbapp).

  2. Attach your .NET debugger to the sbd-java.exe process.

  3. Use the StreamBase sbadmin utility (with the command sbadmin resume) to run your application.

.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 | Load StreamBase Sample... and look under the Extending StreamBase section for an entry called Microsoft .NET Operator.