How to: Build a Duplex Service for a Silverlight Client

Microsoft Silverlight will reach end of support after October 2021. Learn more.

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. Duplex services have numerous applications including, for example, a chat server for instant messaging or a monitoring service that sends notifications to the client. This sample provides a service that allows a client to order a specified number of product items by name. It simulates processing the order and then calls back to the client with status on the order.

In duplex messaging, both service and client expose service contracts, which specify the operations that each of them exposes to the other. In this example, we use the following pair of service contracts. The client invokes the Order method on the service and the service invokes the Receive method on the client.

[ServiceContract(Namespace="Silverlight", CallbackContract = typeof(IDuplexClient))]
public interface IDuplexService
{
    [OperationContract]
    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 client method that is part of the callback contract. This property indicates that these operations do not return reply messages.

To create a WCF duplex service to work with Silverlight

  1. Open Visual Studio 2010.

  2. From the File menu, select New, select Project, and specify WCF 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.

  3. 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.

    //Interface contracts in the IService1.cs file.
    
    namespace DuplexService
    {
        [ServiceContract(Namespace="Silverlight", CallbackContract = typeof(IDuplexClient))]
        public interface IDuplexService
        {
            [OperationContract]
            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 that they expose to define the message patterns used.

  4. Open the Service1.svc.cs file and add the following using statement to the top of the file.

    using System.Threading;
    

    Functionality from this namespace will be used to simulate the processing delay behavior of an actual service in the sample duplex service.

  5. Edit the contents of the file Service1.svc.cs to define an OrderService class that implements the IDuplexService contract. First, remove all classes inside the DuplexService namespace declaration. Then define the OrderService class as shown in the following code.

    public class OrderService : IDuplexService
       {
          IDuplexClient client;
          string orderName;
          int orderQuantity;
    
          public void Order(string name, int quantity)
          {
            // Grab the 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 Order operation simulates the execution of a process of ordering an item from an online store. The order is received, and typically a series of subsequent actions takes place outside the scope of the service (for example, checking on the availability of an item or verifying a user’s credit card information). After these actions are complete, the service needs to call the client back (that is, push information to the client) to notify it that the order has been completed successfully. To simulate this scenario, we 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 is used to contact the Silverlight client after it is implemented and running.

  6. Inside the DuplexService namespace, add an Order class and an auxiliary OrderStatus enum 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
    }
    
  7. Inside the OrderService class, 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 Payload property.

  8. To specify the DuplexService.OrderService as the service in the Service1.svc file, right-click the Service1.svc file in Solution Explorer, select View Markup, and paste the following service directive over the default directive.

    // Directive contained in the Service1.svc file.
    
    <%@ServiceHost language="c#" Debug="true" Service="DuplexService.OrderService" CodeBehind="Service1.svc.cs "%>
    

The service contract for the service has now been developed, and now we have to choose between the duplex transports that Silverlight 4 understands.

To use PollingDuplexHttpBinding

  1. 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 the %ProgramFiles%\Microsoft SDKs\Silverlight\v4.0\Libraries\Server\ directory), select it, and click OK.

