Microsoft .Net Framework 3.5 as.Net Application Development by feg39964

VIEWS: 0 PAGES: 65

Microsoft .Net Framework 3.5 as.Net Application Development document sample

More Info
									Hands-On Lab
Introduction To StreamInsight and Complex
Event Processing
Lab version:   1.0.0

Last updated: 3/18/2011
CONTENTS

OVERVIEW ................................................................................................................................................... 3

EXERCISE 1: DEVELOPING A SIMPLE STREAMINSIGHT APPLICATION ............................................ 5
       Task 1 – Reviewing an Existing WinForm Project ................................................................................. 7
       Task 2 – Adding a Simple StreamInsight Query .................................................................................... 9
       Task 3 – Updating the Textbox............................................................................................................ 16
       Task 4 – Testing the Code ................................................................................................................... 18

EXERCISE 2: ENHANCING THE STREAMINSIGHT QUERY .................................................................. 20
       Task 1 – Creating a Tumbling Window Query ..................................................................................... 20
       Task 2 – Testing Tumbling Window Query ......................................................................................... 23
       Task 3 – Creating a Snapshot Window Query ..................................................................................... 24
       Task 4 – Testing Snapshot Window Query.......................................................................................... 28

EXERCISE 3: DEVELOPING STREAMINSIGHT ADAPTERS ................................................................. 29
       Task 1 – Creating the Input Event Payload Definition ........................................................................ 33
       Task 2 – Creating the Input Configuration Class ................................................................................. 35
       Task 3 – Creating the Input Adapter Class .......................................................................................... 36
       Task 4 – Creating the Input Adapter Factory Class ............................................................................. 50
       Task 5 – Creating the Output Configuration Class .............................................................................. 52
       Task 6 – Creating the Output Adapter Class ....................................................................................... 53
       Task 7 – Creating the Output Adapter Factory Class .......................................................................... 58
       Task 8 – Updating the Form ................................................................................................................ 60

SUMMARY .................................................................................................................................................. 65
Overview
Microsoft SQL Server StreamInsight is a platform for developing Complex Event Processing (CEP)
applications on the .NET Framework. Complex Event Processing applications are a type of application
where the main task is to process multiple events sources (also known as event streams) with the goal
of identifying meaningful events (occurring in one or many event streams) or a meaningful series of
events, over a period of time.
StreamInsight enhances the implementation of robust and highly efficient event processing applications
due to its high-throughput stream processing architecture and the Microsoft .NET Framework-based
development platform. Event stream sources typically include data from manufacturing applications,
financial trading applications, Web analytics, and operational analytics. By using StreamInsight, you can
develop CEP applications that derive immediate business value from this raw data by reducing the cost
of extracting, analyzing, and correlating the data; and by allowing you to monitor, manage, and mine the
data for conditions, opportunities, and defects almost instantly.
By using StreamInsight to develop CEP applications, you can achieve the following tactical and strategic
goals for your business:
       Monitor your data from multiple sources for meaningful patterns, trends, exceptions, and
        opportunities.

       Analyze and correlate data incrementally while the data is in-flight (without first storing it)
        yielding very low latency. Aggregate seemingly unrelated events from multiple sources and
        perform highly complex analyses over time.
       Manage your business by performing low-latency analytics on the events and triggering
        response actions that are defined on your business key performance indicators (KPIs).
       Respond quickly to areas of opportunity or threat by incorporating your KPI definitions into the
        logic of the CEP application, thereby improving operational efficiency and your ability to
        respond quickly to business opportunities.
       Mine events for new business key performance indicators (KPIs).
       Move toward a predictive business model by mining historical data to continuously refine and
        improve your KPI definitions.

 Note: For more information, see Overview (StreamInsight).




Objectives
In this Hands-On Lab, you will learn how to:
       Leverage the new StreamInsight framework that will be released as part of SQL Server 2008 R2.

       Learn the basics of Complex Event Processing
       Develop StreamInsight applications


System Requirements
You must have the following items to complete this lab:

       Microsoft Visual Studio 2008 with Service Pack 1
       Microsoft SQL Server StreamInsight


Setup
All the requisites for this lab are verified using the Configuration Wizard. To make sure that everything is
correctly configured, follow these steps.

 Note: To perform the setup steps you need to run the scripts in a command window with
 administrator privileges.



    1. Launch the Configuration Wizard for this Lab by running the Setup.cmd script located under the
       Setup folder in the Source folder of this lab. Install any pre-requisites that are missing
       (rescanning if necessary) and complete the wizard.



Exercises
The following exercises make up this Hands-On Lab:
    1. Developing a Simple StreamInsight Application
    2. Enhancing the StreamInsight Query
    3. Developing StreamInsight Adapters


 Note: The source code that accompanies this hands-on lab includes an End folder where you can find a
 Visual Studio solution with the code that you would obtain if you complete the steps in each exercise.
 You can use this solution as a guide if you need additional help working through the exercises.



Estimated time to complete this lab: 60 minutes.
Exercise 1: Developing a Simple
StreamInsight Application
Before we get into coding, we should get familiar with a few terms and their definitions.


Introducing Complex Event Processing Concepts
Events
Events are the basic unit of data processed by a Complex Event Processing server. Each event consists of
the following parts:
       Header: The Event Header contains the metadata about the event, and the timestamps that
        define the interval of the event. In StreamInsight, because it is built on .NET, all timestamps use
        the .NET Datetime data type. It defines the event kind, and the time related (temporal)
        properties of the event.
       Payload: A .NET data structure that represents the data associated with the event. With
        StreamInsight, the Payload is made up of user defined, serializable, .NET types.

Event Kind
There are two types of Event Kinds in StreamInsight:
       Insert: Adds a new event to the stream with its header and payload. The header identifies the
        start and end time for the event. The payload is the specific data for the event.
       Current Time Increment (CTI): Is a special type of event that defines the completeness of the
        existing events in the stream. It is used to manage out of order event or latency in the event
        stream.

Event Models
The event model defines the event shape (the duration type). In StreamInsight there are three types of
Event Models:
       Point: The Point Event Model describes an event as a single point in time. It has only a start
        time. The Complex Event Processing Server infers the valid end time by adding a tick. Point
        events are valid only for this single instant of time.
       Interval: The Interval Event Model represents an event that is valid for a finite period of time. It
        requires both a start and end time. Internal Events are only valid for this defined period of time.
       Edge: The Edge Event Model is similar to the Interval model, but only the start time is known
        when it arrives at the Complex Event Processing Server. The end time is updated at a later point
        in time. An example of an Edge Event could be a web user session.

Streams
Events are organized into streams. You can think of them as data sources. Each stream is a collection of
data that describes changes occurring over time. Streams are submitted to the Complex Event
Processing Server from external sources.

Adapters
Adapters are responsible for translating and delivering incoming and outgoing event streams to and
from the Complex Event Processing Server. StreamInsight has flexible adapter SDK built using .NET
Framework 3.5. They are stored as .NET assemblies and registered with the CEP Server.

Input Adapters
Input Adapters translate events from external sources (databases, message queues, network ports, etc.)
into the StreamInsight event format and adds them to the CEP Server. Each Input Adapter should be
responsible for specific event sources.

Output Adapters
Output Adapters as used to transform the events processed by the CEP Server into a format that is
expected by the output data consumer.


StreamInsight Event Processing Model
Now that we have a basic understanding of the different aspects of a Complex Event Processing
application, we would like to process and analyze the event streams. To accomplish this, we need to
understand the different development models utilized when building StreamInsight applications.
A developer using StreamInsight can use three different development models. All three models utilize
LINQ expressions, but differ in the way that they define the event producers and consumers and bind
them to the query template to create running queries.

Implicit Server Model
The Implicit Server Model is an easy to use environment that hides most of the complexity associated
with creating and registering most of the StreamInsight objects. It hides the complexity by constraining
some of the abilities of StreamInsight. Typical uses of the Implicit Model are in applications where only a
single query is required, or as a testing environment for query developers. The Implicit Model has the
following characteristics:
       The CEP application can host only one query and it is automatically hosted in an in-process,
        implicit host at run time. The server, query template and other application objects do not need
        to be created or maintained. The implicit model will manage them.

       The query is contained in memory only and is not stored on disk or other stable storage device.
       The must provide input and output adapter bindings (configurations) and query, but they do not
        have to explicitly specify the binding between the adapters and the query.

Explicit Server Model
The Explicit Server Model provides the full capabilities of Complex Event Processing environment by
allowing the developer to create, register and access all the objects required to process the events. The
developer must:
       Understand the API object model (server, application, query template, query, event type,
        adapter, metadata).
       Create or Connect to a CEP Server.

       Create all the CEP objects and explicitly register them with the server.
The Explicit Model supports:
       Creation and use of multiple objects (queries, applications, adapters, and event types).
       Storage of its metadata either in memory or persist it to some media or storage device.
       In process and out of process execution. The CEP Server can be run locally or remotely when
        exposed as a web service.

IObservable/IObserver Development Model
The IObservable Development Model is an alternative to using input and output adapters as the
producer and consumer of event streams. Model is based on the IObservable/IObserver design pattern
in which an observer is any object that wishes to be notified when the state of another object changes,
and an observable is any object whose state may be of interest, and in whom another object may
register an interest. It is the simplest of the development models, and also the most restrictive.
To use the IObservable/IObserver Development Model you must:

       Implement an IObservable class for the data/event producer.
       Implement an IObserver class for the data/event consumer.


Task 1 – Reviewing an Existing WinForm Project
    1. Start Microsoft Visual Studio 2008 from Start | All Programs | Microsoft Visual Studio 2008.
2. In the File menu, choose Open and then Project/Solution. In the Open Project dialog, browse to
   Ex1-DevelopingSimpleStreamInsightApp\Begin in the Source folder of this lab, select Begin.sln
   and click Open.
