Taking an Imperative Approach

Applies To: System Center 2012 - Orchestrator, System Center 2012 R2 Orchestrator, System Center 2012 SP1 - Orchestrator

The Orchestrator SDK allows you the flexibility to take a declarative or imperative approach to creating custom activities. With the declarative approach, you use the class attributes to define activity, inputs and outputs, but the detailed implementation is left to the underlying architecture of the SDK. It can be a much simpler approach in most situations, but sometimes you want some fine-grained control over how items are displayed or how data is handled. This may require utilizing the imperative approach, which uses interfaces and requires you to be more deliberate in defining how the activity should function. This section describes how you would take the Hello World example and modify it to utilize the imperative approach.

Getting Started

Just like in the previous example, you need to start with a .NET 3.5 project and a class referencing the Orchestrator SDK Library. The easiest thing to do is simply copy and paste the class file created in the previous example to a new file, called HelloWorldImperative.cs. Then just remove everything inside the class brackets so that you’re left with something similar to the class shown below.

    [Activity ("Hello World)") ]
    public class HelloWorldImperative
    {
      
    }

Next, give the activity a new name to distinguish it from the other activity, then make the class inherit from the IActivity class.

[Activity ("Hello World (Imperative)") ]
    public class HelloWorldImperative : IActivity
    {
      
    }

Because this activity uses the IActivity interface, it must contain at least two special methods to get input and return output to Orchestrator. Those methods are Design and Execute. The Design method is called when the activity is being configured at design time (in the Runbook Designer). The Execute method is called when the activity is actually being invoked by the Runbook Server (or Runbook Tester).

You’ll need to insert the base implementation of those methods into the class in order for the code to even compile, let along for the activity to function. The base implementation is shown below.

[Activity ("Hello World (Imperative)") ]
    public class HelloWorldImperative : IActivity
    {
      
        public void Design(IActivityDesigner designer)
        {
        }

        public void Execute(IActivityRequest request, 
                        IActivityResponse response)
        {
        }
      
    }

Of course, in order for the activity to actually have inputs and outputs and actually do anything, you’ll have to add more code. Using the previous Hello World example, we have one input property called “Hello World Input”, and an output property called “Hello World Output”. You define those input and output properties inside the Design method by using the AddInput and AddOutput methods of the IActivityDesigner interface like shown below.

[Activity ("Hello World (Imperative)") ]
    public class HelloWorldImperative : IActivity
    {
      
        public void Design(IActivityDesigner designer)
        {
            designer.AddInput("Hello World Input");
            designer.AddOutput("Hello World Output");
        }

        public void Execute(IActivityRequest request, 
                            IActivityResponse response)
        {
        }
      
    }

You might notice that in this simple example, defining the inputs and outputs is actually a lot less code than using the declarative method because we don’t have to define standalone properties with “get” methods, using ActivityInput and ActivityOutput attributes.

Now that the inputs and outputs are defined, it’s time to define that happens at runtime. From the previous example, the activity simply took the input text and reversed it. Putting that into the imperative model, here is the resulting code:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.SystemCenter.Orchestrator.Integration;

namespace OrchestratorSDKExample
{
    [Activity ("Hello World (Imperative)") ]
    public class HelloWorldImperative : IActivity
    {
      
        public void Design(IActivityDesigner designer)
        {
            designer.AddInput("Hello World Input");
            designer.AddOutput("Hello World Output");
            
        }

        public void Execute(IActivityRequest request, 
                            IActivityResponse response)
        {
            string hwinput = request.Inputs["Hello World Input"].AsString();
            char[] arr = hwinput.ToCharArray();
            Array.Reverse(arr);
            response.Publish("Hello World Output", new string(arr));
        }
    }
}

At runtime, the activity runs the Execute method and is passed two parameters to hold the inputs and outputs. In the IActivityRequest object, you use the Inputs collection to get the values of input properties. When you’re ready to return the output information, you use the IActivityResponse interface’s Publish method to send a value to a specific output property.

When you compile this code and use the resulting DLL in the Invoke .NET activity, you get the same results as the previous exercise.