ARTICLE

Callback operations in windows communication foundation.

Posted by John Charles Olamendy Articles | Web Service in VB.NET April 29, 2008
This article is intended to illustrate how to implement callback operations in WCF through a common business scenario.
 
Reader Level:

Introduction

This article is intended to illustrate how to implement callback operations in Windows Communication Foundation through a common business scenario where the service needs to notify that some events has happened to the client. During a callback, in many aspects the tables are turned: the service becomes the client, and the client becomes the server. So, we need to develop an interesting application supporting the solution.

Implementing the solution

The first thing to know before implementing this approach is that not all bindings support callback operations and only bidirectional-capable bindings can be used. Due to the connectionless nature of HTTP, this transport protocol cannot be used for callbacks, that's, you cannot use the BasicHttpBinding and WSHttpBinding for this purpose. In order to support callbacks in your application, WCF provides the WSDualHttpBinding, which actually sets up two HTTP channels: one for the calls from the client to the server, and one for the calls from the server to the client.

Now, let's create a console application to host the service in Visual Studio.NET and add a reference to the System.ServiceModel assembly.

Defining the callback contract

The callback operations are part of the service contract. A service contract can have at most one callback contract. Once defined, the clients are required to support the callback and also provide the callback endpoint to service in every call.

In our example, we have a service which provides a math service doing some long term calculation. We want to be notified when the calculation operation begins. The service contract attribute annotates our contract and offers a CallbackContract property of the type Type setting up our callback contract, as shown in Listing 1.

Imports System

Imports System.Collections.Generic

Imports System.Text

Imports System.ServiceModel

 

Namespace CallbackApp

    Public Interface IMathCalculationCallback

        <OperationContract()> _

        Sub OnCalculating()

    End Interface

 

    <ServiceContract(CallbackContract:=GetType(IMathCalculationCallback))> _

    Public Interface IMathCalculation

        <OperationContract()> _

        Sub DoLongCalculation(ByVal nParam1 As Integer, ByVal nParam2 As Integer)

    End Interface

End Namespace

Listing 1. Definition of the service contract and the associated callback contract

The service implementation

In order to invoke the client callback from the service, we need a reference to the callback object. When the client invokes the service operations, it supplies a callback channel for the communication with the server through the callback. This channel can be referenced from the server by calling the GetCallbackChannel operation on the global OperationContext instance, as shown in the Listing 2. The DoLongCalculation operation does some long initialization, then notifies to the client the a complex operation begins now (in our case this complex operation is nParam1 + nParam2, I guess it is joke but with a delay System.Threading.Thread.Sleep(10000)).

<ServiceBehavior(ConcurrencyMode:=ConcurrencyMode.Reentrant)> _

Public Class MathCalculationService

    Implements IMathCalculation

    Public Function DoLongCalculation(ByVal nParam1 As Integer, ByVal nParam2 As Integer) As Integer

        System.Threading.Thread.Sleep(10000)

        Dim objCallback As IMathCalculationCallback = OperationContext.Current.GetCallbackChannel(Of IMathCalculationCallback)()

        If objCallback IsNot Nothing Then

            objCallback.OnCalculating()

        End If

        System.Threading.Thread.Sleep(10000)

 

        Return nParam1 + nParam2

    End Function

End Class 

Listing 2. The MathCalculationService implementation

You may notice that the service is invoking the callback reference while executing the operation DoLongCalculation. By default the service class is configure for single-threaded access, thus the service is associated with a lock, and only one thread at a time can own the lock and access the service instance. When the service invokes the callback reference while executing one of its operations, the service thread is blocked, because the thread which is processing the reply message from the client once the callback returns a message response requires ownership of the same lock, so a deadlock situation occurs. To avoid this situation, there are three possibilities:

  • Configure the service for multi-threaded access. The drawback is that the developer needs to provide synchronization codes.
  • Configure the service for reentrancy. The service is associated with a lock, and only one thread is allowed to access the service, however if the service is calling back to its clients, WCF will release the lock first. This is the strategy that I follow in my example, and I implemented it by decorating the service class with the attribute ServiceBehavior and setting the property ConcurrencyMode to ConcurrencyMode.Reentrant as shown in Listing 2.
  • Configure the operations as one-way operation because there is no reply message to contend for the lock.

The application's main workflow is shown in Listing 3.
 

Class Program

    Private Shared Sub Main(ByVal args As String())

        MathCalculationServiceHost.StartService()

        System.Console.WriteLine("Please, press any key to finish ...")

        System.Console.Read()

    End Sub

