Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C#4.0

Understanding Operation types in WCF

4.79/5 (9 votes)
14 Oct 2013CPOL9 min read 31.9K   392  
This article explains the operaton type supported in WCF

 

Introduction  

WCF supports 3 ways for a client to call a service. This article demonstrate all 3 ways.

Background

WCF supports 3 ways for a client to invoke a service. this article covers these 3 basic methods of innvoking service through a client. There are two other ways invoking operations asynchronously and queued, those ways are not covered in this article.

  1. Request Reply : default way of invocation, the client would issue a call, block while the call was in progress, and would continue executing once the method returned. If service doesn't respond to the service within receiveTimeout, client will receive TimeOutException .
  2. One way calls: fire and forget type operations. that simply means client will send a request to the server and does not care whether service execution was a success or failure. There is no return value from the server side. Client will be blocked only for a moment till it dispatches its call to service. Client can continue to execute its statement, after making one-way call to the service. sometime calls are queued at the server side to be dispatched one at a time. if the number of queued messages has exceeded the queue's capacity, client will be blocked for the moment till the message is being queued. and as soon as the call is queued, the client will be unblocked and can continue executing, while the service processes the operation in the background.
  3. Call back Operation: WCF Supports allowing a service to call back to its clients. They are specially useful, for notifying client(s) that something happened on the service side. call backs are also commonly referred as duplex operations. not all binding types support call back operations. for example HTTP (for obvious reasons of being a connectionless nature). only two commonly used binding that offer call backs are NetTcpBinding and NetNamedBinding. to offer call backs over HTTP, WCF offers the WSDualHttpBinding, which actually sets up two WS channels.

Using the code

This article has been divided into 3 modules:

  • WCF Service Library (OperationLib.dll): Actual Service logic, which defines a Service Contract Interface, OperationContract, and implements them, and exposes few functions to the world to use them
  • Console based Host Application to host the WCF Service Library OperationLibHost.exe): Host the WCF library
  • Console Client Application (OperationClient.exe): Client Application which will use this service

First Module: WCF Service Library (OperationLib.dll)

To create this project, you can simply take a "Class Library" project, while choosing from project wizard option. let's name it "OperationLib", it is the actual service which implements the business logic. The project already contains a file Class1.cs, let us do some house keeping, before we write any code for the service library  

Little House Keeping

  • Delete the file Class1.cs from the project workspace.
  • Add a new Interface named IRequestReplyService to the project, a new file IRequestReplyService.cs will be added to the project.
  • Add a new Class named RequestReplyService, to the project. that will implement the IRequestReplyService interface, a new file RequestReplyService.cs will be added to the project.
  • Add a new Interface named IOneWayCallService to the project, a new file IOneWayCallService.cs will be added to the project.
  • Add a new Class named OneWayCallService, to the project. that will implement the IOneWayCallService interface, a new file OneWayCallService.cs will be added to the project.
  • Add a new Interface named IDuplexService to the project, a new file IDuplexService.cs will be added to the project.
  • Add a new Class named DuplexService, to the project. that will implement the IDuplexService interface, a new file DuplexService.cs will be added to the project.

Defining Interfaces       

Defining IRequestReply Interface  
C#
//  Listing of IRequestReplyService.cs

using System;
using System.Text;
using System.ServiceModel;

namespace OperationLib
{
    [ServiceContract]
    interface IRequestReplyService
    {
        [OperationContract]
        double AddNumer(double dblNum1, double dblNum2);
     }
}    

Explanation

Interface simple defines 1 method that takes 2 parameter(s) of double type, and returns a double  

Defining IOneWayCallService Interface   
C#
//  Listing of IOneWayCallService.cs
using System;
using System.ServiceModel;
using System.Text;
namespace OperationLib
{
   [ServiceContract(SessionMode = SessionMode.Required)]
   interface IOneWayCallService
   {
        [OperationContract(IsOneWay = true)]
        void AddNumer(double dblNum1, double dblNum2 );

        [OperationContract]
        double GetResult();
    }
} 

Explanation

Interface simple defines 1 method that 1 parameter of double type, and returns nothing. notice [ServiceContract(SessionMode = SessionMode.Required)] attribute. this has been done to make make use of One-way operation in Sessionful service, it is just to preserve the value of the last operation, as the operation marked as [OperationContract(IsOneWay = true)] should not return any value. to get the result of the operation. we have defined another method

