How To: Dynamic Update
This topic outlines the basic steps required to create and dynamically update the routing configuration. In this example, the initial routing configuration is obtained from the configuration file and routes all messages to the regularCalc calculator service; however, it is subsequently updated programmatically in order to change the destination endpoint the roundingCalc service.
Note
In many implementations, configuration will be fully dynamic and will not rely on a default configuration; however, there are some scenarios, such as the one in this topic, where it is desirable to have a default configuration state when the service starts.
Note
Dynamic updates occur only in memory, and do not result in the modification of configuration files.
Both regularCalc and roundingCalc support the same operations of add, subtract, multiply and divide; however, roundingCalc rounds all calculations to the nearest integer value before returning. A configuration file is used to configure the service to route all messages to the regularCalc service. After the Routing Service has been started, ApplyConfiguration is used to reconfigure the service to route messages to the roundingCalc service.
Implement Initial Configuration
Create the basic Routing Service Configuration by specifying the service endpoints exposed by the service. The following example defines a single service endpoint, which will be used to receive messages. It also defines a client endpoint that will be used to send messages to the regularCalc.
<services> <service behaviorConfiguration="routingConfiguration" name="System.ServiceModel.Routing.RoutingService"> <host> <baseAddresses> <add baseAddress="https://localhost/routingservice/router" /> </baseAddresses> </host> <!--Set up the inbound endpoint for the Routing Service--> <endpoint address="calculator" binding="wsHttpBinding" name="routerEndpoint" contract="System.ServiceModel.Routing.IRequestReplyRouter" /> </service> </services> <client> <!--set up the destination endpoint--> <endpoint name="regularCalcEndpoint" address="net.tcp://localhost:9090/servicemodelsamples/service/" binding="netTcpBinding" contract="*" /> </client>
Define the filter used to route messages to the destination endpoints. For this example, the MatchAll filter is used to route all messages to the regularCalcEndpoint defined previously. The following example defines the filter and filter table.
<filters> <!--define the message filter--> <filter name="MatchAllFilter" filterType="MatchAll"/> </filters> <filterTables> <filterTable name="filterTable1"> <!--add the filter to the message filter table--> <add filterName="MatchAllFilter" endpointName="regularCalcEndpoint"/> </filterTable> </filterTables>
To evaluate incoming messages against the filters contained in the filter table, you must associate the filter table with the service endpoints by using the routing behavior. The following example demonstrates associating “filterTable1” with the service endpoint.
<behaviors> <!--default routing service behavior definition--> <serviceBehaviors> <behavior name="routingConfiguration"> <routing filterTableName="filterTable1" /> </behavior> </serviceBehaviors> </behaviors>
Implement Dynamic Configuration
Dynamic configuration of the Routing Service can only be performed in code by creating a new RoutingConfiguration and using ApplyConfiguration to replace the current configuration. For this example, the Routing Service is self-hosted within a console application. After the application has started, you can modify the routing configuration by entering ‘regular’ or ‘rounding’ at the console window to configure the destination endpoint that messages are routed to; regularCalc when ‘regular’ is entered, otherwise roundingCalc when ‘rounding’ is entered.
The following using statements must be added in order to support the Routing Service.
using System; using System.Collections.Generic; using System.ServiceModel; using System.ServiceModel.Channels; using System.ServiceModel.Description; using System.ServiceModel.Dispatcher; using System.ServiceModel.Routing;
The following code is used to self-host the Routing Service as a console application. This initializes the Routing Service using the configuration described in the previous step, which is contained within the application configuration file. The while loop contains the code used to change the routing configuration.
// Host the service within this EXE console application. public static void Main() { // Create a ServiceHost for the CalculatorService type. using (ServiceHost serviceHost = new ServiceHost(typeof(RoutingService))) { // Open the ServiceHost to create listeners // and start listening for messages. Console.WriteLine("The Routing Service configured, opening...."); serviceHost.Open(); Console.WriteLine("The Routing Service is now running."); Console.WriteLine("Type 'quit' to terminate router."); Console.WriteLine("<ENTER> to change routing configuration."); while (Console.ReadLine() != "quit") { .... } } }
To dynamically update the routing configuration, a new routing configuration must be created. This must contain all endpoints, filters and filter tables that are required for the new routing configuration, as it will completely replace the existing routing configuration. In order to use the new routing configuration, you must invoke ApplyConfiguration and pass the new configuration.
Add the following code to the while loop defined previously to allow the service to be reconfigured based on user input.
Console.WriteLine("Enter 'regular' or 'rounding' to set the destination endpoint:"); string destEndpoint = Console.ReadLine(); // Create a new RoutingConfiguration RoutingConfiguration rc = new RoutingConfiguration(); // Determine the endpoint to configure for switch (destEndpoint) { case "regular": // Define the destination endpoint ServiceEndpoint regularCalc = new ServiceEndpoint( ContractDescription.GetContract(typeof(IRequestReplyRouter)), new NetTcpBinding(), new EndpointAddress("net.tcp://localhost:9090/servicemodelsamples/service/")); // Create a MatchAll filter and add to the filter table rc.FilterTable.Add(new MatchAllMessageFilter(), new List<ServiceEndpoint> { regularCalc }); // Use ApplyConfiguration to update the Routing Service serviceHost.Extensions.Find<RoutingExtension>().ApplyConfiguration(rc); Console.WriteLine("Applied new routing configuration."); break; case "rounding": // Define the destination endpoint ServiceEndpoint roundingCalc = new ServiceEndpoint( ContractDescription.GetContract(typeof(IRequestReplyRouter)), new NetTcpBinding(), new EndpointAddress("net.tcp://localhost:8080/servicemodelsamples/service/")); // Create a MatchAll filter and add to the filter table rc.FilterTable.Add(new MatchAllMessageFilter(), new List<ServiceEndpoint> { roundingCalc }); // Use ApplyConfiguration to update the Routing Service serviceHost.Extensions.Find<RoutingExtension>().ApplyConfiguration(rc); Console.WriteLine("Applied new routing configuration."); break; default: Console.WriteLine("Incorrect value entered, no change."); break; }
Note
Since the method for providing a new RoutingConfiguration is contained in the RoutingExtension service extension, new RoutingConfiguration objects can be provided anywhere in the WCF extensibility model that has or can obtain a reference to the ServiceHost or ServiceExtensions (such as in another ServiceExtension). For an example of dynamically updating the RoutingConfiguration in this manner, see Dynamic Reconfiguration.
Example
Following is a complete listing of the console application used in this example.
//-----------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All Rights Reserved.
//-----------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;
using System.ServiceModel.Routing;
namespace Microsoft.Samples.AdvancedFilters
{
public class Router
{
// Host the service within this EXE console application.
public static void Main()
{
// Create a ServiceHost for the CalculatorService type.
using (ServiceHost serviceHost =
new ServiceHost(typeof(RoutingService)))
{
// Open the ServiceHost to create listeners
// and start listening for messages.
Console.WriteLine("The Routing Service configured, opening....");
serviceHost.Open();
Console.WriteLine("The Routing Service is now running.");
Console.WriteLine("Type 'quit' to terminate router.");
Console.WriteLine("<ENTER> to change routing configuration.");
while (Console.ReadLine() != "quit")
{
Console.WriteLine("Enter 'regular' or 'rounding' to set the destination endpoint:");
string destEndpoint = Console.ReadLine();
// Create a new RoutingConfiguration
RoutingConfiguration rc = new RoutingConfiguration();
// Determine the endpoint to configure for
switch (destEndpoint)
{
case "regular":
// Define the destination endpoint
ServiceEndpoint regularCalc = new ServiceEndpoint(
ContractDescription.GetContract(typeof(IRequestReplyRouter)),
new NetTcpBinding(),
new EndpointAddress("net.tcp://localhost:9090/servicemodelsamples/service/"));
// Create a MatchAll filter and add to the filter table
rc.FilterTable.Add(new MatchAllMessageFilter(), new List<ServiceEndpoint> { regularCalc });
// Use ApplyConfiguration to update the Routing Service
serviceHost.Extensions.Find<RoutingExtension>().ApplyConfiguration(rc);
Console.WriteLine("Applied new routing configuration.");
break;
case "rounding":
// Define the destination endpoint
ServiceEndpoint roundingCalc = new ServiceEndpoint(
ContractDescription.GetContract(typeof(IRequestReplyRouter)),
new NetTcpBinding(),
new EndpointAddress("net.tcp://localhost:8080/servicemodelsamples/service/"));
// Create a MatchAll filter and add to the filter table
rc.FilterTable.Add(new MatchAllMessageFilter(), new List<ServiceEndpoint> { roundingCalc });
// Use ApplyConfiguration to update the Routing Service
serviceHost.Extensions.Find<RoutingExtension>().ApplyConfiguration(rc);
Console.WriteLine("Applied new routing configuration.");
break;
default:
Console.WriteLine("Incorrect value entered, no change.");
break;
}
}
}
}
}
}
Following is a complete listing of the configuration file used in this example.
<?xml version="1.0" encoding="utf-8" ?>
<!-- Copyright (c) Microsoft Corporation. All rights reserved -->
<configuration>
<system.serviceModel>
<services>
<service behaviorConfiguration="routingConfiguration"
name="System.ServiceModel.Routing.RoutingService">
<host>
<baseAddresses>
<add baseAddress="https://localhost/routingservice/router" />
</baseAddresses>
</host>
<!--Set up the inbound endpoint for the Routing Service-->
<endpoint address="calculator"
binding="wsHttpBinding"
name="routerEndpoint"
contract="System.ServiceModel.Routing.IRequestReplyRouter" />
</service>
</services>
<behaviors>
<!--default routing service behavior definition-->
<serviceBehaviors>
<behavior name="routingConfiguration">
<routing filterTableName="filterTable1" />
</behavior>
</serviceBehaviors>
</behaviors>
<client>
<!--set up the destination endpoint-->
<endpoint name="regularCalcEndpoint"
address="net.tcp://localhost:9090/servicemodelsamples/service/"
binding="netTcpBinding"
contract="*" />
</client>
<routing>
<filters>
<!--define the message filter-->
<filter name="MatchAllFilter" filterType="MatchAll"/>
</filters>
<filterTables>
<filterTable name="filterTable1">
<!--add the filter to the message filter table-->
<add filterName="MatchAllFilter" endpointName="regularCalcEndpoint"/>
</filterTable>
</filterTables>
</routing>
</system.serviceModel>
</configuration>