Peter McGrattan’s Weblog

Silverlight, WCF, ASP.NET, AJAX, Graphics, RIA

Posts Tagged ‘System.ServiceModel.PollingDuplex’

Configuring Silverlight 2 Polling Duplex Endpoints in .config file only

Posted by petermcg on October 23, 2008

(Sample Application Code) | (PollingDuplexConfig.cs only)

I got an interesting question recently asking if it was possible to specify a Silverlight 2 Polling Duplex endpoint entirely within an application’s configuration file instead of defining the endpoint in code as show below in the excerpt taken from my Polling Duplex Stock List demo:

using System;
using System.ServiceModel;
using System.ServiceModel.Description;

namespace StockServer
{
    public class StockServiceHost : ServiceHost
    {
        public StockServiceHost(object singletonInstance, params Uri[] baseAddresses)
            : base(singletonInstance, baseAddresses)
        {
        }

        public StockServiceHost(Type serviceType, params Uri[] baseAddresses)
            : base(serviceType, baseAddresses)
        {
        }

        protected override void InitializeRuntime()
        {
            // Policy Requirements Endpoint
            this.AddServiceEndpoint(
                typeof(IPolicyProvider),
                new WebHttpBinding(),
                new Uri("http://localhost:10201/")).Behaviors.Add(new WebHttpBehavior());
            
            // Polling Duplex Endpoint
            this.AddServiceEndpoint(
                typeof(IStockService),
                new PollingDuplexHttpBinding(),
                new Uri("http://localhost:10201/StockService"));

            base.InitializeRuntime();
        }
    }
}

Surprisingly this is not supported by default in the release version of Silverlight 2 due to the absence of the required configuration-related classes needed to expose the Polling Duplex binding and binding element to the WCF configuration system.  However, thanks to the great extensibility model of WCF it is possible to enable this scenario and bridge the gap by registering a custom binding extension.

The aim is to render the StockServiceHost class (defined in code above) redundant and instead replace it’s functionality entirely in a configuration file, in this case App.config.  As you can see in the InitializeRuntime method the StockServiceHost class actually registers two endpoints: in addition to the PollingDuplexHttpBinding endpoint there is the WebHttpBinding endpoint to account for the Silverlight 2 cross-domain policy requirements.  It would defeat the aim to succeed in defining one of these endpoints in the configuration file without the other, so the measure of success is to be able to define both in App.config.  How this aim can be achieved is shown below:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.serviceModel>
    <diagnostics performanceCounters="All" />

    <behaviors>
      <endpointBehaviors>
        <behavior name="webHttpBehavior">
          <webHttp />
        </behavior>
      </endpointBehaviors>
    </behaviors>

    <bindings>
      <pollingDuplexHttpBinding>
        <binding name="pollingDuplexHttpBindingConfiguration"
                 inactivityTimeout="00:09:59"
                 serverPollTimeout="00:14:59" />
      </pollingDuplexHttpBinding>
    </bindings>

    <extensions>
      <bindingExtensions>
        <add name="pollingDuplexHttpBinding"
             type="StockServer.PollingDuplexHttpBindingCollectionElement, StockServer" />
      </bindingExtensions>
    </extensions>

    <services>
      <service name="StockServer.StockService">

        <!-- Policy Requirements Endpoint -->
        <endpoint address="http://localhost:10201/"
                  binding="webHttpBinding"
                  behaviorConfiguration="webHttpBehavior"
                  contract="StockServer.IPolicyProvider" />

        <!-- Polling Duplex Endpoint -->
        <endpoint address="http://localhost:10201/StockService"
                  binding="pollingDuplexHttpBinding"
                  bindingConfiguration="pollingDuplexHttpBindingConfiguration"
                  contract="StockServer.IStockService" />

      </service>
    </services>
  </system.serviceModel>
</configuration>

Notice under the <bindingExtensions> element that the pollingDuplexHttpBinding item relies on the following two configuration-related classes (demonstrated in use as part of the sample application or available separately for download above) in the PollingDuplexConfig.cs file:

using System;
using System.Configuration;
using System.Globalization;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Configuration;