C#
double GetResult(); 
Defining IDuplexService Interface  
C#
//  Listing of IDuplexService.cs
using System;
using System.ServiceModel;
using System.Text;
namespace OperationLib
{
    public interface IDuplexServiceCallback
    {
        [OperationContract]
        void OnValueAdded(double dblNum1, double dblNum2, double dblResult);
    }

    [ServiceContract(CallbackContract = typeof(IDuplexServiceCallback))]
    public interface IDuplexService
    {
        [OperationContract()]
        void AddNumer(double dblNum1, double dblNum2);
    }
}      
Explanation

Callback service can be enabled by using CallbackContract property in the ServiceContract attribute. as shown, this defines two interfaces, one interface (IDuplexService) which will be implemented by the service, IDuplexService interface simply defines a method that takes two double parameters, and returns nothing.

the other interface (IDuplexServiceCallback) has been defined, which will be implemented by the client. IDuplexServiceCallback has been defined as callback property of (IDuplexService) interface, (IDuplexServiceCallback) simply defines one method prototype.

C#
void OnValueAdded(double dblNum1, double dblNum2, double dblResult);  

Implementing Interfaces

let's implement interface for each service.

Implementing IRequestReply Interface
C#
//  Listing of RequestReplyService.cs
using System;
using System.Text;

namespace OperationLib
{
    public class RequestReplyService : IRequestReplyService
    {
        public double AddNumer(double dblNum1, double dblNum2)
        {
            return (dblNum1 + dblNum2);
        }
    }
}  
Explanationn

the method simply adds 2 values and returns the result.   

Implementing IOneWayCallService Interface
C#
//  Listing of OneWayCallService.cs
using System; 
using System.Text; 
namespace OperationLib 
{ 
    public class OneWayCallService : IOneWayCallService 
    { 
        private double m_dblResult = 0; 
        
        public void AddNumer(double dblVal1, double dblVal2) 
        { 
            m_dblResult = dblVal1 + dblVal2; 
        }
         
        public double GetResult() 
        { 
            return m_dblResult; 
        } 
    }
} 
Explanationn

Simply adds 2 values and stores the result in a private varibale, Interface was marked with SessionMode.Required, to make it a sessionful service, as we need to return the value of the operation, and methods was marked as [OperationContract(IsOneWay = true)] can not return any value,  so we added one more method, that will simply return the value of private variable (result). that means, client needs to call double GetResult () method to get the result, after calling the AddNumber method.   

Implementing IDuplexService Interface
C#
//  Listing of DuplexService.cs
using System; 
using System.ServiceModel ; 
using System.Text; 
namespace OperationLib 
{ 
    [ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Multiple)] 
    public class DuplexService : IDuplexService 
    { 
        public void AddNumber(double dblNum1, double dblNum2) 
        { 
            double dblResult = dblNum1 + dblNum2; 
            IDuplexServiceCallback callbackInstance = OperationContext.Current.GetCallbackChannel<IDuplexServiceCallback>(); 
            callbackInstance.OnNumberAdded(dblNum1, dblNum2, dblResult); 
        }
    }
} 
 Explanationn  

You can note that We have set the ConcurrencyMode to Multile. If you are not using ConcurrencyMode to Multiple or Reentent, you will be end up with deadlock exception as shown below. This is because when a client made a call to the service, channel is created and lock by WCF service. If you are calling the Callback method inside the service method. Service will try to access the lock channel, this may leads to deadlock. So you can set ConcurrencyMode to Multiple or Reentent so it will release the lock silently.

C#
[ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Multiple)] 
public class DuplexService : IDuplexService 
{ 
    . . . 
}

The OperationContext class provides the service with easy access to the callback reference via the generic method GetCallbackChannel<T>( ). 

C#
IDuplexServiceCallback callbackInstance = OperationContext.Current.GetCallbackChannel<IDuplexServiceCallback>();

Service then can call the client side callback method using the obtained reference. The following code shows the callback method invocation.

C#
callbackInstance.OnNumberAdded(dblNum1, dblNum2, dblResult); 
Build the Class Libray, and the first part is complete.

Second Module: Host Application (OperationLibHost.exe)

To create this project, you can simply take a "Console based Application" project, while choosing from project wizard option. let's name it "OperationLibHost", 

a new Project OperationLibHost will be added to the workspace. 

Assumption

