You are on page 1of 8

A Beginner's Tutorial on Managing

Sequence of Operations in WCF Service


Rahul Rajat Singh, 2013
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.

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

[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:

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:

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.

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

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);
}
}
}

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.

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);
}
}
}

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.

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);
}
}
}

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.

You might also like