How to: Build a Duplex Service for a Silverlight Client
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(string name, int quantity);
}
[ServiceContract]
public interface IDuplexClient
{
[OperationContract(IsOneWay = true)]
void Receive();
}
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 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 System.ServiceModel.PollingDuplex.dll assembly (located in %ProgramFiles%\Microsoft SDKs\Silverlight\v3.0\Libraries\Server\ directory), select it, and click OK.

Note that there are two assemblies named System.ServiceModel.PollingDuplex.dll that ship with the Silverlight version 3 SDK. One of them (located in %ProgramFiles%\Microsoft SDKs\Silverlight\v3.0\Libraries\Server\ directory), the reference to which you just added, is used by WCF duplex services. The other one (located in %ProgramFiles%\Microsoft SDKs\Silverlight\v3.0\Libraries\Client\ directory) is used in Silverlight duplex 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, and then, subsequently, for the duplex client application. -
Replace the default contents of the IService1.cs file with the following code for the DuplexService namespace. This code uses interfaces to define 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(string name, int quantity); } [ServiceContract] public interface IDuplexClient { [OperationContract(IsOneWay = true)] void Receive(Order order); } }Note that this is the same code that was presented in the introduction to this topic when discussing the nature of duplex communication between WCF services and Silverlight clients and the service contracts exposed by them to define the message patterns used.
Add the following using statement to the top of the Service1.svc.cs file.
using System.Threading;
Functionality from this namespace will be used to simulate the behavior of an actual service in the sample duplex service.
-
Edit the contents of the file Service1.svc.cs to define an
OrderServiceclass that implements theIDuplexServicecontract. First, remove all classes inside theDuplexServicenamespace declaration. Then define theOrderServiceclass as shown as shown in the following code.public class OrderService : IDuplexService { IDuplexClient client; string orderName; int orderQuantity; public void Order(string name, int quantity) { // Grab client callback channel. client = OperationContext.Current.GetCallbackChannel<IDuplexClient>(); orderName = name; orderQuantity = quantity; // Pretend service is processing and will call client back in 5 seconds. using (Timer timer = new Timer(new TimerCallback(CallClient), null, 5000, 5000)) { Thread.Sleep(11000); } } }The
Orderoperation simulates the execution of a process of ordering an item from an online store. The order is received, and subsequently a series of actions typically takes place outside the scope of our service (for example, checking for the availability of an item, 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. To simulate this scenario, use a Timer, which calls the service twice after two 5-second intervals.Note the use of the OperationContext.GetCallbackChannel(T)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
DuplexServicenamespace, add anOrderclass and an auxiliaryOrderStatusenum to represent the information that the service can send back to the client about the order.public class Order { public OrderStatus Status { get; set; } public List<string> Payload { get; set; } } public enum OrderStatus { Processing, Completed }-
Inside the
OrderServiceclass, add the following.bool processed = false; private void CallClient(object o) { Order order = new Order(); order.Payload = new List<string>(); // Process order. if (processed) { // Pretend service is fulfilling the order. while (orderQuantity-- > 0) { order.Payload.Add(orderName + orderQuantity); } order.Status = OrderStatus.Completed; } else { // Pretend service is processing payment. order.Status = OrderStatus.Processing; processed = true; } // Call client back. client.Receive(order); }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 and populate the order
Payloadproperty. -
The next step is to create a service host to host the service that was just created. Right-click the DuplexService project in Solution Explorer, select Add, and then New Item…. From the Add New Item window that opens, select the Code from the Categories column on the left, Code File from the Templates listed on the right, name the new file PollingDuplexServiceHostFactory.cs in the Name box below, and click Add on the lower right. The new PollingDuplexServiceHostFactory.cs file should open automatically. Paste the following code into this file.
using System; using System.ServiceModel; using System.ServiceModel.Activation; using System.ServiceModel.Channels; using System.ServiceModel.Description; 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)); base.Description.Behaviors.Add(new ServiceMetadataBehavior()); } protected override void InitializeRuntime() { // Add an endpoint for the given service contract. this.AddServiceEndpoint( typeof(IDuplexService), new CustomBinding( new PollingDuplexBindingElement(), new BinaryMessageEncodingBindingElement(), new HttpTransportBindingElement()), ""); // Add a metadata endpoint. this.AddServiceEndpoint( typeof(IMetadataExchange), MetadataExchangeBindings.CreateMexHttpBinding(), "mex"); 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 first call in the code to the AddServiceEndpoint method adds a new endpoint to the service host. This endpoint exposes the
IDuplexServicecontract. The second call in the code adds a metadata endpoint to the service, so that Silverlight clients can generate proxies for the service. -
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 ServiceOrder Service test page, which confirms that 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.
Example
The Service1.svc.cs file should have the following contents.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;
using System.Threading;
namespace DuplexService
{
public class OrderService : IDuplexService
{
IDuplexClient client;
string orderName;
int orderQuantity;
public void Order(string name, int quantity)
{
// Grab client callback channel.
client = OperationContext.Current.GetCallbackChannel<IDuplexClient>();
orderName = name;
orderQuantity = quantity;
// Pretend service is processing and will call client back in 5 seconds.
using (Timer timer = new Timer(new TimerCallback(CallClient), null, 5000, 5000))
{
Thread.Sleep(11000);
}
}
bool processed = false;
private void CallClient(object o)
{
Order order = new Order();
order.Payload = new List<string>();
// Process order.
if (processed)
{
// Pretend service is fulfilling the order.
while (orderQuantity-- > 0)
{
order.Payload.Add(orderName + orderQuantity);
}
order.Status = OrderStatus.Completed;
}
else
{
// Pretend service is processing payment.
order.Status = OrderStatus.Processing;
processed = true;
}
// Call client back
client.Receive(order);
}
}
public class Order
{
public OrderStatus Status { get; set; }
public List<string> Payload { get; set; }
}
public enum OrderStatus
{
Processing,
Completed
}
}
Copyright © 2009 by Microsoft Corporation. All rights reserved.