The Host application will expose the following endpoints for the service.

  • RequestReplyService will expose HTTP endpoint at Port 9011
  • Corresponding mex End Point (IMetadatExchange) for the HTTP end point.
  • RequestReplyService will expose TCP endpoint at Port 9012
  • Corresponding mex End Point (IMetadatExchange) for the TCP end point.
  • OneWayCallService will expose HTTP endpoint at Port 9013
  • Corresponding mex End Point (IMetadatExchange) for the HTTP end point.
  • OneWayCallService will expose TCP endpoint at Port 9014
  • Corresponding mex End Point (IMetadatExchange) for the TCP end point.  
  • DuplexService will expose HTTP endpoint at Port 9015
  • Corresponding mex End Point (IMetadatExchange) for the HTTP end point.
  • DuplexService will expose TCP endpoint at Port 9016
  • Corresponding mex End Point (IMetadatExchange) for the TCP end point. 

Defining configuration for RequestReplyService 

XML
<service name="OperationLib.RequestReplyService" behaviorConfiguration="RequestReplyServiceBehavior">
        <host>
          <baseAddresses>
            <add baseAddress="http://localhost:9011/RequestReplyService"/>
            <add baseAddress="net.tcp://localhost:9012/RequestReplyService"/>
          </baseAddresses>
        </host>
        
        <endpoint address="http://localhost:9011/RequestReplyService" binding="wsHttpBinding" contract="OperationLib.IRequestReplyService"/>
        <endpoint address="net.tcp://localhost:9012/RequestReplyService" binding="netTcpBinding" contract="OperationLib.IRequestReplyService"/>
        <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
        <endpoint address="mex" binding="mexTcpBinding" contract="IMetadataExchange"/>
      </service> 
 Explanationn  

Application configuration files defines 2 endpoints, one for WSHttpBinding and one for TCP Binding,  at port 9011 and 9012 respectively and 1 mex end point for each.  WSHttpBinding is similar to the BasicHttpBinding, but provides more web service features.

Defining configuration for OneWayCallService

XML
<service name="OperationLib.OneWayCallService" behaviorConfiguration="OneWayCallServiceBehavior">
    <host>
       <baseAddresses>
          <add baseAddress="http://localhost:9013/OneWayCallService"/>
          <add baseAddress="net.tcp://localhost:9014/OneWayCallService"/>
       </baseAddresses>
    </host>

   <endpoint address="http://localhost:9013/OneWayCallService" binding="wsHttpBinding" contract="OperationLib.IOneWayCallService"/>
   <endpoint address="net.tcp://localhost:9014/OneWayCallService" binding="netTcpBinding" contract="OperationLib.IOneWayCallService"/>
   <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
   <endpoint address="mex" binding="mexTcpBinding" contract="IMetadataExchange"/>
</service>  

Defining configuration for DuplexService

XML
<service name="OperationLib.DuplexService" behaviorConfiguration="DuplexServiceBehavior">
        <host>
          <baseAddresses>
            <add baseAddress="http://localhost:9015/DuplexService"/>
            <add baseAddress="net.tcp://localhost:9016/DuplexService"/>
          </baseAddresses>
        </host>

        <endpoint address="http://localhost:9013/DuplexService" binding="wsDualHttpBinding" contract="OperationLib.IDuplexService"/>
        <endpoint address="net.tcp://localhost:9014/DuplexService" binding="netTcpBinding" contract="OperationLib.IDuplexService"/>
        <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
        <endpoint address="mex" binding="mexTcpBinding" contract="IMetadataExchange"/>
      </service>  
Important:

in the Duplex service binding has beed defined as wsDualHttpBinding, unlike the previous interfaces where binding was defined as wsHttpBinding. wsDualHttpBinding provides the same support for web service protocol as WSHttpBinding, but for use with duplex contracts

Defining Behavior(s) for Services.   

XML
<!-- ********************************** behaviors ********************************** -->
<behaviors>
    <serviceBehaviors>
      
        <!-- Single Call Service Behavior -->
        <behavior name="RequestReplyServiceBehavior">
          <serviceMetadata httpGetEnabled="true"/>
          <serviceDebug includeExceptionDetailInFaults="true "/>
        </behavior>

        <!--Single Session Service Behavior -->
        <behavior name="OneWayCallServiceBehavior">
          <serviceMetadata httpGetEnabled="true"/>
          <serviceDebug includeExceptionDetailInFaults="true "/>
        </behavior>

        <!--Singleton Service Behavior -->
        <behavior name="DuplexServiceBehavior" >
          <serviceMetadata httpGetEnabled="true"  />
          <serviceDebug includeExceptionDetailInFaults="true "/>
        </behavior>
        
    </serviceBehaviors>