3. Open the Form1 in the Designer. To do this, in the Solution Explorer double-click the Form1.cs
   file.




   Figure 1
   Exploring the Form1 form in the Designer


4. Review the code of the Form1 class. To do this, in the Solution Explorer, right-click the Form1.cs
   file and select View Code.
5. Notice that the ClickMeButton_Click event handler updates the Text property of the
   clickEventTextBox textbox.
   C#
   private void ClickMeButton_Click(object sender, EventArgs e)
   {
       var clickedButton = (Button)sender;

       clickEventTextBox.Text = clickEventTextBox.Text +
   clickedButton.Name + " " + DateTime.Now.ToLongTimeString() + "\r\n";
   }



6. Start the application in the debugger by pressing F5.
7. Click the Click Me button, and you will notice that the name of the button control and the time
   when was clicked is displayed in the textbox.
        Figure 2
        Clicking the Click Me button



          Note: By clicking the Click Me button, the ClickMeButton_Click method is called to handle the
          event to write the button name, the date and time to the textbox. This is a very simple event
          stream that we can easily control for a demo. In the next Task, we will enhance the project
          using StreamInsight and a couple of pre-existing adapters.



    8. Close the form to stop debugging.

Task 2 – Adding a Simple StreamInsight Query
What we would like to do now is to enhance the application by taking advantage of StreamInsight and
perform some click stream analysis. Before we write a complex query, we are going to re-write the
existing code to use StreamInsight instead of the simple event handler. The project has a number of
additional classes to help us write our first StreamInsight application. We will get to those classes later in
this lab, but first let us get this application to use those classes.
    1. In the Solution Explorer, right-click the Form1.cs file and select View Code.
    2. Add the following using statements at the top of the Form1.cs file.
        C#
        using StreamInsight.WinFormDemo.EventTypes;
        using Microsoft.ComplexEventProcessing.Linq;
        using Microsoft.ComplexEventProcessing;
