Objective:
PART 1 of this article is here
This article will explain how to return values in call back or duplex operation.
Before reading this article, I strongly recommend to read my first article post
on this series. This article is fully based on the A Simple Duplex Service in
WCF.
If you read above article, you will notice below points
-
The entire operation contract is configured for one way operation.
- Return type of the entire operation contract is void.
Now assume there is a requirement
-
To have request and reply on operation contract.
- To return some value from functions rather than void.
I mean we want signature of call back operation contract and operation of
service contract as below ,
Now if we modify contract as below,
IService1.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Web;
using System.Text;
namespace WcfService3
{
[ServiceContract(CallbackContract=typeof(IMyContractCallBack))]
public interface IService1
{
[OperationContract]
string NormalFunction();
}
public interface IMyContractCallBack
{
[OperationContract]
string CallBackFunction(string
str);
}
}
And modify the Service implementation as below,
Service1.svc.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Web;
using System.Text;
namespace WcfService3
{
[ServiceBehavior(InstanceContextMode=InstanceContextMode.PerCall)]
public class Service1 : IService1
{
public string
NormalFunction()
{
IMyContractCallBack
callback = OperationContext.Current.GetCallbackChannel<IMyContractCallBack>();
return callback.CallBackFunction("Calling from Call Back");
}
}
}
And service is configured as below,
Web.Config
<?xml version="1.0"?>
<configuration>
<system.web>
<compilation debug="true" targetFramework="4.0" />
</system.web>
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior name ="svcbh">
<serviceMetadata httpGetEnabled="False"/>
<serviceDebug includeExceptionDetailInFaults="False"/>
</behavior>
</serviceBehaviors>
</behaviors>
<!--<serviceHostingEnvironment multipleSiteBindingsEnabled="true"
/>-->
<services>
<service name ="WcfService3.Service1" behaviorConfiguration ="svcbh" >
<host>
<baseAddresses>
<add baseAddress = "http//localhost:9000/Service1/" />
</baseAddresses>
</host>
<endpoint name ="duplexendpoint"
address =""
binding ="wsDualHttpBinding"
contract ="WcfService3.IService1"/>
<endpoint name ="MetaDataTcpEndpoint"
address="mex"
binding="mexHttpBinding"
contract="IMetadataExchange"/>
</service>
</services>
</system.serviceModel>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true"/>
</system.webServer>
</configuration>
Once we consume the above service in a console application and modify the duplex
proxy class as below,
MyCallBack.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ConsoleApplication1.ServiceReference1;
using System.ServiceModel;
namespace ConsoleApplication1
{
class MyCallBack :IService1Callback
,IDisposable
{
Service1Client
proxy;
public string
CallBackFunction(string str)
{
return
str;
}
public void callService()
{
InstanceContext
context = new InstanceContext(this);
proxy = new
Service1Client(context);
Console.WriteLine(proxy.NormalFunction());
}
public void Dispose()
{
proxy.Close();
}
}
}
And at the client side,
Programs.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ConsoleApplication1.ServiceReference1;
using System.ServiceModel;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[]
args)
{
MyCallBack
obj = new MyCallBack();
obj.callService();
Console.Read();
obj.Dispose();
}
}
}
And after consumption of service try to run the client, you will get an
exception while trying to call the operation contract from call back function.
The error message is operation would deadlock because the reply cannot be
received until the current message completes processing.
So how to solve the above exception?
We will have to configure the service behavior for reentrancy.
By setting the concurrency mode as Reentrant
-
Service will access the operation context and call back reference and then
invokes it.
- After the call back returns control will go to service and the lock will get
reallocated to thread.
So now modified Service implementation will be as below,
Service1.svc.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Web;
using System.Text;
namespace WcfService3
{
[ServiceBehavior(ConcurrencyMode
=ConcurrencyMode.Reentrant)]
public class Service1 : IService1
{
public string
NormalFunction()
{
IMyContractCallBack
callback = OperationContext.Current.GetCallbackChannel<IMyContractCallBack>();
return callback.CallBackFunction("Calling from Call Back");
}
}
}
Now on running the client after service consumption, we will get output as
below,