Contents
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.
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
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
→ → .
From the Insert an Operator or Adapter dialog that opens, select Microsoft .NET and double-click or press OK.
This section describes the properties you can set for a Microsoft .NET operator, using the various tabs of the Properties view in StreamBase Studio.
Name: Use this field to specify or change the component's name, which must be unique in the application. 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: A field that shows the fully qualified class name that implements the functionality of this operator. Use this class name when loading the operator in StreamSQL programs with the APPLY JAVA statement. 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 or to a module parameter that evaluates to true, an instance of this operator starts as part of the containing StreamBase Server. If this field is set to No or to a module parameter that evaluates to false, the adapter is loaded with the server, but does not start until you send an sbadmin resume command, or until you start the component with StreamBase Manager. With this option set to No or false, the operator does not start even if the application as a whole is suspended and later resumed. The recommended setting is selected by default.
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 and Error Streams 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.
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:
-
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
-
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
). - .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 namedC:\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.
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.
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.
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.
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.
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:
-
Use
→ → to create a Studio project that you can callmyPi.sbapp
. -
Create an input stream, call it
precision
, and give it a schema consisting of one int field namednumDigits
. -
Drag a .NET Operator from the palette, name it myPi, and connect it to the input stream.
-
Add an output stream and connect it to the operator.
-
Click the .NET operator and go to its Properties tab.
-
Enter the full path to the assembly:
C:\MyAssemblies\CalculatorAssembly.DLL.
-
Enter the fully qualified class name:
MyNamespace.MyDotNETOperator.
-
Click the Ports tab and set the number of input ports and output ports to 1 each.
-
Click the Schemas tab and define the output port's schema to a string field named
pi
. -
Save your completed
myPi.sbapp
application. -
Click StreamBase Studio’s
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 .
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.
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.
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.
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.
-
Open the target assembly’s solution in Visual Studio.
-
Place a breakpoint in the desired location (for example, within your implementation of
Operator.ProcessTuple()
) -
In
menu, select , choosesbd-java.exe
in the list of processes, and then click .
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()
:
-
Start your StreamBase Server with the
--suspend
option (for example,sbd --suspend myPi.sbapp
). -
Attach your .NET debugger to the
sbd-java.exe
process. -
Use the StreamBase
sbadmin
utility (with the commandsbadmin resume
) to run your application.
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 Extending StreamBase section for an entry called Microsoft .NET Operator.
and look under the