3. Scroll to the top of the class and you will see the two static properties added to Form1. They will
   be used by the input and output adapters and are initialized in the Form1 constructor.
   C#
   public partial class Form1 : Form
   {
       internal static readonly Dictionary<string, Control> InputsControls
           = new Dictionary<string, Control>();
       internal static readonly Dictionary<string, Form1> Forms
           = new Dictionary<string, Form1>();



       Note: We cannot pass the controls directly into or out of adapters by way of the configuration
       classes because the configuration classes must be XML Serializable.



4. Review the default constructor of the Form1 class. The clickMeButton button is added to the
   InputControls dictionary and the Form1 instance is added to the Forms dictionary.
   C#
   public Form1()
   {
       InitializeComponent();

          try
          {
                Form1.InputsControls.Add(clickMeButton.Name, clickMeButton);
                Form1.Forms.Add("me", this);
          }
          catch (Exception e)
          {
          }
   }



5. In the Form1 constructor, add the following bolded code, which passes to the input adapter the
   name of the control that we are going to be listening for click events, and to the output adapter,
   the name of the textbox where we want to write the details of the event.
   C#
   public Form1()
   {
       InitializeComponent();

          try
          {
                Form1.InputsControls.Add(clickMeButton.Name, clickMeButton);
                Form1.Forms.Add("me", this);

                var inputConfig = new MouseClickInputConfig
                    {
                        InputControlName = clickMeButton.Name
                    };
                var outputConfig = new MouseClickOutputConfig { };
          }
          catch (Exception e)
          {
          }
   }



6. Next, create an instance of the input stream (the adapter will be created implicitly from the
   definition of the streams). The Input Stream is created using the Create method of the generic
   class, CepStream with a generic type parameter of MouseClickEvent, and regular parameters of
   the stream name, the Input Adapter Factory type which is MouseClickInputFactory, the
   instance of the InputConfig (inputConfig) and the Event Shape(EventShape.Point).

       Adapter Configurations: Input and Output Adapters are created by way of methods that utilize
       AdapterFactory classes and their associated adapter configuration classes. The configuration
       class enables the Adapters to be decoupled from the configuration information. Configuration
       classes do not need to inherit from special classes or an interface, but must be XML
       Serializable.
       You can create typed and un-typed adapters, which inherit from different base classes.
       Creating Adapters will be covered in the next exercises.



   C#
   public Form1()
   {
       InitializeComponent();

          try
          {

                Form1.InputsControls.Add(clickMeButton.Name, clickMeButton);
                Form1.Forms.Add("me", this);

                var inputConfig = new MouseClickInputConfig
                    {
                        InputControlName = clickMeButton.Name
                    };
                var outputConfig = new MouseClickOutputConfig { };
                var inputStream =
                    CepStream<MouseClickEvent>
                        .Create(
                             "inputstream",
                             typeof(MouseClickInputFactory),
                             inputConfig,
                             EventShape.Point);
          }
          catch (Exception e)
          {
          }
   }



       Note: In this example, we will keep it simple and use the Implicit Server Model.



7. Next, write the following query template shown in bold. The query template is created by
   writing a LINQ expression. In this case, just select all the items e from the inputStream. In a real
   world application you would probably do something a lot more complex, filtering the event
   stream, applying aggregations, or just projecting it to a new shape.

       Query Templates: Queries are built using LINQ in .NET Framework 3.5. LINQ is a language
       platform that enables you to express declarative computation over sets in a manner that is
       fully integrated into the host language. Query templates continuously analyze and process
       events in the CEP Server using input adapters. With LINQ based queries you can describe how
       to:
       Project the events into new shapes
       Filter Events
       Group Events
       Window Events over Time - enables grouping events over time
       Aggregation
       Identifying TOP N candidates
       Join events from multiple streams.
       Create User Defined Functions.



   C#
   public Form1()
   {
       InitializeComponent();
        try
        {

              Form1.InputsControls.Add(clickMeButton.Name, clickMeButton);
              Form1.Forms.Add("me", this);

              var inputConfig = new MouseClickInputConfig
                  {
                      InputControlName = clickMeButton.Name
                  };
              var outputConfig = new MouseClickOutputConfig { };

              var inputStream =
                  CepStream<MouseClickEvent>
                      .Create(
                          "inputstream",
                          typeof(MouseClickInputFactory),
                          inputConfig,
                          EventShape.Point);

              CepStream<MouseClickEvent> filter = from e in inputStream
                                                  select e;

        }
        catch (Exception e)
        {
        }
   }
   select e;



8. Now that the we have created the query template, we need to create an instance of the query.
   To do this, create a class level readonly field of type Query named _eventQuery as shown in the
   following bolded code.
   C#
   public partial class Form1 : Form
   {
       internal static readonly Dictionary<string, Control> InputsControls
           = new Dictionary<string, Control>();
       internal static readonly Dictionary<string, Form1> Forms
           = new Dictionary<string, Form1>();

        private readonly Query _eventQuery;
9. Go back to the Form1 constructor and create an instance of the query. You can do this by using
   ToQuery extension method, passing in the instance of the type that represents the Output
   Adapter's Factory class, in this case MouseClickOutputFactory, the instance of the Output
   Configuration (outputConfig), the Event Shape (EventShape.Point), and the event stream order,
   (StreamEventOrder.FullyOrdered).

     Query Binding: When you bind a query to instances of input (producer) and output (consumer)
     adapters, you register a query template instance in a CEP Server. As events are consumed by
     the CEP Server they are processed by the input adapters and processed by the current query
     instances (the standing queries), which then emit the output events to the output adapters.
     The queries can be started, stopped and managed.



   C#
   public Form1()
   {
       InitializeComponent();

        try
        {

              Form1.InputsControls.Add(clickMeButton.Name, clickMeButton);
              Form1.Forms.Add("me", this);

              var inputConfig = new MouseClickInputConfig
                  {
                      InputControlName = clickMeButton.Name
                  };
              var outputConfig = new MouseClickOutputConfig { };

              var inputStream =
                  CepStream<MouseClickEvent>
                      .Create(
                          "inputstream",
                          typeof(MouseClickInputFactory),
                          inputConfig,
                          EventShape.Point);

              CepStream<MouseClickEvent> filter = from e in inputStream
                                                  select e;

              _eventQuery = filter
                  .ToQuery(
                      typeof(MouseClickOutputFactory),
                      outputConfig,
                      EventShape.Point,
                      StreamEventOrder.FullyOrdered);
         }
         catch (Exception e)
         {
         }
   }



10. Start the _eventQuery instance. To do this, add the following bolded coded that calls the its
    Start method.
   C#
   public Form1()
   {
       InitializeComponent();

         try
         {

               Form1.InputsControls.Add(clickMeButton.Name, clickMeButton);
               Form1.Forms.Add("me", this);

               var inputConfig = new MouseClickInputConfig
                   {
                       InputControlName = clickMeButton.Name
                   };
               var outputConfig = new MouseClickOutputConfig { };

               var inputStream =
                   CepStream<MouseClickEvent>
                       .Create(
                           "inputstream",
                           typeof(MouseClickInputFactory),
                           inputConfig,
                           EventShape.Point);

               CepStream<MouseClickEvent> filter = from e in inputStream
                                                   select e;

               _eventQuery = filter
                   .ToQuery(
                       typeof(MouseClickOutputFactory),
                       outputConfig,
                       EventShape.Point,
                       StreamEventOrder.FullyOrdered);

               _eventQuery.Start();
         }
         catch (Exception e)
             {
             }
        }



    11. The query has now been defined and started, but now we should gracefully stop the query
        when the Form1 closes. To do this, go to the FormClosing event handler (Form1_FormClosing)
        and execute the Stop method on the _eventQuery class level field.
        C#
        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            _eventQuery.Stop();
        }




Task 3 – Updating the Textbox
Now we have to write the events that satisfy our query to the clickEventTextBox textbox. Unlike our
original, Winforms eventing version, the CEP version of the application is multithreaded. The CEP Server
processes on its own thread. So the Output Adapter cannot just write to the Text property of the
clickEventTextBox textbox. Instead, the OutputAdapter has a MouseEventAdded event, but because we
never get access to the instance of the Output Adapter that is bound to our Query, the Output Adapter
will wire up the MouseEventAdded event to the OutputAdapter_OnMouseEventAdded method of
Form1. From there, we can sync to the UI thread using the Invoke method of the Form class (which is
the standard way to do multithreading in WinForms). The Invoke method takes a delegate and an array
of objects as parameters.
    1. In the Form1 class create a private delegate named UIUpdaterDelegate with one parameter of
       type EventAdapters.MouseEventAddedEventArgs named args. To do this, paste the following
       bolded code at the top of the class body.
        C#
        public partial class Form1 : Form
        {
            internal static readonly Dictionary<string, Control> InputsControls
                = new Dictionary<string, Control>();
            internal static readonly Dictionary<string, Form1> Forms
                = new Dictionary<string, Form1>();

             private readonly Query _eventQuery;
             delegate void UIUpdaterDelegate(
                 EventAdapters.MouseEventAddedEventArgs args);
2. Make sure that the Form1 class has an internal method that returns void, called
   OutputAdapter_OnMouseEventAdded with two parameters, sender which is of type Object
   and arg which is of type MouseEventAddedEventArgs.
   C#
   internal void OutputAdapter_OnMouseEventAdded(object sender,
   MouseEventAddedEventArgs arg)
   {

   }



3. Create a protected method that returns void called UpdateUI that takes one parameter of type
   MouseEventAddedEventArgs called arg. Inside it, append to the Text property of the
   clickEventTextBox textbox, the arg.ControlName, the ToLongTimeString format of the arg's
   EventFiredDateTime propery, a carriage return character and a new line character.
   C#
   protected void UpdateUI(MouseEventAddedEventArgs arg)
   {
       clickEventTextBox.Text = clickEventTextBox.Text +
                       arg.ControlName +
                       " " +
                       arg.EventFiredDateTime.ToLongTimeString() + "\r\n";
   }



4. Update the code in the OutputAdapter_OnMouseEventAdded method to create an instance of
   of the UIUpdaterDelegate delegate and set it to the UpdateUI method. Then execute the
   Invoke method of the Form passing the created delegate and a new array of objects initialized
   with the arg parameter.
   C#
   internal void OutputAdapter_OnMouseEventAdded(object sender,
   MouseEventAddedEventArgs arg)
   {
       UIUpdaterDelegate updateUI = UpdateUI;
       this.Invoke(updateUI, new object[] { arg });
   }



5. We no longer need the code in the original event handler, so now go back to the
   ClickMeButton_Click method and comment out the code to update the clickEventTextBox
   textbox.
   C#
   private void ClickMeButton_Click(object sender, EventArgs e)
       {
             //var clickedButton = (Button) sender;
             //clickEventTextBox.Text = clickEventTextBox.Text +
             //clickedButton.Name + " " + DateTime.Now.ToLongTimeString()
             //+ "\r\n";
       }




Task 4 – Testing the Code
Now we can test the updated version of the application.
    1. Start the application in the debugger by pressing F5.
    2. The clickEventTextBox textbox should have one row in it with that begins with "Start Event" and
       then the date and time the application started.




       Figure 3
       Form1 Initial State


    3. Click the Click Me button. The clickEventTextBox textbox should be updated to include a new
       row that begins with "clickMeButton" and the date and time of the click event.
   Figure 4
   Form1 after clicking Click Me button


4. Click the Click Me button a couple more times, and a new row should be create in the
   clickEventTextBox textbox for each click.




   Figure 5
   Form1 After Button has been clicked a couple times


5. Close the form to stop debugging.
Exercise 2: Enhancing the StreamInsight
Query
In this exercise, we will enhance the query from the previous exercise, and demonstrate some of the
power of StreamInsight. Instead of just selecting all the mouse click events, we will we write two
different aggregation queries. For the first one, we will count how many mouse click events occur during
two-second intervals. We can do this by just changing a couple parts of the StreamInsight LINQ query.
Task 1 – Creating a Tumbling Window Query
    1. In the File menu, choose Open and then Project/Solution. In the Open Project dialog, browse
       to Ex2-EnhancingTheStreamInsightQuery\Begin in the Source folder of this lab, select Begin.sln
       and click Open.
    2. Open the code of the Form1 class. To do this, right-click the Form1.cs file in the Solution
       Explorer and select View Code.
    3. In the default constructor of the Form1 class, change the outputConfig local variable so that it
       creates a new instance of MouseClickCountOutputConfig class instead of the
       MouseClickOutputConfig class.
        C#
        //…
        try
        {
              Form1.InputsControls.Add(clickMeButton.Name, clickMeButton);
              Form1.Forms.Add("me", this);

              var inputConfig = new MouseClickInputConfig
                  { InputControlName = clickMeButton.Name };

              //var outputConfig = new MouseClickOutputConfig {};
              var outputConfig = new MouseClickCountOutputConfig {};

              var inputStream =
                  CepStream<MouseClickEvent>.Create(
                      "inputstream",
                      typeof(MouseClickInputFactory),
                      inputConfig,
                      EventShape.Point);

              CepStream<MouseClickEvent> filter = from e in inputStream
                                                  select e;

              _eventQuery = filter.ToQuery(
             typeof(MouseClickOutputFactory),
             outputConfig, EventShape.Point, StreamEventOrder.FullyOrdered);

       _eventQuery.Start();
   }
   catch (Exception e)
   {
   }



4. Now update the query template so that it counts the number of events that occur during
   consecutive two-second windows in time, and returns a new instance of the class
   MouseClickCountEvent, setting the Count property to Window.Count (using the
   TumblingWindow extension method to create consecutive windows of a fixed period). To do
   this, replace the original definition of the filter query template for the following bolded code.

     Understanding Hopping and Tumbling Windows: It is common in CEP applications to have to
     perform calculations over subsets of events that fall within a given time frame. These subsets
     are called Windows. StreamInsight supports two types of Windows, Hopping and Snapshot.
     Hopping Windows are consecutive windows that hop forward in time by a fixed period. The
     Hopping Window has tow time spans, the hop size (the time span in the window where an
     event is considered part of that window) and window size (time span between the start time
     of consecutive windows). If the hop size and the window size are the same (so that there are
     no gaps between hops), it is a special type of a hopping window known as a Tumbling
     Window.



   C#
   //…
   try
   {
         Form1.InputsControls.Add(clickMeButton.Name, clickMeButton);
         Form1.Forms.Add("me", this);

         var inputConfig = new MouseClickInputConfig
         { InputControlName = clickMeButton.Name };
         var outputConfig = new MouseClickCountOutputConfig {};

         var inputStream =
             CepStream<MouseClickEvent>.Create(
                 "inputstream",
                 typeof(MouseClickInputFactory),
                 inputConfig,
                 EventShape.Point);

         // CepStream<MouseClickEvent> filter = from e in inputStream
         //                                      select e;
         CepStream<MouseClickCountEvent> filter =
            from window in inputStream.TumblingWindow(TimeSpan.FromSeconds(2))
            select new MouseClickCountEvent
                {
                    Count = window.Count()
                };

         _eventQuery = filter.ToQuery(
             typeof(MouseClickOutputFactory),
             outputConfig, EventShape.Point, StreamEventOrder.FullyOrdered);

         _eventQuery.Start();
   }
   catch (Exception e)
   {
   }



5. The output of the query is now significantly different than what we used before. Instead of a
   return type of MouseClickEvent we are creating a MouseClickCountEvent. The EventShape has
   also changed from a Point shape to an Interval shape. Therefore, we need to update the code
   that creates the instance of the _eventQuery query. To do this, replace the following bolded
   code.
   C#
   //…
   try
   {
         Form1.InputsControls.Add(clickMeButton.Name, clickMeButton);
         Form1.Forms.Add("me", this);

         var inputConfig = new MouseClickInputConfig
             { InputControlName = clickMeButton.Name };
         var outputConfig = new MouseClickCountOutputConfig {};

         var inputStream =
             CepStream<MouseClickEvent>.Create(
                 "inputstream",
                 typeof(MouseClickInputFactory),
                 inputConfig,
                 EventShape.Point);

         CepStream<MouseClickCountEvent> filter =
           from window in inputStream.TumblingWindow(TimeSpan.FromSeconds(2))
           select new MouseClickCountEvent
               {
                   Count = window.Count()
                    };

            // _eventQuery = filter.ToQuery(
            //      typeof(MouseClickOutputFactory),
            //      outputConfig, EventShape.Point, StreamEventOrder.FullyOrdered);
            _eventQuery = filter.ToQuery(
                typeof(MouseClickCountOutputFactory),
                outputConfig,
                EventShape.Interval,
                StreamEventOrder.FullyOrdered);

           _eventQuery.Start();
       }
       catch (Exception e)
       {
       }




Task 2 – Testing Tumbling Window Query
Now we can test the version of the application with the Tumbling Window query.
    1. Start the application in the debugger by pressing F5.
    2. Click the Click Me button once, and wait two seconds.
    3. Nothing should be displayed yet. Click the Click Me button twice, and the first result should
       appear with a count of 1.
    4. Wait two more seconds and click the Click Me button once. A second result should appear with
       a count of 2.
    5. Wait two more seconds and click the Click Me button once more. A third result should appear
       with a count of 1.
        Figure 6
        Testing Tumbling Window Query



         Note: Because of the windowing semantics in StreamInsight, windows without events are not
         created, so you will not see a result with a count of zero. A window is not closed until a new
         event triggers the closure of the window, so in this case it will not display the result until a click
         comes in for the next tumbling window. This happens by design.




Task 3 – Creating a Snapshot Window Query
Now that we have created a tumbling window query, we are going to change it to the second type of
window query, a Snapshot Window Query. Instead of counting the number of clicks in a repeating two-
second interval, we want to query for when two or more clicks occur within two-seconds of a click. The
big difference here is that the starting time of each window is the time at which the click occurs, and not
a fixed interval like in the tumbling window. To accomplish this will require two query streams. The first
query (we will call the slidingCount which is a Snapshot query) will change the duration of the event to
two seconds and then create a Snapshot window. The second query statement uses the slidingCount
stream, filters out a window with a count below two.
    1. Make sure that the try-catch part of the Form1 constructor looks like the following one.
        C#
        //…
        try
        {
              Form1.InputsControls.Add(clickMeButton.Name, clickMeButton);
              Form1.Forms.Add("me", this);
         var inputConfig = new MouseClickInputConfig
             { InputControlName = clickMeButton.Name };
         var outputConfig = new MouseClickCountOutputConfig {};

         var inputStream =
             CepStream<MouseClickEvent>.Create(
                 "inputstream",
                 typeof(MouseClickInputFactory),
                 inputConfig,
                 EventShape.Point);

         CepStream<MouseClickCountEvent> filter =
           from window in inputStream.TumblingWindow(TimeSpan.FromSeconds(2))
           select new MouseClickCountEvent
               {
                   Count = window.Count()
               };

         _eventQuery = filter.ToQuery(
             typeof(MouseClickCountOutputFactory),
             outputConfig,
             EventShape.Interval,
             StreamEventOrder.FullyOrdered);

       _eventQuery.Start();
   }
   catch (Exception e)
   {
   }



2. Comment out the Tumbling Window query as shown in the following code.
   C#
   //…
   try
   {
         Form1.InputsControls.Add(clickMeButton.Name, clickMeButton);
         Form1.Forms.Add("me", this);

         var inputConfig = new MouseClickInputConfig
             { InputControlName = clickMeButton.Name };
         var outputConfig = new MouseClickCountOutputConfig {};

         var inputStream =
             CepStream<MouseClickEvent>.Create(
                 "inputstream",
                 typeof(MouseClickInputFactory),
                  inputConfig,
                  EventShape.Point);

         //CepStream<MouseClickCountEvent> filter =
         //from window in inputStream.TumblingWindow(TimeSpan.FromSeconds(2))
         //select new MouseClickCountEvent
         //    {
         //        Count = window.Count()
         //    };

         _eventQuery = filter.ToQuery(
             typeof(MouseClickCountOutputFactory),
             outputConfig,
             EventShape.Interval,
             StreamEventOrder.FullyOrdered);

         _eventQuery.Start();
   }
   catch (Exception e)
   {
   }



3. Create a new Snapshot query, called slidingCount, which alters the duration by two seconds
   and returning the Snapshot. To do this, paste the following bolding code inside the constructor
   of the Form1 class.
   C#
   //…
   try
   {
         Form1.InputsControls.Add(clickMeButton.Name, clickMeButton);
         Form1.Forms.Add("me", this);

         var inputConfig = new MouseClickInputConfig
             { InputControlName = clickMeButton.Name };
         var outputConfig = new MouseClickCountOutputConfig {};

         var inputStream =
             CepStream<MouseClickEvent>.Create(
                 "inputstream",
                 typeof(MouseClickInputFactory),
                 inputConfig,
                 EventShape.Point);

         //CepStream<MouseClickCountEvent> filter =
         //from window in inputStream.TumblingWindow(TimeSpan.FromSeconds(2))
         //select new MouseClickCountEvent
         //    {
         //         Count = window.Count()
         //    };
         CepStream<MouseClickCountEvent> slidingCount =
             from window in inputStream
                  .AlterEventDuration(e => TimeSpan.FromSeconds(2))
                  .Snapshot()
             select new MouseClickCountEvent { Count = window.Count() };

         _eventQuery = filter.ToQuery(
             typeof(MouseClickCountOutputFactory),
             outputConfig,
             EventShape.Interval,
             StreamEventOrder.FullyOrdered);

       _eventQuery.Start();
   }
   catch (Exception e)
   {
   }



4. Now write a query called filter that only returns windows with a Count > 1. To do this, paste the
   following bolding code inside the constructor of the Form1 class.
   C#
   //…
   try
   {
         Form1.InputsControls.Add(clickMeButton.Name, clickMeButton);
         Form1.Forms.Add("me", this);

         var inputConfig = new MouseClickInputConfig
             { InputControlName = clickMeButton.Name };
         var outputConfig = new MouseClickCountOutputConfig {};

         var inputStream =
             CepStream<MouseClickEvent>.Create(
                 "inputstream",
                 typeof(MouseClickInputFactory),
                 inputConfig,
                 EventShape.Point);

         //CepStream<MouseClickCountEvent> filter =
         //from window in inputStream.TumblingWindow(TimeSpan.FromSeconds(2))
         //select new MouseClickCountEvent
         //    {
         //        Count = window.Count()
         //    };
         CepStream<MouseClickCountEvent> slidingCount =
                 from window in inputStream
                     .AlterEventDuration(e => TimeSpan.FromSeconds(2))
                     .Snapshot()
                 select new MouseClickCountEvent { Count = window.Count() };

            CepStream<MouseClickCountEvent> filter =
                from e in slidingCount
                where e.Count > 1
                select e;

            _eventQuery = filter.ToQuery(
                typeof(MouseClickCountOutputFactory),
                outputConfig,
                EventShape.Interval,
                StreamEventOrder.FullyOrdered);

            _eventQuery.Start();
       }
       catch (Exception e)
       {
       }




Task 4 – Testing Snapshot Window Query
Now we can test the version of the application with the Snapshot Window query.
    1. Start the application in the debugger by pressing F5.
    2. Click the Click Me button once, and wait at least two seconds and click it again. Nothing should
       be displayed.
    3. Wait at least two seconds and click the button twice, and then wait at least two seconds and
       click it again. A result with a count of two should be displayed.
    4. Now click the button four times in a row, wait at least two seconds and click the button again.
       The follow five results will be displayed
        Figure 7
        Testing Snapshot Window Query



         Note: Snapshot windows are defined according to the unique interval time stamps that are
         associated with events in the stream, instead of a fixed grid along the timeline. The size and
         time period of the window is defined only by the events in the stream. For each pair of closest
         event endpoints (start time and end time), a snapshot window is created. By this definition,
         there is neither event start time nor end that does not fall on a window boundary. That is,
         snapshot windows divide the timeline according to all occurring changes.




Exercise 3: Developing StreamInsight
Adapters
In this exercise we will build the input and output adapters used in the first example. But, before you
can start building an event adapter, it is important to understand how the adapters work with the CEP
Server. The first thing we need to review is how events flow thru StreamInsight's CEP Server.


StreamInsight Event Flow
Figure 8 describes the flow of events, starting at the event source, thru the input adapter, to the CEP
Server (where the queries are processed), and to the output adapter.
Figure 8
StreamInsight Event Flow



Input Adapter
The job of the input adapter is to read the events from the event source (such as a database, a file, a
COM port, or just listening as an event handler), translate them into an event type, and then
write(enqueue) them to the CEP Server. Each input adapter must also define the event model , which
determines the event shape and temporal characteristics.
As we learned in the introduction of the Exercise 1 of this lab, there are three types of Event Models
(Point, Interval and Edge) and two types of Event Kinds (Insert and Current Time Increment) in
StreamInsight
For the Insert event type, you need to decide if you want to use a strongly typed payload. If there is only
going to be one type of event in the stream with the same properties (shape), then a strongly typed
payload is the best option. With a typed payload, you get all the benefits of strongly typed development.
If you do not have a known structure, or there are multiple types of events in the stream, then an un-
typed payload is required. An un-typed payload requires the adapter to be written so that the type of
payload can be derived from the configuration information.

Input Adapter Base Classes
Once you have chosen the event type and event model, you can select the correct base class for the
input adapter.

Adapter Type                                          Input Adapter Base Class

Typed point                                           TypedPointInputAdapter

Untyped point                                         PointInputAdapter
Typed interval                                       TypedIntervalInputAdapter

Untyped interval                                     IntervalInputAdapter

Typed edge                                           TypedEdgeInputAdapter

Untyped edge                                         EdgeInputAdapter


Input Adapter Factory
The Adapter Factory helps to decouple the configuration information from the Adapter implementation.
This allows the developers to create one Factory that can instantiate different adapters based on the
information passed in as configuration information.
Input Adapter Factories must implement at least one of the follow interfaces:
        ITypedInputAdapterFactory<TConfigInfo>: should be used for Typed Input Adapters.
        IInputAdapterFactory<TConfigInfo>: should be used for Un-Typed Input Adapters.

Output Adapter
The job of the output adapter is to read (dequeue) the events from the CEP Server, translate them into
an output format, and then write it to the output source. Output adapters can either write the output
directly (like to a database, port, or device), or publish a callback method (event).
Output Adapters have the same three Event Models as Input Adapters (Point, Interval, and Edge). Just
like Input Adapters, the Output Adapter Type is determined by what Event Model the Output Adapter
can consume.

Output Adapter Base Classes
Once you have chosen the event type and event model, you can select the correct base class for the
output adapter.

Adapter Type                                         Input Adapter Base Class

Typed point                                          TypedPointOutputAdapter

Untyped point                                        PointOutputAdapter

Typed interval                                       TypedIntervalOutputAdapter

Untyped interval                                     IntervalOutputAdapter

Typed edge                                           TypedEdgeOutputAdapter

Untyped edge                                         EdgeOutputAdapter


Output Adapter Factory
Just like in the Input Adapter Factory, the Output Adapter Factory helps to decouple the configuration
information from the Adapter implementation.
Output Adapter Factories must implement at least one of the follow interfaces:

       ITypedOutputAdapterFactory<TConfigInfo>: should be used for Typed Output Adapters.
       IOutputAdapterFactory<TConfigInfo>: should be used for Un-Typed Output Adapters.

Adapter Machine State
Both the Input and Output Adapters have the same interactions with the CEP Server that can be
explained with the help of Figure 9.
Figure 9
StreamInsight Adapter State Machine


The CEP Server will execute the following Adapter methods:
       Start: Lets the adapter execute its startup process.
       Resume: Lets the adapter resume its process.
       Dispose: Cleans up any adapter resources.
The Adapter calls the CEP Server by way of the following methods implemented in the Adapter base
class:

       Enqueue: For the input adapter, which returns the values EnqueueOperationResult.Success or
        EnqueueOperationResult.Full.
       Dequeue: For the output adapter, which returns the values DequeueOperationResult.Success or
        DequeueOperationResult.Empty.
       Ready: which returns a boolean value TRUE or FALSE.
       Stopped: which returns a boolean value TRUE or FALSE.

Adapters and Threading
The CEP Server never shares the same thread as the adapter (and the CEP Server does not have to be
even running in the same process, but that is beyond the scope of this lab). The CEP Server will obtain a
worker thread on behalf of the adapter, and call the Start and Resume methods using it. It is the
adapter’s responsibility to use the thread appropriately.


Task 1 – Creating the Input Event Payload Definition
Now that we have a basic understanding of the different aspects of Input and Output Adapters, it is time
to actually write one of each. We will start with a version of the same application used in Exercise 1, but
without containing the input and output adapter code. It only contains the form and the EventAdapter
and EventTypes folders.
For this exercise, we will know that the input event stream that we want to send thru the CEP Server is
made up of the click events from the Click Me button. The original mouse click events need to be
transformed into another type that will be sent into the CEP Server. We have a very limited amount of
data that we can derive from the mouse event. We could get data about the user, but to keep this
exercise simple, the payload for this event is just going to be the name of the control associated with the
click event and the time the event was fired.
1. In the File menu, choose Open and then Project/Solution. In the Open Project dialog, browse
   to Ex3-DevelopingStreamInsightAdapters\Begin in the Source folder of this lab, select
   Begin.sln and click Open.
2. Add a new class called MouseClickEvent to the project. To do this, in Solution Explorer right-
   click the EventTypes folder of the StreamInsight.WritingAdapters project, point to Add, and
   then click Class. Type MouseClickEvent.cs in the Name text box and then click Add. The
   MouseClickEvent.cs file should be added to the project and opened in the class editor.




   Figure 10
   Adding the MouseClickEvent class to the StreamInsight.WritingAdapters project


3. Update the MouseClickEvent class definition to make it public and add the ControlName and
   EventDateTime properties. To do this, replace the code in the MouseClickEvent.cs file with the
   following one:
   C#
   using System;

   namespace StreamInsight.WritingAdapters.EventTypes
   {
       public struct MouseClickEvent
       {
           public string ControlName { get; set; }
           public DateTime EventDateTime { get; set; }
       }
        }




Task 2 – Creating the Input Configuration Class
Before we can create the Input Adapter, we first have to create the Input Configuration class. Each
instance of the input adapter needs to listen to the click event of the associated control, but since each
property in the Input Adapter Configuration must be XML serializable, we cannot just pass in the control.
So instead, we will pass in the name of the control and, in the adapter, we will use this value to wire up
the event listener to the control. Therefore, the class only needs one property, InputControlName, with
a setter and getter.
    1. Add a new class called MouseClickInputConfig to the project. To do this, in Solution Explorer
       right-click the EventAdapters folder of the StreamInsight.WritingAdapters project, point to
       Add, and then click Class. Type MouseClickInputConfig.cs in the Name text box and then click
       Add. The MouseClickInputConfig.cs file should be added to the project and opened in the class
       editor.




        Figure 11
        Adding the MouseClickInputConfig class to the StreamInsight.WritingAdapters project


    2. Update the MouseClickInputConfig class definition to make it public and add the
       InputControlName property. To do this, replace the code in the MouseClickInputConfig.cs file
       with the following one:
       C#
       namespace StreamInsight.WritingAdapters.EventAdapters
       {
           public class MouseClickInputConfig
           {
               public string InputControlName { get; set; }
           }
       }




Task 3 – Creating the Input Adapter Class
Now we can actually create the Input Adapter Class, which we will call MouseClickPointInput. Since
there will be one event type, we can make this a strongly typed adapter by inheriting from the
TypedPointInputAdapter<T> class.
    1. Add a new class called MouseClickPointInput to the project. To do this, in Solution Explorer
       right-click the EventAdapters folder of the StreamInsight.WritingAdapters project, point to
       Add, and then click Class. Type MouseClickPointInput.cs in the Name text box and then click
       Add. The MouseClickPointInput.cs file should be added to the project and opened in the class
       editor.




       Figure 12
       Adding the MouseClickPointInput class to the StreamInsight.WritingAdapters project


    2. Add the following using statements at the top of the MouseClickPointInput.cs file:
   C#
   using   System.Windows.Forms
   using   Microsoft.ComplexEventProcessing
   using   Microsoft.ComplexEventProcessing.Adapters
   using   StreamInsight.WritingAdapters.EventTypes



3. Update the MouseClickPointInput class to make it public and inherit from the
   TypedPointInputAdapter<MouseClickEvent> abstract class.
   C#
   using   System;
   using   System.Collections.Generic;
   using   System.Linq;
   using   System.Text;
   using   System.Windows.Forms;
   using   Microsoft.ComplexEventProcessing;
   using   Microsoft.ComplexEventProcessing.Adapters;
   using   StreamInsight.WritingAdapters.EventTypes;

   namespace StreamInsight.WritingAdapters.EventAdapters
   {
       public class MouseClickPointInput :
           TypedPointInputAdapter<MouseClickEvent>
       {
       }
   }



4. Add three attributes to the MouseClickPointInput class to hold a reference to the control we are
   listening to, a pending event and a pending CTI event timestamp. To do this, paste the following
   bolded code inside the MouseClickPointInput class.
   C#
   public class    MouseClickPointInput : TypedPointInputAdapter<MouseClickEvent>
   {
       private    readonly Control _observedControl;
       private    PointEvent<MouseClickEvent> _pendingEvent;
       private    DateTime? _pendingCtiTime = null;

   }



5. Implement the Start and Resume abstract methods from the base class. To do this, paste the
   following bolded code inside the MouseClickPointInput class.
   C#
   public class MouseClickPointInput : TypedPointInputAdapter<MouseClickEvent>
   {
          private readonly Control _observedControl;
          private PointEvent<MouseClickEvent> _pendingEvent;
          private DateTime? _pendingCtiTime = null;

          public override void Start()
          {
          }

          public override void Resume()
          {
          }

   }



6. Create a constructor for the MouseClickPointInput class that receives a MouseClickInputConfig
   instance as a parameter. To do this, add the following constructor shown in bold.
   C#
   public class MouseClickPointInput : TypedPointInputAdapter<MouseClickEvent>
   {
       private readonly Control _observedControl;
       private PointEvent<MouseClickEvent> _pendingEvent;
       private DateTime? _pendingCtiTime = null;

          public MouseClickPointInput(MouseClickInputConfig config)
          {
              _observedControl = Form1.InputsControls[config.InputControlName];
          }

          public override void Start()
          {
          }

          public override void Resume()
          {
          }

   }



       Note: Since we will be creating a factory class to instantiate the adapter, we can pass an
       instance of the MouseClickInputConfig into the constructor. We will then use the
       InputControlName property to get the instance of the control from the static internal property
       InputsControls in the Form1 class.
7. We have now all the information we need to implement the Adapter State Machine for this
   Input Adapter. The first method that will be called by the CEP Server after it is instantiated is the
   Start method. What we want to do is to wire up the MouseClick event of the obserevedControl
   control to an event handler called observedControl_MouseClick, so let us stub out that method,
   as shown in the following bolded code, and fill it out later.
   C#
   public class MouseClickPointInput : TypedPointInputAdapter<MouseClickEvent>
   {
       private readonly Control _observedControl;
       private PointEvent<MouseClickEvent> _pendingEvent;
       private DateTime? _pendingCtiTime = null;

        public MouseClickPointInput(MouseClickInputConfig config)
        {
            _observedControl = Form1.InputsControls[config.InputControlName];
        }

        public override void Start()
        {
        }

        public override void Resume()
        {
        }

        void observedControl_MouseClick(object sender, MouseEventArgs e)
        {
        }
   }



8. Just for this lab purpose, in the Start method we want to write out an event to the CEP Server to
   let us know that the Adapter has started. We can do this by putting the message "Start Event" in
   to the ControlName property of the CEP Event. First, we need to create an instance of the
   correct Event Type. We do this by calling the CreateInsertEvent method of the
   TypedPointInputAdapter class that we inherited from. We can then set the time, and then
   create an instance of the Payload and set that to the newly created event. Now that the event
   instance is completed we can write it to the CEP Server by using the inherited Enqueue method.
   Because there will be no other events related to the event we just wrote to the CEP Server, we
   will can write a Current Time Increment (CTI) Event (this lets the CEP Server that there will be no
   subsequent incoming INSERT events that will revise the event history before the CTI
   timestamp).
   C#
   public override void Start()
   {
        _observedControl.MouseClick += observedControl_MouseClick;
        var result = EnqueueCtiEvent(DateTime.Now);

        PointEvent<MouseClickEvent> evt = CreateInsertEvent();
        evt.StartTime = DateTime.Now;
        evt.Payload = new MouseClickEvent
        {
            ControlName = "Start Event"
        };
        Enqueue(ref evt);
        EnqueueCtiEvent(DateTime.Now);
   }



   The event adapter is now ready to be called whenever a user clicks the control. The method
   observedControl_MouseClick will be called, so that is where we will have to put the code to
   write to the CEP Server.
9. Implement the observedControl_MouseClick method. To do this, paste the following bolded
   code inside the method definition.
   C#
   void observedControl_MouseClick(object sender, MouseEventArgs e)
   {
       DateTime currctitime = default(DateTime);
       EnqueueOperationResult result = EnqueueOperationResult.Full;
       Control observedControl = sender as Control;
       PointEvent<MouseClickEvent> currEvent =
           CreateCepEventFromMouseClick(observedControl, e);

        try
        {
              if (AdapterState.Stopped == AdapterState)
              {
                  return;
              }

              if (AdapterState.Stopping == AdapterState)
              {
                  if (_pendingEvent != null)
                  {
                      currEvent = _pendingEvent;
                      _pendingEvent = null;
                  }

                  PrepareToStop(currEvent);
                  Stopped();
                  return;
              }

          }
          catch (AdapterException ex)
          {
              Console.WriteLine("ProduceEvents - " + ex.Message + ex.StackTrace);
          }
   }



       Note: The above code refers to two new methods, CreateCepEventFromMouseClick and
       PrepareToStop that will be added in the next Step.



   Since the CEP Server is multi-threaded and can try to shut down the adapter via another thread,
   we have write code to be aware of this (which makes the adapter code a bit harder). Therefore,
   each time the observedControl_MouseClick method is called we have to check the current state
   of the adapter by checking the AdapterState property. If the state is Stopped, then we just need
   to exit the method. If the state is Stopping, we need store the current event for a later time and
   then exit the method.
10. Add the following helper methods to the MouseClickPointInput class.
   C#
   private PointEvent<MouseClickEvent> CreateCepEventFromMouseClick(Control
   sender, MouseEventArgs args)
   {
       PointEvent<MouseClickEvent> evt = CreateInsertEvent();
       evt.StartTime = DateTime.Now;
       evt.Payload = new MouseClickEvent
       {
           ControlName = sender.Name,
           EventDateTime = DateTime.Now
       };
       return evt;
   }

   private void PrepareToStop(PointEvent<MouseClickEvent> currEvent)
   {
       _observedControl.MouseClick -= observedControl_MouseClick;

          if (null != currEvent)
          {
              ReleaseEvent(ref currEvent);
          }
   }

   private void PrepareToResume(PointEvent<MouseClickEvent> currEvent)
   {
          _pendingEvent = currEvent;
   }

   private void PrepareToResume(DateTime currCtiTime)
   {
       _pendingCtiTime = currCtiTime;
   }



       Note: The CreateCepEventFromMouseClick just creates a new instance of the CEP Event with
       the data from the mouse click event, and the PrepareToStop method unbinds the event
       handler and releases the current event, if there is any, to avoid memory leaks in the CEP
       Server. The PrepareToResume methods will help to prepare the adapter to resume if it is
       stopped.



   After adding these methods, we can go back to the observedControl_MouseClick method and
   finish the code.
11. Add the following bolded code in the observedControl_MouseClick method of the
    MouseClickPointInput class.
   C#
   void observedControl_MouseClick(object sender, MouseEventArgs e)
   {
       DateTime currctitime = default(DateTime);
       EnqueueOperationResult result = EnqueueOperationResult.Full;
       Control observedControl = sender as Control;
       PointEvent<MouseClickEvent> currEvent =
          CreateCepEventFromMouseClick(observedControl, e);

          try
          {
                if (AdapterState.Stopped == AdapterState)
                {
                    return;
                }

                if (AdapterState.Stopping == AdapterState)
                {
                    if (_pendingEvent != null)
                    {
                        currEvent = _pendingEvent;
                        _pendingEvent = null;
                    }

                    PrepareToStop(currEvent);
                    Stopped();
                    return;
               }

               if (_pendingEvent != null)
               {
                    currEvent = _pendingEvent;
                    _pendingEvent = null;
               }
               else if (_pendingCtiTime != null)
               {
                    // the check is important; but for this adapter, there is nothing
                    // to be done here.
                    // In general, you may want to take some action if the last CTI
                    // dequeue failed
               }
               else
               {
                    _pendingEvent = null;
               }

          }
          catch (AdapterException ex)
          {
              Console.WriteLine("ProduceEvents - " + ex.Message + ex.StackTrace);
          }
   }



       Note: At this point, we know that the adapter is in a state that can write CEP events. But
       before we write the event associated with this call to the method we first have to check if
       there is a previous event held because the state changed to stopping during the last call. If
       there is one, then we have to write that one first and then the current one. We should also
       check to see if there is a pending CTI event, but for this adapter there is really nothing to do.



12. If we had a pending CTI Event to be written, we want to write it using the EnqueueCtiEvent
    method, and if that fails, we must be stopping so, save that for when we are resumed. To do
    this, paste the following bolded code in the observedControl_MouseClick method of the
    MouseClickPointInput class.
   C#
   void observedControl_MouseClick(object sender, MouseEventArgs e)
   {
       DateTime currctitime = default(DateTime);
       EnqueueOperationResult result = EnqueueOperationResult.Full;
       Control observedControl = sender as Control;
PointEvent<MouseClickEvent> currEvent =
    CreateCepEventFromMouseClick(observedControl, e);

try
{
      if (AdapterState.Stopped == AdapterState)
      {
          return;
      }

      if (AdapterState.Stopping == AdapterState)
      {
          if (_pendingEvent != null)
          {
              currEvent = _pendingEvent;
              _pendingEvent = null;
          }

          PrepareToStop(currEvent);
          Stopped();
          return;
      }

      if (_pendingEvent != null)
      {
           currEvent = _pendingEvent;
           _pendingEvent = null;
      }
      else if (_pendingCtiTime != null)
      {
      }
      else
      {
           _pendingEvent = null;
      }

      if (_pendingCtiTime != null)
      {
          result = EnqueueCtiEvent(_pendingCtiTime.Value);
          if (EnqueueOperationResult.Full == result)
          {
               PrepareToResume(_pendingCtiTime.Value);
               Ready();
               return;
          }
          else
          {
               // there is no Cti event pending
               _pendingCtiTime = null;
                  }
            }
        }
        catch (AdapterException ex)
        {
            Console.WriteLine("ProduceEvents - " + ex.Message + ex.StackTrace);
        }
   }



13. If there was no pending CTI Event to be written, then we can try to write out the CEP Event for
    this mouse click. If there is an error on the call to EnqueueCtiEvent, save the event for next time
    and exit. To do this, paste the following bolded code in the observedControl_MouseClick
    method of the MouseClickPointInput class.
   C#
   void observedControl_MouseClick(object sender, MouseEventArgs e)
   {
       DateTime currctitime = default(DateTime);
       EnqueueOperationResult result = EnqueueOperationResult.Full;
       Control observedControl = sender as Control;
       PointEvent<MouseClickEvent> currEvent =
           CreateCepEventFromMouseClick(observedControl, e);

        try
        {
              if (AdapterState.Stopped == AdapterState)
              {
                  return;
              }

              if (AdapterState.Stopping == AdapterState)
              {
                  if (_pendingEvent != null)
                  {
                      currEvent = _pendingEvent;
                      _pendingEvent = null;
                  }

                  PrepareToStop(currEvent);
                  Stopped();
                  return;
              }

              if (_pendingEvent != null)
              {
                  currEvent = _pendingEvent;
                  _pendingEvent = null;
             }
             else if (_pendingCtiTime != null)
             {
             }
             else
             {
                  _pendingEvent = null;
             }

             if (_pendingCtiTime != null)
             {
                  result = EnqueueCtiEvent(_pendingCtiTime.Value);
                  if (EnqueueOperationResult.Full == result)
                  {
                       PrepareToResume(_pendingCtiTime.Value);
                       Ready();
                       return;
                  }
                  else
                  {
                       // there is no Cti event pending
                       _pendingCtiTime = null;
                  }
             }
             else
             {
                  DateTimeOffset currEventTime = currEvent.StartTime.AddTicks(1);
                  result = Enqueue(ref currEvent);

                  if (EnqueueOperationResult.Full == result)
                  {
                      PrepareToResume(currEvent);
                      Ready();
                      return;
                  }
            }
        }
        catch (AdapterException ex)
        {
            Console.WriteLine("ProduceEvents - " + ex.Message + ex.StackTrace);
        }
   }



14. If there is no error, try to write a CTI Event. If there is an error, save it for the next time,
    otherwise make sure the pending CTI Event is set to null. To do this, paste the following bolded
    code in the observedControl_MouseClick method of the MouseClickPointInput class.
   C#
void observedControl_MouseClick(object sender, MouseEventArgs e)
{
    DateTime currctitime = default(DateTime);
    EnqueueOperationResult result = EnqueueOperationResult.Full;
    Control observedControl = sender as Control;
    PointEvent<MouseClickEvent> currEvent =
        CreateCepEventFromMouseClick(observedControl, e);

    try
    {
          if (AdapterState.Stopped == AdapterState)
          {
              return;
          }

          if (AdapterState.Stopping == AdapterState)
          {
              if (_pendingEvent != null)
              {
                  currEvent = _pendingEvent;
                  _pendingEvent = null;
              }

              PrepareToStop(currEvent);
              Stopped();
              return;
          }

          if (_pendingEvent != null)
          {
               currEvent = _pendingEvent;
               _pendingEvent = null;
          }
          else if (_pendingCtiTime != null)
          {
          }
          else
          {
               _pendingEvent = null;
          }

          if (_pendingCtiTime != null)
          {
              result = EnqueueCtiEvent(_pendingCtiTime.Value);
              if (EnqueueOperationResult.Full == result)
              {
                  PrepareToResume(_pendingCtiTime.Value);
                  Ready();
                  return;
                   }
                   else
                   {
                          // there is no Cti event pending
                          _pendingCtiTime = null;
                   }
            }
            else
            {
                   DateTimeOffset currEventTime = currEvent.StartTime.AddTicks(1);
                   result = Enqueue(ref currEvent);

                   if (EnqueueOperationResult.Full == result)
                   {
                        PrepareToResume(currEvent);
                        Ready();
                        return;
                   }
                   else
                   {
                        result = EnqueueCtiEvent(currEventTime);
                        if (EnqueueOperationResult.Full == result)
                        {
                             PrepareToResume(currctitime);
                             Ready();
                             return;
                        }
                        else
                        {
                             // reset the Cti time so that new events can be enqueued
                             _pendingCtiTime = null;
                        }
                   }
            }
        }
        catch (AdapterException ex)
        {
            Console.WriteLine("ProduceEvents - " + ex.Message + ex.StackTrace);
        }
   }



   At this point, we are done with the observedControl_MouseClick method.
15. Implement the Resume method of the MouseClickPointInput class. To do this, paste the
    following bolded code inside the definition of the Resume method.
   C#
   public override void Resume()
{
    if (AdapterState.Stopped == AdapterState ||
        AdapterState.Stopping == AdapterState)
    {
        return;
    }

    EnqueueOperationResult result = EnqueueOperationResult.Full;
    DateTime currctitime = default(DateTime);

    if (_pendingEvent != null)
    {
        result = Enqueue(ref _pendingEvent);

         if (EnqueueOperationResult.Full == result)
         {
              PrepareToResume(_pendingEvent);
              Ready();
              return;
         }
         else
         {

              result = EnqueueCtiEvent(currctitime);
              if (EnqueueOperationResult.Full == result)
              {
                   PrepareToResume(currctitime);
                   Ready();
                   return;
              }
              else
              {
                   // reset the Cti time so that new events can be enqueued
                   _pendingCtiTime = null;
              }
         }

         _pendingEvent = null;
    }

    _observedControl.MouseClick += observedControl_MouseClick;
}



In the Resume method, we perform something similar to the observedControl_MouseClick
method, but instead of having a new mouse event to try to write to the CEP Server we have to
check the pending event. If there is an event there, try to write it and then wire up the event
handler in preparation for resuming normal adapter functionality.
Congratulations, you have written your first Input Adapter!


Task 4 – Creating the Input Adapter Factory Class
Now that we have both the Configuration and the Adapter class finished, we need to create the Factory
class that the CEP Server will use to create a new instance of the Adapter.
    1. Add a new class called MouseClickInputFactory to the project. To do this, in Solution Explorer
       right-click the EventAdapters folder of the StreamInsight.WritingAdapters project, point to
       Add, and then click Class. Type MouseClickInputFactory.cs in the Name text box and then click
       Add. The MouseClickInputFactory.cs file should be added to the project and opened in the class
       editor.




        Figure 13
        Adding the MouseClickInputFactory class to the StreamInsight.WritingAdapters project


    2. Add the following using statements at the top of the MouseClickInputFactory.cs file.
        C#
        using Microsoft.ComplexEventProcessing;
        using Microsoft.ComplexEventProcessing.Adapters;



    3. Make the class public and implement the ITypedInputAdapterFactory<MouseClickInputConfig>
       interface.
   C#
   using Microsoft.ComplexEventProcessing;
   using Microsoft.ComplexEventProcessing.Adapters;

   namespace StreamInsight.WritingAdapters.EventAdapters
   {
       public class MouseClickInputFactory :
           ITypedInputAdapterFactory<MouseClickInputConfig>
       {
       }




4. Implement the Dispose and Create<TPayload>methods required by the
   ITypedInputAdapterFactory interface. To do this, paste the following bolded code inside the
   definition of the MouseClickInputFactory class.
   C#
   public class MouseClickInputFactory :
       ITypedInputAdapterFactory<MouseClickInputConfig>
   {
       public InputAdapterBase Create<TPayload>(
           MouseClickInputConfig configInfo, EventShape eventShape)
       {
           InputAdapterBase adapter = default(InputAdapterBase);

             if (eventShape == EventShape.Point)
             {
                 adapter = new MouseClickPointInput(configInfo);
             }

             return adapter;
        }

        public void Dispose()
        {

        }
   }



   The Dispose method in this case does not have any resources to release, so it will do nothing.
   However, the Create method is where we check to make sure that this eventShape is a Point,
   and then create the instance of the MouseClickPointInput adapter passing in the instance of the
   config class.
At this point, we have finished all the tasks needed to create an Input Adapter. On to the Output
Adapter.


Task 5 – Creating the Output Configuration Class
As with the Input Adapter, the first thing we need to do is create the Output Adapter Configuration
class. This class must also be XML Serializable, so it cannot hold a reference to the textbox control.
Because the thread that the adapter is running on is not going to be the same one as the User Interface
thread, we cannot write to the textbox directly. So instead of passing in the control name, like we did in
the Input Adapter, we will instead expose an event on the Output Adapter that the Form can listen to.
Since the form will never get a reference to the instance of the Output Adapter (it only has a reference
to the Factory), we have to get a reference to the form and wire up the event handler. What this means
is that there is no properties in the Output Config.
    1. Add a new class called MouseClickOutputConfig to the project. To do this, in Solution Explorer
       right-click the EventAdapters folder of the StreamInsight.WritingAdapters project, point to
       Add, and then click Class. Type MouseClickOutputConfig.cs in the Name text box and then click
       Add. The MouseClickOutputConfig.cs file should be added to the project and opened in the
       class editor.




        Figure 14
        Adding the MouseClickOutputConfig class to the StreamInsight.WritingAdapters project


    2. Make the class public. The final code should looks like the following one:
       C#
       namespace StreamInsight.WritingAdapters.EventAdapters
       {
           public class MouseClickOutputConfig
           {
           }
       }




Task 6 – Creating the Output Adapter Class
The Output Adapter is not nearly as complex as the input adapter. As in the Input Adapter, we want this
one to be strongly typed so we will inherit from the TypedPointOutputAdapter<T> class. Just as in the
Input Adapter, we need to implement the Start and Resume methods. To handle the threading issue
with the UI, in the constructor, we get a reference to the Form via its static Forms property.
We also need to add a custom event to the Output Adapter called MouseEventAdded, and the
corresponding virtual method OnMouseEventAdded. To pass the event information to the Form, we will
need a custom EventArgs class, MouseEventAddedEventArgs.
    1. Add a new class called MouseClickPointOutput to the project. To do this, in Solution Explorer
       right-click the EventAdapters folder of the StreamInsight.WritingAdapters project, point to
       Add, and then click Class. Type MouseClickPointOutput.cs in the Name text box and then click
       Add. The MouseClickPointOutput.cs file should be added to the project and opened in the class
       editor.




       Figure 15
   Adding the MouseClickPointOutput class to the StreamInsight.WritingAdapters project


2. Add the following using statements at the top of the MouseClickPointOutput.cs file.
   C#
   using Microsoft.ComplexEventProcessing
   using Microsoft.ComplexEventProcessing.Adapters;
   using StreamInsight.WritingAdapters.EventTypes;



3. Make the class public and inherit from the TypedPointOutputAdapter<MouseClickEvent> class.
   C#
   using Microsoft.ComplexEventProcessing
   using Microsoft.ComplexEventProcessing.Adapters;
   using StreamInsight.WritingAdapters.EventTypes;

   namespace StreamInsight.WritingAdapters.EventAdapters
   {
       public class MouseClickPointOutput :
           TypedPointOutputAdapter<MouseClickEvent>
       {
       }
   }



4. Implement the Start and Resume abstract methods from the base class. To do this, paste the
   following bolded code inside the definition of the MouseClickPointOutput class.
   C#
   using Microsoft.ComplexEventProcessing.Adapters;
   using StreamInsight.WritingAdapters.EventTypes;

   namespace StreamInsight.WritingAdapters.EventAdapters
   {
       public class MouseClickPointOutput :
           TypedPointOutputAdapter<MouseClickEvent>
       {
           public override void Start()
           {
           }

             public override void Resume()
             {
             }
        }
   }
5. Because the code that executes the Output Adapter will be on a different thread than the UI,
   we cannot just update the form directly. What we have to do is inform the form that a new
   Event has come in from the CEP Server by way of a custom event on the Adapter, and then wire
   it to an event handler on the form. So first, let us create the custom event, called
   MouseEventAdded and a virtual protected method called OnMouseEventAdded that will fire
   the event. We will also need an event arguments class called MouseEventAddedEventArgs with
   two properties (ControlName and EventFiredDateTime). To do this, paste the following bolded
   code inside the definition of the MouseClickPointOutput class.
   C#
   using Microsoft.ComplexEventProcessing;
   using Microsoft.ComplexEventProcessing.Adapters;
   using StreamInsight.WritingAdapters.EventTypes;

   namespace StreamInsight.WritingAdapters.EventAdapters
   {
       public class MouseClickPointOutput :
           TypedPointOutputAdapter<MouseClickEvent>
       {
           public override void Start()
           {
           }

            public override void Resume()
            {
            }

            public event EventHandler<MouseEventAddedEventArgs>
                MouseEventAdded = delegate { };

            protected virtual void OnMouseEventAdded(
                PointEvent<MouseClickEvent> firedEvent)
            {
                string observedControlName = firedEvent.Payload.ControlName;

                 MouseEventAdded(this, new MouseEventAddedEventArgs()
                 {
                     ControlName = firedEvent.Payload.ControlName,
                     EventFiredDateTime = firedEvent.Payload.EventDateTime
                 });
            }

            public class MouseEventAddedEventArgs : EventArgs
            {
                public string ControlName { get; set; }
                public DateTime EventFiredDateTime { get; set; }
              }
        }
   }



6. Implement the ConsumeEvents method that will be called from both the Resume and the Start
   method. To do this, paste the following bolded code in the MouseClickPointOutput class. Make
   sure to make a call to the ConsumeEvents method from both the Resume and the Start
   methods.
   C#
   public override void Start()
   {
       ConsumeEvents();
   }

   public override void Resume()
   {
       ConsumeEvents();
   }

   private void ConsumeEvents()
   {
       PointEvent<MouseClickEvent> currEvent = null;
       DequeueOperationResult result;

        try
        {
              while (true)
              {
                  if (AdapterState.Stopping == AdapterState)
                  {
                      result = Dequeue(out currEvent);
                      PrepareToStop(currEvent, result);
                      Stopped();
                      return;
                  }

                  result = Dequeue(out currEvent);
                  if (DequeueOperationResult.Empty == result)
                  {
                       PrepareToResume();
                       Ready();
                       return;
                  }
                  else
                  {
                       CreateLineFromEvent(currEvent);
                       // very important - release the event
                       ReleaseEvent(ref currEvent);
                   }
              }
          }
          catch (AdapterException e)
          {
              Console.WriteLine("ConsumeEvents - " + e.Message + e.StackTrace);
          }
   }



       Note: The above code refers to two new methods, PrepareToStop and PrepareToResume that
       will be added in the next Step.



   Since both Input and Output Adapters implement the same State Machine, the Consume Events
   method is similar to the code in the Input Adapter. It checks the Adapter State to see if it is
   stopping, and do some housekeeping if it is. Otherwise, it just needs to read an event from the
   CEP Server and then fire the MouseEventAdded event.
7. Add the following helper methods to the MouseClickPointOutput class.
   C#
   private void PrepareToStop(PointEvent<MouseClickEvent> currEvent,
                             DequeueOperationResult result)
   {
       if (DequeueOperationResult.Success == result)
       {
           ReleaseEvent(ref currEvent);
       }
   }

   private void PrepareToResume()
   {
   }



8. Add the following bolded method, called CreateLineFromEvent, to the MouseClickPointOutput
   class.
   C#
   private void CreateLineFromEvent(PointEvent<MouseClickEvent> currEvent)
   {
       if (EventKind.Cti == currEvent.EventKind)
       {
           return;
             }
             OnMouseEventAdded(currEvent);
        }



        In the ConsumeEvents method, when a Dequeue method gets a result, it calls the
        CreateLineFromEvent method. The CreateLineFromEvent method, checks if the Event Kind is
        equal to Cti, and if so, just return. Otherwise, it calls to the OnMouseEventAdded method so the
        MouseEventAdded event is raised.
    9. Create a constructor for the MouseClickPointOutput class that receives a
       MouseClickOutputConfig instance as a parameter. To do this, add the following constructor
       shown in bold.
        C#
        public MouseClickPointOutput(MouseClickOutputConfig config)
        {
            this.MouseEventAdded += Form1.Forms["me"].OutputAdapter_OnMouseEventAdded;
        }




The Output Adapter is now complete.


Task 7 – Creating the Output Adapter Factory Class
Just like with the Input Adapter, we need to create a strongly typed Adapter Factory. Therefore, we will
create a new MouseClickOutputFactory class and have it implement the
ITypedOutputAdapterFactory<MouseClickOutputConfig> interface. It will implement two methods,
Create<TPayload> and Dispose. Dispose does not have any resources to release, so it will do nothing.
The Create method will check that the Event Shape is a Point, and if so, create an instance of the Output
Adapter, passing in an instance of the Output Config class.
    1. Add a new class called MouseClickOutputFactory to the project. To do this, in Solution Explorer
       right-click the EventAdapters folder of the StreamInsight.WritingAdapters project, point to
       Add, and then click Class. Type MouseClickOutputFactory.cs in the Name text box and then
       click Add. The MouseClickOutputFactory.cs file should be added to the project and opened in
       the class editor.
   Figure 16
   Adding the MouseClickOutputFactory class to the StreamInsight.WritingAdapters project


2. Add the following using statements at the top of the MouseClickOutputFactory.cs file.
   C#
   using Microsoft.ComplexEventProcessing;
   using Microsoft.ComplexEventProcessing.Adapters;



3. Make the class public and implement the
   ITypedOutputAdapterFactory<MouseClickOutputConfig> interface.
   C#
   using Microsoft.ComplexEventProcessing;
   using Microsoft.ComplexEventProcessing.Adapters;

   namespace StreamInsight.WritingAdapters.EventAdapters
   {
       public class MouseClickOutputFactory :
   ITypedOutputAdapterFactory<MouseClickOutputConfig>
       {
       }
   }
    4. Implement the Create<TPayload> and Dispose methods required by the
       ITypedOutputAdapterFactory interface. To do this, paste the following bolded code inside the
       definition of the MouseClickInputFactory class.
        C#
        public class MouseClickOutputFactory :
            ITypedOutputAdapterFactory<MouseClickOutputConfig>
        {
            public OutputAdapterBase Create<TPayload>(
                MouseClickOutputConfig configInfo, EventShape eventShape)
            {
                OutputAdapterBase adapter = default(OutputAdapterBase);

                 switch (eventShape)
                 {
                     case EventShape.Point:
                         adapter = new MouseClickPointOutput(configInfo);
                         break;
                     case EventShape.Interval:
                         throw new NotImplementedException();
                     case EventShape.Edge:
                         throw new NotImplementedException();
                     default:
                         break;
                 }

                 return adapter;
             }

             public void Dispose()
             {
             }
        }



        Just like the Adapter Factory, the Dispose method does do anything, and the Create<TPayload>
        method checks that the Event Shape is a Point, and then calls the constructor of the Output
        Adapter passing in the instance of the config class.

At this point, we have completed both the input and output adapters and their supporting classes. All
we need to do now is to repeat what we did in Exercise 1 and add the query to the Form.
Task 8 – Updating the Form
    1. Open the code for the Form1. To do this, right-click the Form1.cs file in the Solution Explorer
       and select View Code.
    2. Add the following using statements at the top of the Form1.cs file.
   C#
   using StreamInsight.WritingAdapters.EventTypes;
   using StreamInsight.WritingAdapters.EventAdapters;



3. Add a class level delegate called UIUpdaterDelegate to the Form1 class. To do this, paste the
   following bolded code inside the Form1 class.
   C#
   using Microsoft.ComplexEventProcessing.Linq;
   using StreamInsight.WritingAdapters.EventTypes;
   using StreamInsight.WritingAdapters.EventAdapters;

   namespace StreamInsight.WritingAdapters
   {
       public partial class Form1 : Form
       {
           internal static readonly Dictionary<string, Control> InputsControls =
               new Dictionary<string, Control>();
           internal static readonly Dictionary<string, Form1> Forms =
               new Dictionary<string, Form1>();

           private readonly Query _eventQuery;
           delegate void
   UIUpdaterDelegate(StreamInsight.WritingAdapters.EventAdapters.MouseClickPointO
   utput.MouseEventAddedEventArgs args);

             public Form1()
             {
                 InitializeComponent();

                  try
                  {

                        Form1.InputsControls.Add(clickMeButton.Name, clickMeButton);
                        Form1.Forms.Add("me",this);
                  }
                  catch (Exception e)
                  {
                  }
             }



4. Update the Form1 constructor to the write a simple StreamInsight query, just like in Example 1.
   To do this, add the following bolded code to the constructor of the Form1 class.
   C#
   public Form1()
   {
        InitializeComponent();

        try
        {
              Form1.InputsControls.Add(clickMeButton.Name, clickMeButton);
              Form1.Forms.Add("me", this);

              var inputConfig = new MouseClickInputConfig
                  { InputControlName = clickMeButton.Name };
              var outputConfig = new MouseClickOutputConfig { };

              var inputStream =
                  CepStream<MouseClickEvent>.Create(
                      "inputstream", typeof(MouseClickInputFactory),
                       inputConfig, EventShape.Point);

              CepStream<MouseClickEvent> filter = from e in inputStream
                                                  select e;

              _eventQuery = filter.ToQuery(typeof(MouseClickOutputFactory),
                  outputConfig,
                  EventShape.Point,
                  StreamEventOrder.FullyOrdered);
              _eventQuery.Start();
        }
        catch (Exception e)
        {
        }
   }



5. Update the event handler Form1_FormClosing to call the Stop method on the class level
   variable _eventQuery.
   C#
   private void Form1_FormClosing(object sender, FormClosingEventArgs e)
   {
       _eventQuery.Stop();
   }



6. Add an event handler method called OutputAdapter_OnMouseEventAdded and have it sync to
   the UI thread using the Invoke method of the Form class passing in the UIUpdateDelegate
   delegate. To do this, paste the following bolded code inside the body of the Form1 class.
   C#
   internal void OutputAdapter_OnMouseEventAdded(object sender,
   StreamInsight.WritingAdapters.EventAdapters.MouseClickPointOutput.MouseEventAd
   dedEventArgs arg)
   {
       // sync to the UI Thread
       UIUpdaterDelegate updateUI = UpdateUI;
       this.Invoke(updateUI, new object[] { arg });
   }



7. Add a method called UpdateUI that will update the Text property of the clickEventTextBox
   textbox. To do this, paste the following bolded code inside the body of the Form1 class.
   C#
   protected void
   UpdateUI(StreamInsight.WritingAdapters.EventAdapters.MouseClickPointOutput.Mou
   seEventAddedEventArgs arg)
   {
       clickEventTextBox.Text = clickEventTextBox.Text +
           arg.ControlName +
           " " +
           arg.EventFiredDateTime.ToLongTimeString() + "\r\n";
   }



8. Start the application in the Debugger by pressing F5.
9. The textbox should have one row in it with that begins with "Start Event" and then the time the
   application started.




   Figure 17
   Form1 Initial State


10. Click the Click Me button. The textbox should be updated to include a new row that begins with
    "clickMeButton" and the time of the click event.




   Figure 18
   Form1 after clicking Click Me button


11. Click the Click Me button a couple more times, and a new row should be create in the
    clickEventTextBox textbox for each click.




   Figure 19
       Form1 After Button has been clicked a couple times


    12. Close the form to stop debugging.



Summary
By completing this hands-on lab, hopefully you have a taste of how to build a simple Complex Event
Processing application using Microsoft SQL Server StreamInsight. This was a very simple
implementation, skipping over most of the advanced topics.

								
To top