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 ,
![Image1.gif]()
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.
![Image2.gif]()
The error message is operation would deadlock because the reply cannot be
received until the current message completes processing.
![Image3.gif]()
So how to solve the above exception?
We will have to configure the service behavior for reentrancy.
![Image4.gif]()
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,