    Cc645027.Warning(en-us,VS.95).gif Caution:
    Note that there are two assemblies named System.ServiceModel.PollingDuplex.dll that ship with the Silverlight version 4 SDK. One of them (located in the %ProgramFiles%\Microsoft SDKs\Silverlight\v4.0\Libraries\Server\ directory), the reference to which you just added, is used by WCF duplex services. The other one (located in the %ProgramFiles%\Microsoft SDKs\Silverlight\v4.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.
  2. Right-click the Web.config file in Solution Explorer and select Open to edit the configuration. First, register the pollingDuplexHttpBinding from the Silverlight 4 SDK by adding the following inside the <system.serviceModel> section.

    <!-- Register the binding extension from the SDK. -->
       <extensions>
          <bindingExtensions>
            <add name=
                "pollingDuplexHttpBinding"
                type="System.ServiceModel.Configuration.PollingDuplexHttpBindingCollectionElement,System.ServiceModel.PollingDuplex, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
          </bindingExtensions>
       </extensions>
    
  3. Add the pollingDuplexHttpBinding element in the <bindings> section to specify the use of this binding.

       <!-- Create the polling duplex binding. -->
       <pollingDuplexHttpBinding>
          <binding name="multipleMessagesPerPollPollingDuplexHttpBinding" 
                   duplexMode="MultipleMessagesPerPoll" 
                   maxOutputDelay="00:00:07"/>
       </pollingDuplexHttpBinding>
    
  4. Then specify the service endpoints in the <services> section.

       <services>
          <service name="DuplexService.OrderService"  
             behaviorConfiguration="DuplexService.OrderServiceBehavior">
    
             <!-- Service Endpoints -->
             <endpoint
                address=""
                binding="pollingDuplexHttpBinding"
                bindingConfiguration="multipleMessagesPerPollPollingDuplexHttpBinding"   
                contract="DuplexService.IDuplexService">
            </endpoint>
            <endpoint
                address="mex"
                binding="mexHttpBinding"
                contract="IMetadataExchange"/>
          </service>
       </services>
    
  5. The <system.serviceModel> section of the Web.config file should now contain the following configuration elements.

        <!-- Register the binding extension from the SDK. -->
        <extensions>
          <bindingExtensions>
            <add name="pollingDuplexHttpBinding"
                 type="System.ServiceModel.Configuration.PollingDuplexHttpBindingCollectionElement,
                       System.ServiceModel.PollingDuplex, 
                       Version=4.0.0.0, 
                       Culture=neutral, 
                       PublicKeyToken=31bf3856ad364e35" />
          </bindingExtensions>
        </extensions>
    
        <!-- Create the polling duplex binding. -->
        <bindings>
          <pollingDuplexHttpBinding>
            <binding name="multipleMessagesPerPollPollingDuplexHttpBinding" duplexMode="MultipleMessagesPerPoll" maxOutputDelay="00:00:07"/>
          </pollingDuplexHttpBinding>
        </bindings>
    
        <services>
          <service name="DuplexService.OrderService">
    
            <!-- Service Endpoints -->
            <endpoint
               address=""
               binding="pollingDuplexHttpBinding"
               bindingConfiguration="multipleMessagesPerPollPollingDuplexHttpBinding"
               contract="DuplexService.IDuplexService">
            </endpoint>
            <endpoint
                address="mex"
                binding="mexHttpBinding"
                contract="IMetadataExchange"/>
          </service>
        </services>
    
    
        <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="true" />
      </system.serviceModel>
              <!-- 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>
    
      </system.serviceModel>
    
  6. The duplex service is now ready to be tested in Visual Studio. 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 OrderService Service test page, which confirms that the service is available.

To use NetTcpBinding

  1. Ensure that you are running Visual Studio with Administrator privileges before continuing.

  2. Open the Web.config file and add a <netTcpBinding> element with a nested <security> element with mode=”None”. The current version of Silverlight does not support NetTcpBinding with security.

        <bindings>
          <netTcpBinding>
            <binding name="tcpBindingNoSecurity">
              <security mode="None" />
            </binding>
          </netTcpBinding>
        </bindings>
    
    Cc645027.security(en-us,VS.95).gif Security Note:
    Because Silverlight 4 does not support NetTcpBinding with security, any information you send using this binding configuration is unencrypted and can be intercepted and modified by unauthorized third parties. Do not send sensitive information using this binding configuration.
  3. Specify the service endpoints in the <services> section.

    <services>
       <service name="DuplexService.OrderService">
    
          <!-- Specify the service endpoints. -->
          <endpoint 
             address=""
             binding="netTcpBinding"
             bindingConfiguration="tcpBindingNoSecurity"
             contract="DuplexService.IDuplexService">
          </endpoint>
          <endpoint 
             address="mex"
             binding="mexHttpBinding"
             contract="IMetadataExchange"/>
          </service>
       </services>
    
  4. The service must be hosted in Internet Information Services (IIS) because the Visual Studio Web server does not support activating TCP-based services.

    1. Right-click the DuplexServices project and select Properties.

    2. Click the Web tab on the left side and select Use Local IIS Web server. (You might have to scroll down to see this option.) It will suggest a path similar to https://localhost/DuplexService in the Project Url box, which is the value you should use.

    3. Click the Create Virtual Directory button to the right of the Project Url box to create the virtual directory.

  5. After the virtual directory is created, you need to use IIS Manager to make some additional configuration changes to that virtual directory.

    1. In the IIS Default Web Site, configure the net.tcp binding type with binding information 4502:*.

    2. In the DuplexService virtual directory, add net.tcp to the list of enabled protocols.

  6. After the service is hosted in IIS, you still need to expose the Silverlight 4 HTTP sockets policy file on the computer; otherwise all Silverlight 4 clients attempting to access the service will fail.

    <?xml version="1.0" encoding ="utf-8"?>
    <access-policy>
      <cross-domain-access>
        <policy>
          <allow-from>
            <domain uri="*" />
          </allow-from>
          <grant-to>
            <socket-resource port="4502" protocol="tcp" />
          </grant-to>
        </policy>
      </cross-domain-access>
    </access-policy>
    
    Cc645027.Warning(en-us,VS.95).gif Caution:
    The TCP-based sockets policy retrieval on port 943 is not supported. There is no way to switch to TCP-based sockets policy retrieval when using the NetTcp binding.

    For more information, see Network Security Access Restrictions in Silverlight.

  7. The duplex service is now ready to be tested in Visual Studio. 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 OrderService Service test page, which confirms that the service is available.

The duplex service is now available. The next procedure, which constructs a Silverlight 4 client for it, is outlined 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 the client callback channel.
            client = OperationContext.Current.GetCallbackChannel<IDuplexClient>();
            orderName = name;
            orderQuantity = quantity;

            // Pretend the service is processing and will call the 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 the order.
            if (processed)
            {
                // Pretend the service is fulfilling the order.
                while (orderQuantity-- > 0)
                {
                    order.Payload.Add(orderName + orderQuantity);
                }

                order.Status = OrderStatus.Completed;

            }
            else
            {
                // Pretend the service is processing the payment.
                order.Status = OrderStatus.Processing;
                processed = true;
            }

            // Call the client back.
            client.Receive(order);

        }

    }

    public class Order
    {
        public OrderStatus Status { get; set; }
        public List<string> Payload { get; set; }
    }

    public enum OrderStatus
    {
        Processing,
        Completed
    }

}

See Also

Send comments about this topic to Microsoft.

Copyright © 2010 by Microsoft Corporation. All rights reserved.