End Class

Listing 3. The service application's main workflow

And finally the configuration file is shown in Listing 4.

<?xml version="1.0" encoding="utf-8" ?>

<configuration>

          <system.serviceModel>

                    <services>

                             <service name="CallbackApp.MathCalculationService" behaviorConfiguration="MathCalculationServiceBeh">

                                       <endpoint contract="CallbackApp.IMathCalculation" binding="wsDualHttpBinding"/>

                             </service>

                    </services>

                    <behaviors>

                             <serviceBehaviors>

                                       <behavior name="MathCalculationServiceBeh" >

                                                <serviceDebug includeExceptionDetailInFaults="true" />

                                                <serviceMetadata httpGetEnabled="true" />

                                       </behavior>

                             </serviceBehaviors>

                    </behaviors>

          </system.serviceModel>

</configuration>

Listing 4. The service application's configuration file

Developing the client side

Now, add another application to the solution and name it CallbackClientApp, and also add a reference to the System.ServiceModel assembly.

In order to generate the proxy class, you need to open command windows and change to the directory of the client application, and run the following line command shown in Listing 5. As you can see the proxy inherits from the class System.ServiceModel.DuplexClientBase.

svcutil http://localhost:8080/CallbackApp/MathCalculationService.svc?wsdl

Listing 5.

The generated proxy is shown in Listing 6.

'------------------------------------------------------------------------------

' <auto-generated>

' This code was generated by a tool.

' Runtime Version:2.0.50727.42

'

' Changes to this file may cause incorrect behavior and will be lost if

' the code is regenerated.

' </auto-generated>

'------------------------------------------------------------------------------

 

Namespace CallbackClientApp

 

    <System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")> _

    <System.ServiceModel.ServiceContractAttribute(ConfigurationName:="IMathCalculation", CallbackContract:=GetType(IMathCalculationCallback))> _

    Public Interface IMathCalculation

 

        <System.ServiceModel.OperationContractAttribute(Action:="http://tempuri.org/IMathCalculation/DoLongCalculation", ReplyAction:="http://tempuri.org/IMathCalculation/DoLongCalculationResponse")> _

        Function DoLongCalculation(ByVal nParam1 As Integer, ByVal nParam2 As Integer) As Integer

    End Interface

 

    <System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")> _

    Public Interface IMathCalculationCallback

 

        <System.ServiceModel.OperationContractAttribute(Action:="http://tempuri.org/IMathCalculation/OnCalculating", ReplyAction:="http://tempuri.org/IMathCalculation/OnCalculatingResponse")> _

        Sub OnCalculating()

    End Interface

 

    <System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")> _

    Public Interface IMathCalculationChannel

        Inherits IMathCalculation

        Inherits System.ServiceModel.IClientChannel

    End Interface

 

    <System.Diagnostics.DebuggerStepThroughAttribute()> _

    <System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")> _

    Partial Public Class MathCalculationClient

        Inherits System.ServiceModel.DuplexClientBase(Of IMathCalculation)

        Implements IMathCalculation

 

        Public Sub New(ByVal callbackInstance As System.ServiceModel.InstanceContext)

            MyBase.New(callbackInstance)

        End Sub

 

        Public Sub New(ByVal callbackInstance As System.ServiceModel.InstanceContext, ByVal endpointConfigurationName As String)

            MyBase.New(callbackInstance, endpointConfigurationName)

        End Sub

 

        Public Sub New(ByVal callbackInstance As System.ServiceModel.InstanceContext, ByVal endpointConfigurationName As String, ByVal remoteAddress As String)

            MyBase.New(callbackInstance, endpointConfigurationName, remoteAddress)

        End Sub

 

        Public Sub New(ByVal callbackInstance As System.ServiceModel.InstanceContext, ByVal endpointConfigurationName As String, ByVal remoteAddress As System.ServiceModel.EndpointAddress)

            MyBase.New(callbackInstance, endpointConfigurationName, remoteAddress)

        End Sub

 

        Public Sub New(ByVal callbackInstance As System.ServiceModel.InstanceContext, ByVal binding As System.ServiceModel.Channels.Binding, ByVal remoteAddress As System.ServiceModel.EndpointAddress)

            MyBase.New(callbackInstance, binding, remoteAddress)

        End Sub

 

        Public Function DoLongCalculation(ByVal nParam1 As Integer, ByVal nParam2 As Integer) As Integer

            Return MyBase.Channel.DoLongCalculation(nParam1, nParam2)

        End Function

    End Class

 