</behaviors> 
Explanation

each service behavior defines two attributes,   

XML
<serviceMetadata httpGetEnabled="true"  /> 

Gets or sets a value that indicates whether to publich service medtadata for retrieval using an HTTP/GET request, true means yes, metadata is available for retrieval using a HTTP/GET request.

XML
<serviceDebug includeExceptionDetailInFaults="true "/>

Set IncludeExceptionDetailsInFaults to true to enble clients to obtain information about internal service method exceptions; it is only recommended as a way of temporarily debugiing a service application. this property must be set to false on production servers.  

once the configuration is in place, let's write the code to host the services.

Hosting RequestReplyService  
C#
ServiceHost m_RequestReplyHost = null;
try
{
    m_RequestReplyHost = new ServiceHost(typeof(OperationLib.RequestReplyService));
    m_RequestReplyHost.Open();
}
catch (Exception eX)
{
    Console.WriteLine("Failed while starting RequestReplyService [" + eX.Message + "]");
    m_RequestReplyHost = null;
}
if (m_RequestReplyHost != null) Console.WriteLine("RequestReplyService hosted successfully . . .");
Hosting OneWayService   
C#
ServiceHost m_OneWayCallHost = null;
try
{
    m_OneWayCallHost = new ServiceHost(typeof(OperationLib.OneWayCallService));
    m_OneWayCallHost.Open();
}
catch (Exception eX)
{
    Console.WriteLine("Failed while starting OneWayCallService [" + eX.Message + "]");
    m_RequestReplyHost = null;
}
if (m_OneWayCallHost != null) Console.WriteLine("OneWayCallService hosted successfully . . .");
   
Hosting DuplexService  
C#
ServiceHost m_DuplexServiceHost = null;
try
{
    m_DuplexServiceHost = new ServiceHost(typeof(OperationLib.DuplexService));
    m_DuplexServiceHost.Open();
}
catch (Exception eX)
{
    Console.WriteLine("Failed while starting DuplexService [" + eX.Message + "]");
    m_RequestReplyHost = null;
}
if (m_DuplexServiceHost!= null) Console.WriteLine("DuplexService hosted successfully . . .");

Build and Execute the Host.

Open a command prompt and execute the host. here is the output. 

 Image 1

Third Module: Client Application (OperationClient.exe)

To create this project, you can simply take a "Console based Application" project, while choosing from project wizard option. let's name it "OperationClient", 

a new Project  OperationClient will be added to the workspace.   

Generating proxies

While the host application is running, right click on the client application project, click on

Generate Proxy for RequestReplyService

References –> Add Service Reference  

In the address bar type the address of the mex endpoint address of RequestReplyService as shown below. 

 Image 2
 

Generate Proxy for OneWayCallService

References –> Add Service Reference  

In the address bar type the address of the mex endpoint address of OneWayCallService as shown below. 

 Image 3
 

Generate Proxy for DuplexService

References –> Add Service Reference  

In the address bar type the address of the mex endpoint address of DuplexService as shown below. 

 Image 4

Now when you have added references of all 3 services, let’s write code for the client to use these services.

Call RequestReplyService
C#
double dblVal1 = 100; double dblVal2 = 200;
try
    {
    RequestReplyServiceReference.RequestReplyServiceClient obj1 = new RequestReplyServiceReference.RequestReplyServiceClient("WSHttpBinding_IRequestReplyService");
    RequestReplyServiceReference.RequestReplyServiceClient obj2 = new RequestReplyServiceReference.RequestReplyServiceClient("NetTcpBinding_IRequestReplyService");
    Console.WriteLine("\nCalling Request Reply Service");
    double dblResult1 = obj1.AddNumber (dblVal1, dblVal2);
    Console.WriteLine("Using HTTP Binding >> Value 1: {0:F2} Value 2: {1:F2}  Returns : {2:F2}", dblVal1, dblVal2, dblResult1);
    dblVal1 = 100; dblVal2 = 200;
    double dblResult2 = obj2.AddNumber(dblVal1, dblVal2);
    Console.WriteLine("Using TCP Binding >> Value 1: {0:F2} Value 2: {1:F2}  Return : {2:F2}", dblVal1, dblVal2, dblResult2);
}
catch (Exception eX)
{
Console.WriteLine("Error while calling Request Reply Service [ " + eX.Message + "]");
} 
 Explanation