namespace StockServer
{
    public class PollingDuplexHttpBindingCollectionElement : 
        StandardBindingCollectionElement<PollingDuplexHttpBinding, PollingDuplexHttpBindingElement>
    {
    }

    public class PollingDuplexHttpBindingElement : StandardBindingElement
    {
        private const string InactivityTimeoutPropertyName = "inactivityTimeout";
        private const string DefaultInactivityTimeout = "00:10:00";

        private const string ServerPollTimeoutPropertyName = "serverPollTimeout";
        private const string DefaultServerPollTimeout = "00:15:00";

        public PollingDuplexHttpBindingElement(string configurationName) :
            base(configurationName)
        {
        }

        public PollingDuplexHttpBindingElement() :
            this(null)
        {
        }

        protected override Type BindingElementType
        {
            get
            {
                return typeof(PollingDuplexHttpBinding);
            }
        }

        [ConfigurationProperty(InactivityTimeoutPropertyName, DefaultValue = DefaultInactivityTimeout)]
        public TimeSpan InactivityTimeout
        {
            get
            {
                return ((TimeSpan)(base[InactivityTimeoutPropertyName]));
            }
            set
            {
                base[InactivityTimeoutPropertyName] = value;
            }
        }

        [ConfigurationProperty(ServerPollTimeoutPropertyName, DefaultValue = DefaultServerPollTimeout)]
        public TimeSpan ServerPollTimeout
        {
            get
            {
                return ((TimeSpan)(base[ServerPollTimeoutPropertyName]));
            }
            set
            {
                base[ServerPollTimeoutPropertyName] = value;
            }
        }

        protected override ConfigurationPropertyCollection Properties
        {
            get
            {
                ConfigurationPropertyCollection properties = base.Properties;
                properties.Add(new ConfigurationProperty(InactivityTimeoutPropertyName, 
                                                         typeof(TimeSpan), DefaultInactivityTimeout));
                properties.Add(new ConfigurationProperty(ServerPollTimeoutPropertyName, 
                                                         typeof(TimeSpan), DefaultServerPollTimeout));
                return properties;
            }
        }

        protected override void InitializeFrom(Binding binding)
        {
            base.InitializeFrom(binding);

            PollingDuplexHttpBinding pollingDuplexHttpBinding = ((PollingDuplexHttpBinding)(binding));
            this.InactivityTimeout = pollingDuplexHttpBinding.InactivityTimeout;
            this.ServerPollTimeout = pollingDuplexHttpBinding.ServerPollTimeout;
        }

        protected override void OnApplyConfiguration(Binding binding)
        {
            if ((binding == null))
            {
                throw new ArgumentNullException("binding");
            }
            if ((binding.GetType() != typeof(PollingDuplexHttpBinding)))
            {
                throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, 
                                                          "Invalid type for binding. Expected type: {0}. Type passed in: {1}.", 
                                                          typeof(PollingDuplexHttpBinding).AssemblyQualifiedName, 
                                                          binding.GetType().AssemblyQualifiedName));
            }

            PollingDuplexHttpBinding pollingDuplexHttpBinding = ((PollingDuplexHttpBinding)(binding));
            pollingDuplexHttpBinding.InactivityTimeout = this.InactivityTimeout;
            pollingDuplexHttpBinding.ServerPollTimeout = this.ServerPollTimeout;
        }
    }
}

To configure Polling Duplex in Silverlight 2 solely via the .config file the PollingDuplexHttpBindingCollectionElement class including the relevant namespace and assembly, need to be referenced in the type attribute of the pollingDuplexHttpBinding item under the <bindingExtensions> element as shown in the configuration markup above.

As this is a standard way to create and register a custom binding extension, once the configuration classes and markup are in place as shown and the project successfully builds, you can edit the binding in the same way as the built-in WCF bindings by opening the produced .config file (StockServer.exe.config in this case) in SvcConfigEditor.exe while the service is running:

Binding Extension Binding Properties

For more detail on configuring WCF bindings in general I recommend reading WCF Bindings In Depth by Aaron Skonnard

Posted in PollingDuplex, Silverlight, WCF | Tagged: , , , | 4 Comments »