A duplex contract allows clients and servers to communicate
with each other independently so
that either can initiate calls to the
other.
The primary interface is used to send messages from client to service.
The callback interface is used to send messages from service back to client.
All Binding does not support duplex service. wsDualHttpBinding supports duplex communication over HTTP binding
Duplex communication is possible over netTcpBinding and netNamedPipeBinding
Example
:
Database Table
GO
/****** Object:
Table [dbo].[Emp] Script Date:
03/08/2013 15:06:31 ******/
SET ANSI_NULLS
ON
GO
SET QUOTED_IDENTIFIER
ON
GO
SET ANSI_PADDING
ON
GO
CREATE TABLE
[dbo].[Emp](
[AutoID] [int] IDENTITY(1,1) NOT NULL,
[Name] [varchar](50) NULL,
[Designation] [varchar](50) NULL,
CONSTRAINT
[PK_Emp] PRIMARY KEY
CLUSTERED
(
[AutoID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS
= ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON
[PRIMARY]
GO
SET ANSI_PADDING OFF
Project Structure :
Service Interface
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;
namespace WCF_DuplexCallBack
{
// IN service contract we define Callback contract
Interface eg: INotifyUser
[ServiceContract(SessionMode = SessionMode.Required, CallbackContract = typeof(INotifyUser))]
public interface IDuplexCallBack
{
[OperationContract]
int AddEmp(string
Name, string Designation); // method to add employee
[OperationContract]
int DeleteEmp(Int32
EmpID); //
method to delete employee
}
Declare the method signatures in the callback interface.
// Call Back Interface
public interface INotifyUser
{
//Create an operation contract in the Interface
//Make sure return type is Void.
//Make sure IsOneWayProperty
is set to true.
//There is no need to mark call back interface as
ServiceContract.
[OperationContract(IsOneWay = true)]
void Initilizing(string
Value);
[OperationContract(IsOneWay = true)]
void NotifyCollection(string
message);
[OperationContract(IsOneWay = true)]
void NotifyUserDeleted(string
message);
}
}
Service :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;
namespace WCF_DuplexCallBack
{
// Service Implementation
// set the instance mode of the service to percall
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
public class DuplexCallBack : IDuplexCallBack
{
INotifyUser callback = null;
public int AddEmp(string EmpName, string
EmpDesignation)
{
//Create a call back
channel (OperationConetxt to get
current call back channel)
INotifyUser callback= OperationContext.Current.GetCallbackChannel<INotifyUser>();
//instance of INotifyUser can be used to make
call functions in call back interface contract at the client side
callback.Initilizing("Process
Started for adding data for employee :
" + EmpName + " Designation :" +
EmpDesignation);
// Save data using entity framework
int retval = 0;
Emp em = new Emp();
em.Name
= EmpName;
em.Designation = EmpDesignation;
using (LinqTestEntities
ctx = new LinqTestEntities())
{
ctx.Emps.AddObject(em);
retval = ctx.SaveChanges();
var empdata = from
total in ctx.Emps
where total.Designation == EmpDesignation
select total;
// calling call back function
callback.NotifyCollection("Total for designation " +
EmpDesignation + " " +
empdata.Count());
}
return retval;
}
// Delete employee function
public int DeleteEmp(Int32 EmpID)
{
//Create a call back channel (OperationConetxt to get
current call back channel)
INotifyUser callback = OperationContext.Current.GetCallbackChannel<INotifyUser>();
int retval = 0;
Emp em
= new Emp();
using (LinqTestEntities
ctx = new LinqTestEntities())
{
var empdata = (from e
in ctx.Emps
where e.AutoID == EmpID
select e).FirstOrDefault();
if (empdata != null)
{
// calling call back function
callback.Initilizing("Process Started
for deleteing employee having designation
" + empdata.Designation);
ctx.Emps.DeleteObject(empdata);
retval = ctx.SaveChanges();
var emp = from
total in ctx.Emps
where total.Designation == empdata.Designation
select
total;
// calling call back function
callback.NotifyCollection("Total
remains for designation " + empdata.Designation + " " + emp.Count());
}
else
{
// calling call back function
callback.Initilizing("Unable to delete
because Employee doesn't exist for ID : " + EmpID);
}
}
return retval;
}
}
}
Webconfig :
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<system.web>
<compilation debug="true" targetFramework="4.0">
<assemblies>
<add assembly="System.Data.Entity,
Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
</assemblies>
</compilation>
</system.web>
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior>
<!-- To avoid
disclosing metadata information, set the value below to false and remove the
metadata endpoint above before deployment -->
<serviceMetadata httpGetEnabled="true" />
<!-- To receive
exception details in faults for debugging purposes, set the value below to
true. Set to false before deployment to
avoid disclosing exception information -->
<serviceDebug includeExceptionDetailInFaults="false" />
</behavior>
</serviceBehaviors>
</behaviors>
<serviceHostingEnvironment multipleSiteBindingsEnabled="false" />
<services>
<service name="WCF_DuplexCallBack.DuplexCallBack">
<endpoint address="http://localhost:54500/DuplexCallBack.svc" binding="wsDualHttpBinding" contract="WCF_DuplexCallBack.IDuplexCallBack" />
</service>
</services>
</system.serviceModel>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true" />
</system.webServer>
<connectionStrings>
<add name="LinqTestEntities" connectionString="metadata=res://*/Model1.csdl|res://*/Model1.ssdl|res://*/Model1.msl;provider=System.Data.SqlClient;provider
connection string="data source=VAMDEV;initial catalog=LinqTest;user
id=dev;password=c0nNd3v;multipleactiveresultsets=True;App=EntityFramework"" providerName="System.Data.EntityClient" />
</connectionStrings>
</configuration>
Client :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using DuplexClient.DuplexServiceClient;
using System.ServiceModel;
namespace DuplexClient
{
// Callback class MyCallBack implimenting interface
IDuplexCallBackCallback
//Note: Since name of the service contract Interface is
(IDuplexCallBack),
//so the call back interface name will be
(IDuplexCallBackCallback).
// It is name of service contract suffixes by keyword
CallBack.
class MyCallBack :
IDuplexCallBackCallback
{
//callback functions
public void
Initilizing(string value)
{
Console.WriteLine("\t
" + value);
}
public void
NotifyCollection(string message)
{
Console.WriteLine("\t
" + message);
}
public void
NotifyUserDeleted(string message)
{
Console.WriteLine("\t
" + message);
Console.WriteLine("\n");
}
}
class Program
{
static void Main(string[] args)
{
//Create an instance of InstanceContext and pass the
reference of current class in the constructor
//Instance Context represents context information of a
service.
// service reference or proxy is used to send messages to
server and Instance Context is used to accept incoming messages
InstanceContext instanceContext = new InstanceContext(new MyCallBack());
//create proxy object
DuplexCallBackClient client = new DuplexCallBackClient(instanceContext);
client.AddEmp("Emp1", "Manager");
Console.WriteLine("");
client.AddEmp("Emp2", "Manager");
Console.WriteLine("");
client.AddEmp("Emp3", "Director");
Console.WriteLine("");
client.AddEmp("Emp4", "Director");
Console.WriteLine("");
client.DeleteEmp(3);
Console.WriteLine("");
Console.ReadLine();
}
}
}
OutPut :
Output
Explanation:
In
client side we add 4 employees with two roles Manager and Directors .
In
service Addemp method implementation we call two methods Initilizing and NotifyCollection to notify client about what is happening on server side. Method Initilizing notify
client that process of adding client started and method NotifyCollection notify client about total number of users of
Current role
Note : In duplex contact server and
client using same communication channel to communicate so some time we face
error like port 80 is used by other application .
Solution of current problem is
just to change client base address in web config file and add service reference
again
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<system.web>
<compilation debug="true" targetFramework="4.0">
<assemblies>
<add assembly="System.Data.Entity,
Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
</assemblies>
</compilation>
</system.web>
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior>
<!-- To avoid disclosing metadata information, set the value below to
false and remove the metadata endpoint above before deployment -->
<serviceMetadata httpGetEnabled="true" />
<!-- To receive exception details in faults for debugging purposes,
set the value below to true. Set to
false before deployment to avoid disclosing exception information -->
<serviceDebug includeExceptionDetailInFaults="false" />
</behavior>
</serviceBehaviors>
</behaviors>
<serviceHostingEnvironment multipleSiteBindingsEnabled="false" />
<bindings>
<wsDualHttpBinding>
<binding clientBaseAddress="http://locahost:6666/" name="WSDualHttpBinding_IPingService" 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">
<security mode="Message">
<message clientCredentialType="Windows" negotiateServiceCredential="true"
algorithmSuite="Default" />
</security>
</binding>
</wsDualHttpBinding>
</bindings>
<services>
<service name="WCF_DuplexCallBack.DuplexCallBack">
<endpoint address="http://localhost:54500/DuplexCallBack.svc" binding="wsDualHttpBinding" contract="WCF_DuplexCallBack.IDuplexCallBack" />
</service>
</services>
</system.serviceModel>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true" />
</system.webServer>
<connectionStrings>
<add name="LinqtestEntities" connectionString="metadata=res://*/Model1.csdl|res://*/Model1.ssdl|res://*/Model1.msl;provider=System.Data.SqlClient;provider
connection string="Data Source=VIKAS-PC;Initial Catalog=Linqtest;User
ID=sa;Password=c0nNd3v;MultipleActiveResultSets=True"" providerName="System.Data.EntityClient" />
</connectionStrings>
</configuration>
No comments :
Post a Comment