Create the object from the Service proxy and call the method, and display the result.

Calling OneWayCallService
try
{
    OneWayCallServiceReference.OneWayCallServiceClient obj3 = new OneWayCallServiceReference.OneWayCallServiceClient("WSHttpBinding_IOneWayCallService");
    OneWayCallServiceReference.OneWayCallServiceClient obj4 = new OneWayCallServiceReference.OneWayCallServiceClient("NetTcpBinding_IOneWayCallService");
    Console.WriteLine("\nCalling OneWayCall Service");

    obj3.AddNumber(dblVal1, dblVal2 );
    double dblResult3 = obj3.GetResult();
    Console.WriteLine("Using HTTP Binding >> Value 1: {0:F2} Value 2: {1:F2}", dblVal1, dblVal2 );
    Console.WriteLine("Result : {0:F2}", dblResult3);

    obj4.AddNumber (dblVal1, dblVal2);
    double dblResult4 = obj4.GetResult();
    Console.WriteLine("Using TCP Binding >> Value 1: {0:F2} Value 2: {1:F2}", dblVal1, dblVal2);
    Console.WriteLine("Result : {0:F2}", dblResult4);
}
catch (Exception eX)
{
    Console.WriteLine("Error while calling One way Service [ " + eX.Message + "]");
}

 Explanation

Create the object from the Service proxy and call the method, passing values, as the method is defined as OneWay, so it does not return any value.   

call another method to collect the result.

double dblResult3 = obj4.GetResult(); 

and

double dblResult4 = obj4.GetResult(); 
     
Calling Duplex Service

Before you call a duplex service, you need to do some home work. you need to implement the Callback Interface at the client side. so define a class which is derived from IDuplexServiceCallback. let's call it DuplexServiceCallbackSink

 Implementing IDuplexServiceCallback interface at the client side 
C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace OperationClient
{
    class DuplexServiceCallbackSink : DuplexServiceReference.IDuplexServiceCallback
    {
        public void OnNumberAdded(double dblNum1, double dblNum2, double dblResult)
        {
            Console.WriteLine(">>  Duplex Service <<CallBack>> Value 1 : {0:F2} Value 2 : {1:F2} Result : {2:F2}", dblNum1, dblNum2, dblResult);
        }
    }
}

Now, when you have written the implementation for the Callback Interface at the client side, it is now time to call the Duplex Service. 

C#
try
{
    DuplexServiceCallbackSink callback = new DuplexServiceCallbackSink();
    InstanceContext insCntxt = new InstanceContext(callback);

    DuplexServiceReference.DuplexServiceClient obj5 = new DuplexServiceReference.DuplexServiceClient(insCntxt, "WSDualHttpBinding_IDuplexService");
    DuplexServiceReference.DuplexServiceClient obj6 = new DuplexServiceReference.DuplexServiceClient(insCntxt, "NetTcpBinding_IDuplexService");

    Console.WriteLine("\nCalling Duplex Service");

    obj5.AddNumber(dblVal1, dblVal2);
    obj6.AddNumber(dblVal1, dblVal2);
}
catch (Exception eX)
{
	Console.WriteLine("Error while calling Duplex Service [ " + eX.Message + "]");
}  
Explanation

Create the Callback Sink, as the Callback method invoke expect an implementation object, who implements the callback method.   

C#
DuplexServiceCallbackSink callback = new DuplexServiceCallbackSink();
InstanceContext insCntxt = new InstanceContext(callback);  

 Call the service using appropriate binding, passing the InstanceContext object.

C#
DuplexServiceReference.DuplexServiceClient obj5 = new DuplexServiceReference.DuplexServiceClient(insCntxt, "WSDualHttpBinding_IDuplexService");
obj5.AddNumber(dblVal1, dblVal2);

or/and

C#
DuplexServiceReference.DuplexServiceClient obj6 = new DuplexServiceReference.DuplexServiceClient(insCntxt, "NetTcpBinding_IDuplexService");
obj6.AddNumber(dblVal1, dblVal2);

and here is the collective output of the client.

 Image 5

Points of Interest 

Explains the working the different operation modes of a WCF Service. 

History 

Initial  version.

License

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