End Namespace

Listing 6. The generated proxy

In order to use the callback capabilities the client application needs to create an instance of a class which implements the callback logic, host it in a context, create the proxy and call the service passing the callback instance's reference.

Let's define the callback class as shown in Listing 7.

Class MathCalculationCallback

    Implements IMathCalculationCallback

#Region "IMathCalculationCallback Members"

    Public Sub OnCalculating()

        System.Console.WriteLine("The server begins to calculate, please for a moment ...")

    End Sub

#End Region

End Class

Listing 7. The callback class definition

Then, we define the client's main workflow as shown in Listing 8.

Class Program

    Private Shared Sub Main(ByVal args As String())

        Dim objCallback As IMathCalculationCallback = New MathCalculationCallback()

        Dim objContext As New InstanceContext(objCallback)

 

        Dim nResult As Integer = 0

        Using objProxy As New MathCalculationClient(objContext)

            nResult = objProxy.DoLongCalculation(1, 2)

        End Using

 

        System.Console.WriteLine("The result is {0}", nResult)

 

        System.Console.WriteLine("Press any key to finish ...")

        System.Console.Read()

    End Sub

End Class

Listing 8. The client's main workflow

And the configuration file is shown in Listing 9.

<?xml version="1.0" encoding="utf-8"?>

<configuration>

          <system.serviceModel>

                    <bindings>

                             <wsDualHttpBinding>

                                       <binding name="WSDualHttpBinding_IMathCalculation" closeTimeout="00:01:00"

                    openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00"

                    bypassProxyOnLocal="false" transactionFlow="false" hostNameComparisonMode="StrongWildcard"

                    maxBufferPoolSize="524288" maxReceivedMessageSize="65536"

                    messageEncoding="Text" textEncoding="utf-8" useDefaultWebProxy="true">

                                                <readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"

                        maxBytesPerRead="4096" maxNameTableCharCount="16384" />

                                                <reliableSession ordered="true" inactivityTimeout="00:10:00" />

                                                <security mode="Message">

                                                          <message clientCredentialType="Windows" negotiateServiceCredential="true"

                            algorithmSuite="Default" />

                                                </security>

                                       </binding>

                             </wsDualHttpBinding>

                    </bindings>

                    <client>

                             <endpoint address="http://localhost:8080/CallbackApp/MathCalculationService.svc"

                binding="wsDualHttpBinding" bindingConfiguration="WSDualHttpBinding_IMathCalculation"

                contract="IMathCalculation" name="WSDualHttpBinding_IMathCalculation">

                             </endpoint>

                    </client>

          </system.serviceModel>

</configuration>

Listing 9. The configuration file

Let's see the client application's output in Figure 1.

Figure 1: The client application's output.

Conclusion

In this article, I covered the main concepts and strategies to develop a WCF service which notifies events to the client using callback operations, and how the client must implement the logic associated to the event handling.

NOTE: THIS ARTICLE IS CONVERTED FROM C# TO VB.NET USING A CONVERSION TOOL. ORIGINAL ARTICLE CAN BE FOUND ON C# Corner (http://www.c-sharpcorner.com/).

Login to add your contents and source code to this article
share this article :
post comment
 
Team Foundation Server Hosting
Become a Sponsor
PREMIUM SPONSORS
  • ceTE software specializes in components for dynamic PDF generation and manipulation. The DynamicPDF™ product line allows you to dynamically generate PDF documents, merge PDF documents and new content to existing PDF documents from within your applications.
    Finally – a virtual platform that delivers next-generation Windows Server 2008 Hyper-V virtualization technology from a managed hosting partner you can truly depend on. Visit www.maximumasp.com/max for a FREE 30 day trial. Hurry offer ends soon. Climb aboard the MaxV platform and take advantage of High Availability, Intelligent Monitoring, Recurrent Backups, and Scalability – with no hassle or hidden fees. As a managed hosting partner focused solely on Microsoft technologies since 2000, MaximumASP is uniquely qualified to provide the superior support that our business is built on. Unparalleled expertise with Microsoft technologies lead to working directly with Microsoft as first to offer IIS 7 and SQL 2008 betas in a hosted environment; partnering in the Go Live Program for Hyper-V; and product co-launches built on WS 2008 with Hyper-V technology.
Team Foundation Server Hosting
Become a Sponsor