Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web / ASP.NET

A Beginner's Tutorial on Managing Sequence of Operations in WCF Service

5.00/5 (6 votes)
25 Mar 2013CPOL6 min read 22.5K   257  
This article talks about managing sequence of operations in a WCF service.

Introduction

In this article we will discuss about how we can enforce and manage sequencing operations in a WCF service. We will see how a WCF service can be configured to work in a way such that the call to methods in a WCF service always be in a specific order and calling the functions out of that order will result in exceptions.

Background

We have already seen how we can configure a WCF service instance as per our application needs (A Beginner's Tutorial for Understanding WCF Instance Management [^]). Now in case we have configured our WCF service to work in a stateful mode by setting the

InstanceContextMode
to PerSession, one more question arises. The question is how can we configure the service in such a way that a particular order of operations in enforced on this stateful WCF service.

Why is this order important? The order is important because if our service is maintaining state for every client then perhaps we need to do some initialization of resource acquisitions whenever the instance is being created. The other functions of the service will then use these resources to perform their operations. We would also want to ensure the that our resource cleanup is done whenever a particular function is called (perhaps the last function in the sequence of functions).

Now we can very well define a convention that the clients should call some function first and then call the other functions of the service so that the initial function will take care of all initialization and the final function will take care of resource cleanup. But what if any client call some intermediate function without calling the initialization function or after calling the cleanup function? This could create some possible inconsistent behavior. WCF provide us a way to enforce this convention and make sure that if any client calls any function without calling the initialization function or after calling the cleanup function then the client call will fail.

Understanding the SessionMode property

before looking at the sequence specific configuration, let us understand a very important property called SessionMode. The SessionMode property goes as part of the ServiceContract attribute and it specifies how the service will implement sessions. SessionMode can be set to three possible values:

  1. Allowed
  2. NotAllowed
  3. Required

Allowed specifies that the service will allow the sessions if the sessions are supported by the protocol being used i.e. binding and the InstanceContextMode is specified as PerSession.

NotAllowed specifies that the service will not allow the sessions even if the sessions are supported by the binding and/or the InstanceContextMode is specified as PerSession.

Required option specifies that the service will support sessions and the underlying binding/protocol should be chosen MUST support sessions.

Configuring the Sequence of Operations

As we have discussed above, if we need to enforce the order of operations in a WCF service, WCF provides a way to mark a particular function as the function that should be the first function to be called to initiate the instance creation process. This can be done by setting the IsInitiating property of the OperationContract to true.

Similarly, to mark a function as the final function in a sequence of operations, we need to set the IsTerminating property of the OperationContract to true. The IsInitiating property for this function should be set to false.

Rest all the functions should be have the OperationContract with the IsInitiating property set to false. All these functions could only be called when the function marked with IsInitiating true has been called and the function marked with IsTerminating set to true has not been called. Otherwise call to this function will result in an exception.

Using the code

Let us now create a sample service to see this in action.

Creating the Test Service

Let us create a simple service with three operations. There will be one function(Function1) that will initiate the instance creation, one function(Function3) that will terminate the instance. There will be one more function that could only be called only when Function1 has been called and Function3 has not yet been called. Calling this function otherwise would lead to an exception.

Also, Let us set the SessionMode property of the ServiceContract as SessionMode.Required so that this service could not be used in a session less scenario/binding. Let us look at the ServiceContract and OperationContact to achieve the same. 

C#
[ServiceContract(SessionMode=SessionMode.Required)]
public interface ISampleService
{
    // This function will INITIATE the session
    [OperationContract(IsInitiating=true)]
    string Function1();

    // This function will work only if session has been INITIATED and not yet TERMINATED
    [OperationContract(IsInitiating = false)]
    string Function2();

    // This function will TERMINATE the session
    [OperationContract(IsInitiating = false, IsTerminating=true)]
    string Function3();
}

The service implementation will have the InstanceContextMode property of ServiceBehavior set as InstanceContextMode.PerSession. The functions will simply return some dummy strings to show what method has been called. This will ensure that one instance of the service will be created for every client.

C#
[ServiceBehavior(InstanceContextMode=InstanceContextMode.PerSession)]
public class SampleService : ISampleService
{
    public string Function1()
    {
       return "This function will INITIATE the session";
    }

    public string Function2()
    {
        return "This function will work when Session has been INITIATED and not yet TERMINATED";
    }

    public string Function3()
    {
        return "This function will TERMINATE the session";
    }
}

Creating a Host

Now since the ServiceContract has the SessionMode property set to Required, we will have to choose the binding that supports sessions. So let us self host this service in a console application that will expose the service via TCP. Since netTcpBinding supports sessions, hosting this service using this binding will supports sessions.

Details of the created endpoint is as follows:

  • Address: net.tcp://localhost/TestService
  • Binding: netTcpBinding
  • Contract: SampleServiceNamespace.SampleService

And the code to host the service will look like:

C#
static void Main(string[] args)
{
    using (ServiceHost host = new ServiceHost(typeof(SampleServiceNamespace.SampleService)))
    {
        host.Open();

        Console.WriteLine("Service up and running at:");
        foreach (var ea in host.Description.Endpoints)
        {
            Console.WriteLine(ea.Address);
        }

        Console.ReadLine();
        host.Close();
    }
}

We can run the host to access our test service. Running the host will look like:

Image 1 

Note: Please refer to the app.config file of the host application to see the full configuration.

Test Client and testing sequence of operations 

We will now create a simple test client that will call the WCF service functions. First let us try to call the Function2 directly. Now since the Function1 is marked as IsInitiating=true, direct call to

Function2
will result in an exception. 

C#
static void Main(string[] args)
{
    using (ServiceReference1.SampleServiceClient client = new ServiceReference1.SampleServiceClient())
    {
        // let try to call the function that required session to be present
        Console.WriteLine("Scenario 1: let try to call the function that required session to be present");
        try
        {   
            string result = client.Function2();
            Console.WriteLine(result);
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }
}
Image 2

Same will be the case if we try to call Function3, since Function3 is marked as IsTerminating=true, calling this without calling Function1 will also result in exception. 

C#
static void Main(string[] args)
{
    using (ServiceReference1.SampleServiceClient client = new ServiceReference1.SampleServiceClient())
    {
        // Let us now try to call the terminating function without initating session       
        Console.WriteLine("Scenario 2: Let us now try to call the terminating function without initating session");
        try
        {
            string result = client.Function3();
            Console.WriteLine(result);
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }
}
Image 3

Now let us try the valid sequence of operations. We will call Function1(which is marked as IsInitiating=true) then we will call Function2(which is supposed to be called after Function1 and before Function3) and finally we will call Function 3(which is marked as IsTerminating=false.

C#
static void Main(string[] args)
{
    using (ServiceReference1.SampleServiceClient client = new ServiceReference1.SampleServiceClient())
    {
        // Now let us call the functions in proper order        
        Console.WriteLine("Scenario 3:  Now let us call the functions in proper order");
        try
        {
            string result1 = client.Function1();
            Console.WriteLine(result1);

            string result2 = client.Function2();
            Console.WriteLine(result2);

            string result3 = client.Function3();
            Console.WriteLine(result3);
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }
}
Image 4

And now finally, let us try to call Function2 after call to Function3 has been completed. Since

Function3
has been marked as IsTerminating=true, call to Function2 after this will result in exception.

C#
static void Main(string[] args)
{
    using (ServiceReference1.SampleServiceClient client = new ServiceReference1.SampleServiceClient())
    {
        // Now let us call the function2 again when the session has been terminated        
        Console.WriteLine("Scenario 4:  Now let us call the function2 again when the session has been terminated");
        try
        {
            string result1 = client.Function1();
            Console.WriteLine(result1);

            string result2 = client.Function2();
            Console.WriteLine(result2);

            string result3 = client.Function3();
            Console.WriteLine(result3);

            // This should now give us the exception
            string result2_alt = client.Function2();
            Console.WriteLine(result2_alt);
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }
}
Image 5

And thus by setting the IsInitating and IsTerminating property we have enforced as sequence of operations in our WCF service.

A Note on Durable WCF Services

WCF also provides a option for marking the service as Durable service. A durable service is a service that persist the information of client sessions in some permanent stores like databases so that even in case of service class instance goes off, it could regain the state information once a new instance for the same client comes up. This is very useful when we need a stateful service running for a long time.

A service can be marked as DurableService to make it a durable service. The functions should also be marked as DurableOperation to make it a durable operation. The DurableOperation supports the similar kind of properties to maintain order of operations. For a durable operation, setting the CanCreateInstance to true will make the operation as the first operation in the sequence and setting the CompleteInstance to true will make the operation as terminating operation in in sequence.

Note: This is a very high level overview of durable services. Durable service creation requires some more configuration to be done at service end.

Point of interest

In this article we saw how we can enforce an order/sequence of operations in a stateful WCF service. This has been written from a beginner's perspective(but some knowledge of WCF service and instance modes is required). I hope this has been informative.

History

  • 25 March 2013: First version.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)