This topic describes how to create a duplex Windows Communication Foundation (WCF) service that can communicate with a Silverlight client. A duplex service maintains a callback channel to the Silverlight client, which allows the service to make calls to the client at any given time. Duplex services have numerous applications including, for example, a chat server for instant messaging or a monitoring service that sends notifications to the client.
In duplex messaging, both service and client expose service contracts, which specify the operations that each of them exposes. In this example, we use the following pair of service contracts.
[ServiceContract(Namespace="Silverlight", CallbackContract = typeof(IDuplexClient))]
public interface IDuplexService
{
[OperationContract(IsOneWay = true)]
void Order(Message receivedMessage);
}
[ServiceContract]
public interface IDuplexClient
{
[OperationContract(IsOneWay = true)]
void Receive(Message returnMessage);
}
Note that the >CallbackContract property is used to specify the contract for the service to call back the client.
Also note that the >IsOneWay property must be set to true in the >OperationContractAttribute for each service method that is part of the service contract and that, similarly, the IsOneWay property must be set to true in the OperationContractAttribute for each client method that is part of the service contract. This property indicates that these operations do not return reply messages.
To create a WCF duplex service to work with Silverlight
-
Open Visual Studio 2008.
-
From the File menu, select New, select Project, specify Web from the Project types listed on the left from within the .NET language (C# or Visual Basic) group you want to code in, then select WCF Service Application from the Templates on the right, name it DuplexService in the Name box below, and click OK.
Right-click the Web.config file in the Solution Explorer and select Delete to remove it from the project.
-
Right-click the DuplexService project in Solution Explorer and select Add Reference…. Click the Browse tab and navigate to the assembly System.ServiceModel.PollingDuplex.dll assembly located in %ProgramFiles%\Microsoft SDKs\Silverlight\v2.0\Libraries\Server\ Directory, select it, and click OK.
.gif) |
|---|
| Also note that there are two assemblies named System.ServiceModel.PollingDuplex.dll that ship with the Silverlight version 2 SDK. One of them is used in duplex WCF services, and the other one is used in duplex Silverlight clients. It is important to follow the preceding steps correctly to the right location, so that you reference the correct assembly for the duplex service. |
Add the following using statements to the top of IService1.cs.
using System.ServiceModel;
using System.ServiceModel.Channels;
-
Replace the default contents of the IService1.cs file with the following code for the DuplexService namespace. This defines the contracts used by both the service and the client.
namespace DuplexService
{
[ServiceContract(Namespace="Silverlight",
CallbackContract = typeof(IDuplexClient))]
public interface IDuplexService
{
[OperationContract(IsOneWay = true)]
void Order(Message receivedMessage);
}
[ServiceContract]
public interface IDuplexClient
{
[OperationContract(IsOneWay = true)]
void Receive(Message returnMessage);
}
}
Add the following using statements to the top of Service1.svc.cs.
using System.ServiceModel.Channels;
using System.Threading;
-
Edit the contents of the file Service1.svc.cs to remove all classes inside the DuplexService namespace declaration. Then define the OrderService class as shown as shown in the following code.
public class OrderService : IDuplexService
{
IDuplexClient client;
public void Order(Message receivedMessage)
{
// Grab the client callback channel.
client = OperationContext.Current.GetCallbackChannel<IDuplexClient>();
// Pretend that the service is processing and will call the client back in 5 seconds.
using (Timer timer = new Timer(new TimerCallback(CallClient),
receivedMessage.GetBody<string>(), 5000, 5000))
{
Thread.Sleep(11000);
}
}
}
The Order operation simulates the execution of a process of ordering an item from an online store. The order is received, and subsequently a series of actions takes place outside the scope of our service (for example, checking for item availability, verifying the user’s credit card information, and so forth). After these actions are complete, the service needs to call the client back (“push” information to the client) to notify it that the order has been completed successfully. We simulate this scenario by using a >Timer, which calls the service twice after two 5-second intervals.
Note the use of the >GetCallbackChannel method to obtain a callback channel of type IDuplexClient, which can be used to contact the Silverlight client after it is implemented and running.
-
Inside the OrderService class, add the following.
bool processed = false;
private void CallClient(object order)
{
string text;
// Process order
if (processed)
{
text = (string) order + " order complete";
}
else
{
text = "Processing " + (string) order + " order";
processed = true;
}
// Construct the message to return to the client.
Message returnMessage = Message.CreateMessage(MessageVersion.Soap11,
"Silverlight/IDuplexService/Receive", text);
// Call the client back.
client.Receive(returnMessage);
}
This method is invoked twice to simulate the order-processing activity on the service. The first time it is invoked it will report that the order is being processed, and the second time it is invoked it will report that the order is complete.
The last two lines of code in the method construct a return >Message and use the callback channel to send that message back to the client. Note that when constructing the message, >MessageVersion.Soap11 must be used because duplex services support only SOAP BP 1.1 messages with no Addressing. Also, the message action must match the name of the operation in the IDuplexClient contract.
-
The next step is to create a service host to host the service that we just created. Add a new file called PollingDuplexServiceHostFactory.cs to the project, and paste the following code inside of it.
using System;
using System.ServiceModel;
using System.ServiceModel.Activation;
using System.ServiceModel.Channels;
namespace DuplexService
{
public class PollingDuplexServiceHostFactory : ServiceHostFactoryBase
{
public override ServiceHostBase CreateServiceHost(string constructorString,
Uri[] baseAddresses)
{
return new PollingDuplexSimplexServiceHost(baseAddresses);
}
}
class PollingDuplexSimplexServiceHost : ServiceHost
{
public PollingDuplexSimplexServiceHost(params System.Uri[] addresses)
{
base.InitializeDescription(typeof(OrderService), new UriSchemeKeyedCollection(addresses));
}
protected override void InitializeRuntime()
{
// Define the binding and set time-outs.
PollingDuplexBindingElement pdbe = new PollingDuplexBindingElement()
{
ServerPollTimeout = TimeSpan.FromSeconds(3),
InactivityTimeout = TimeSpan.FromMinutes(1)
};
// Add an endpoint for the given service contract.
this.AddServiceEndpoint(
typeof(IDuplexService),
new CustomBinding(
pdbe,
new TextMessageEncodingBindingElement(
MessageVersion.Soap11,
System.Text.Encoding.UTF8),
new HttpTransportBindingElement()),
"");
base.InitializeRuntime();
}
}
}
The PollingDuplexBindingElement configures the WCF service for duplex communication with a Silverlight client. When configured with this binding, the Silverlight client periodically polls the service on the network layer, and checks for any new messages that the service wants to send on the callback channel. The service queues all messages sent on the client callback channel and delivers them to the client when the client polls the service. The ServerPollTimeout property determines the length of time (in milliseconds) that the service holds a poll from the client before returning. The InactivityTimeout property determines the length of time (in milliseconds) that can elapse without any message exchange with the client before the service closes its session. Note that message exchange in the preceding context refers to real messages sent by methods on the service or client, not polls on the network layer.
The call to >AddServiceEndpoint adds a new endpoint to the service host. This endpoint exposes the IDuplexService contract .
-
Edit the Service1.svc file to enable the service host defined in the previous step. Right-click the Service1.svc file in Solution Explorer, select View Markup, and paste the following service directive over the default directive.
<%@ServiceHost language="c#" Debug="true" Factory="DuplexService.PollingDuplexServiceHostFactory"%>
-
The duplex service is now ready to be tested. Select the Service1.svc file in Solution Explorer, right-click, and select View in Browser (or press Ctrl + F5) to display a test page for the service. You should see the service test page, which confirms the service is available.
The duplex service is now available, and we can construct a Silverlight client for it, as shown in How to: Access a Duplex Service with the Channel Model.
Example
The Service1.svc.cs file should have the following contents.
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.Threading;
namespace DuplexService
{
public class OrderService : IDuplexService
{
IDuplexClient client;
public void Order(Message receivedMessage)
{
// Grab the client callback channel.
client = OperationContext.Current.GetCallbackChannel<IDuplexClient>();
// Pretend that the service is processing and will call the client back in 5 seconds.
using (Timer timer = new Timer(new TimerCallback(CallClient),
receivedMessage.GetBody<string>(), 5000, 5000))
{
Thread.Sleep(11000);
}
}
bool processed = false;
private void CallClient(object order)
{
string text;
// Process the order.
if (processed)
{
text = (string)order + " order complete";
}
else
{
text = "Processing " + (string)order + " order";
processed = true;
}
// Construct the message to return to the client.
Message returnMessage = Message.CreateMessage(MessageVersion.Soap11,
"Silverlight/IDuplexService/Receive", text);
// Call the client back.
client.Receive(returnMessage);
}
}
}