What's New in Windows Communication Foundation (WCF) 4.0 Part- II - Developing Routing Service
As a .NET 3.x service provider, whenever I am with my client, a frequently asked question is how can the client application make a request to one or more WCF services through a single service interface? My clients have been expecting that a single WCF service interface should automatically route messages to multiple WCF services. And I have been giving them various solutions, however they were clumsy to implement.
As part of Microsoft .NET 4.0, with WCF 4.0, a message routing feature is provided. This helps to define a routing service. This service contains endpoint information of the WCF service(s) to which a message can be routed. Along with endpoint information, routing service also defines filters. Filter is the most important part of the routing service. Routing determines how message can be forwarded when request from the client is received. Here are the following filter types available:
· Action
· MatchAll
· Address
· AddressPrefix
· And
· Custom
· Endpoint
· XPath.
In the following example we will see how WCF Routing can be implemented. The following architecture is used in the application I will demonstrate in this article:
Task 1: Creating WCF Service.
Step 1: Open VS 2010 and create a Blank solution. Name this as ‘WCF_Routing_Service’. In this project add a new WCF ‘WCF Service Application’ project as below:
Step 2: Rename ‘IService1.cs’ to ‘IQtrwiseSales.cs’ and name ‘Service1.csv’ to ‘CQtrwiseSales.svc’.
Step 3: Right click on ‘CQtrwiseSales.svc’ and select ‘View Markup’ and write the following:
<%@ ServiceHost Language="C#" Debug="true" Service="WCF_ServiceQtrwsieSales.CQtrwiseSales" CodeBehind="CQtrwiseSales.svc.cs" %>
Step 4: Write the following code in ‘IQtrwiseSales.cs’:
C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Web;
using System.Text;
namespace WCF_ServiceQtrwsieSales
{
[ServiceContract]
public interface IQtrwiseSales
{
[OperationContract]
List<Sales> GetSalesDetsils();
}
[DataContract]
public class Sales
{
[DataMember]
public int CompanyId { get; set; }
[DataMember]
public string CompanyName { get; set; }
[DataMember]
public decimal Q1 { get; set; }
[DataMember]
public decimal Q2 { get; set; }
[DataMember]
public decimal Q3 { get; set; }
[DataMember]
public decimal Q4 { get; set; }
}
}
VB.NET
Imports System
Imports System.Collections.Generic
Imports System.Linq
Imports System.Runtime.Serialization
Imports System.ServiceModel
Imports System.ServiceModel.Web
Imports System.Text
Namespace WCF_ServiceQtrwsieSales
<ServiceContract> _
Public Interface IQtrwiseSales
<OperationContract> _
Function GetSalesDetsils() As List(Of Sales)
End Interface
<DataContract> _
Public Class Sales
Private privateCompanyId As Integer
<DataMember> _
Public Property CompanyId() As Integer
Get
Return privateCompanyId
End Get
Set(ByVal value As Integer)
privateCompanyId = value
End Set
End Property
Private privateCompanyName As String
<DataMember> _
Public Property CompanyName() As String
Get
Return privateCompanyName
End Get
Set(ByVal value As String)
privateCompanyName = value
End Set
End Property
Private privateQ1 As Decimal
<DataMember> _
Public Property Q1() As Decimal
Get
Return privateQ1
End Get
Set(ByVal value As Decimal)
privateQ1 = value
End Set
End Property
Private privateQ2 As Decimal
<DataMember> _
Public Property Q2() As Decimal
Get
Return privateQ2
End Get
Set(ByVal value As Decimal)
privateQ2 = value
End Set
End Property
Private privateQ3 As Decimal
<DataMember> _
Public Property Q3() As Decimal
Get
Return privateQ3
End Get
Set(ByVal value As Decimal)
privateQ3 = value
End Set
End Property
Private privateQ4 As Decimal
<DataMember> _
Public Property Q4() As Decimal
Get
Return privateQ4
End Get
Set(ByVal value As Decimal)
privateQ4 = value
End Set
End Property
End Class
End Namespace
The above service contract, defines an operation named ‘GetSalesDetasils’ which returns ‘List<Sales>’ object. DataContract maps with the ‘Sales’ data table in the Sql Server 2005.
Step 5: In ‘CQtrwiseSales.svc.cs’ write the following code:
C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Web;
using System.Text;
using System.Data.SqlClient;
using System.Data;
namespace WCF_ServiceQtrwsieSales
{
public class CQtrwiseSales : IQtrwiseSales
{
SqlConnection Conn;
SqlCommand Cmd;
SqlDataReader Reader;
#region IQtrwiseSales Members
public List<Sales> GetSalesDetsils()
{
List<Sales> lstSales = new List<Sales>();
Conn = new SqlConnection("Data Source=.;Initial Catalog=Company;Integrated Security=SSPI");
Conn.Open();
Cmd = new SqlCommand("Select * from Sales", Conn);
Reader = Cmd.ExecuteReader();
while (Reader.Read())
{
lstSales.Add
(
new Sales()
{
CompanyId = Convert.ToInt32(Reader["CompanyId"]),
CompanyName=Reader["CompanyName"].ToString(),
Q1=Convert.ToInt32(Reader["Q1"]) ,
Q2=Convert.ToInt32(Reader["Q2"]),
Q3=Convert.ToInt32(Reader["Q3"]),
Q4=Convert.ToInt32(Reader["Q4"])
}
);
}
Conn.Close();
return lstSales;
}
#endregion
}
}
VB.NET
Imports System
Imports System.Collections.Generic
Imports System.Linq
Imports System.Runtime.Serialization
Imports System.ServiceModel
Imports System.ServiceModel.Web
Imports System.Text
Imports System.Data.SqlClient
Imports System.Data
Namespace WCF_ServiceQtrwsieSales
Public Class CQtrwiseSales
Implements IQtrwiseSales
Private Conn As SqlConnection
Private Cmd As SqlCommand
Private Reader As SqlDataReader
#Region "IQtrwiseSales Members"
Public Function GetSalesDetsils() As List(Of Sales)
Dim lstSales As New List(Of Sales)()
Conn = New SqlConnection("Data Source=.;Initial Catalog=Company;Integrated Security=SSPI")
Conn.Open()
Cmd = New SqlCommand("Select * from Sales", Conn)
Reader = Cmd.ExecuteReader()
Do While Reader.Read()
lstSales.Add (New Sales() With {.CompanyId = Convert.ToInt32(Reader("CompanyId")), .CompanyName=Reader("CompanyName").ToString(), .Q1=Convert.ToInt32(Reader("Q1")), .Q2=Convert.ToInt32(Reader("Q2")), .Q3=Convert.ToInt32(Reader("Q3")), .Q4=Convert.ToInt32(Reader("Q4"))})
Loop
Conn.Close()
Return lstSales
End Function
#End Region
End Class
End Namespace
The above code makes call to the ‘Company’ database and retrieves data from ‘Sales’ table.
Step 6: In the Web.Config file make the following changes:
<system.serviceModel>
<services>
<service behaviorConfiguration="ServBehave"
name="WCF_ServiceQtrwsieSales.CQtrwiseSales">
<endpoint address="" binding="wsHttpBinding"
contract="WCF_ServiceQtrwsieSales.IQtrwiseSales">
<identity>
<dns value="localhost" />
</identity>
</endpoint>
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="ServBehave">
<serviceMetadata httpGetEnabled="true"/>
<serviceDebug includeExceptionDetailInFaults="false"/>
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
Step 7: Publish the WCF Service Application to IIS by creating a virtual directory.
Task 2: Creating Routing service.
Step 1: In the same solution add a new console application, name this as ‘WCF_TempRoutingService’.
Step 2: Add the reference to following namespace in this project:
System.ServiceModel.
System.ServiveModel.Routing.
Step 3: In this project add the App.Config file and write the following code:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<services> <service name="System.ServiceModel.Routing.RoutingService" behaviorConfiguration="MyRoutedServBehave">
<host>
<baseAddresses>
<add baseAddress="http://localhost:5643/MyServ"/>
</baseAddresses>
</host>
<endpoint
address=""
binding="wsHttpBinding" name="MyRoutingEndpoint" contract="System.ServiceModel.Routing.IRequestReplyRouter"/>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="MyRoutedServBehave">
<serviceMetadata httpGetEnabled="True"/>
<serviceDebug includeExceptionDetailInFaults="True"/>
<routing routingTableName="ServiceRouterTable"/>
<!--The Router Table Contains Entries for services-->
</behavior>
</serviceBehaviors>
</behaviors>
<!--Define Services Here-->
<client>
<endpoint
name="WCF_QtrwiseSalesService" binding="wsHttpBinding"
address="http://localhost/WCF40_QtrwiseSalesVD/CQtrwiseSales.svc"
contract="*">
</endpoint>
</client>
<!--Routing Defination-->
<routing>
<!--Filter For Detecting Messages Headers to redirect-->
<filters>
<filter name="MyFilter_1" filterType="MatchAll"/>
</filters>
<!--Define Routing Table, This will Map the service with Filter-->
<routingTables>
<table name="ServiceRouterTable">
<entries>
<add filterName="MyFilter_1" endpointName="WCF_QtrwiseSalesService"/>
</entries>
</table>
</routingTables>
</routing>
</system.serviceModel>
</configuration>
The above configuration contains the Routing service ‘System.ServiceModel.Routing.RoutingService’ hosted on the address ‘http://localhost:5643/MyServ’ and uses ‘wsHttpBinding’. This uses contract ‘System.ServiceModel.Routing.IRequestReplyRouter’. The configuration file contains endpoint information of the ‘WCF_ServiceQtrwsieSales’. The contract value ‘*’ represents all contracts that will be referred from the service. Service behavior defines routing table name.
Routing section of the configuration defines filters. This is the heart of the routing mechanism. Based upon the filter, the routing service will redirect the request received from the client to the WCF service. Routing table maps filter with the WCF service endpoint.
Step 4: In the ‘Program.cs’ write the following code:
C#
static void Main(string[] args)
{
ServiceHost Host = new ServiceHost(typeof(RoutingService));
try
{
Host.Open();
Console.WriteLine("Routing Service is Started...............");
Console.ReadLine();
Host.Close();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
Console.ReadLine();
}
VB.NET
Shared Sub Main(ByVal args() As String)
Dim Host As New ServiceHost(GetType(RoutingService))
Try
Host.Open()
Console.WriteLine("Routing Service is Started...............")
Console.ReadLine()
Host.Close()
Catch ex As Exception
Console.WriteLine(ex.Message)
End Try
Console.ReadLine()
End Sub
Task 3: Creating Client Application
Step 1: In the solution, add a new WPF project and name it as ‘WPF_Client_RoutingService’. In this project add the service reference of the ‘WCF_ServiceQtrwsieSales’. This will add App.config file in the client application.
Step 2: Since the client should only know the interface information of the service, but will communicate to it using routing service, so delete existing contents of the ‘App.Config’ as below:
<configuration>
<system.serviceModel>
<client>
<endpoint name="clientEdpQtrwiseSales"
address="http://localhost:5643/MyServ"
binding="wsHttpBinding"
contract="MyRefQtrSales.IQtrwiseSales"></endpoint>
</client>
</system.serviceModel>
</configuration>
Step 3: Add the DataGrid on WPF window as below:
<DataGrid x:Name="dgQtrwiseSales" Grid.Column="0" AutoGenerateColumns="True"></DataGrid>
Step 4: In the Window.xaml.cs write the following code:
C#
MyRefQtrSales.QtrwiseSalesClient ProxyQtrwsieSales;
MyRefQtrSales.Sales[] arrQtrSales;
void Window1_Loaded(object sender, RoutedEventArgs e)
{
ProxyQtrwsieSales = new WPF_Client_RoutingService.MyRefQtrSales.QtrwiseSalesClient("clientEdpQtrwiseSales");
arrQtrSales = ProxyQtrwsieSales.GetSalesDetsils();
gQtrwiseSales.ItemsSource = arrQtrSales;
}
VB.NET
Private ProxyQtrwsieSales As MyRefQtrSales.QtrwiseSalesClient
Private arrQtrSales() As MyRefQtrSales.Sales
Private Sub Window1_Loaded(ByVal sender As Object, ByVal e As RoutedEventArgs)
ProxyQtrwsieSales = New WPF_Client_RoutingService.MyRefQtrSales.QtrwiseSalesClient("clientEdpQtrwiseSales")
arrQtrSales = ProxyQtrwsieSales.GetSalesDetsils()
gQtrwiseSales.ItemsSource = arrQtrSales
End Sub
Step 5: Run the Routing Service and the client application. The following output will be displayed:
Conclusion:
WCF 4.0 Routing message is in my opintion the best facility provided by .NET 4.0. This reduces the complication and the time for developing routings using code.
The entire source code of this article can be downloaded over here
This article has been editorially reviewed by Suprotim Agarwal.
C# and .NET have been around for a very long time, but their constant growth means there’s always more to learn.
We at DotNetCurry are very excited to announce The Absolutely Awesome Book on C# and .NET. This is a 500 pages concise technical eBook available in PDF, ePub (iPad), and Mobi (Kindle).
Organized around concepts, this Book aims to provide a concise, yet solid foundation in C# and .NET, covering C# 6.0, C# 7.0 and .NET Core, with chapters on the latest .NET Core 3.0, .NET Standard and C# 8.0 (final release) too. Use these concepts to deepen your existing knowledge of C# and .NET, to have a solid grasp of the latest in C# and .NET OR to crack your next .NET Interview.
Click here to Explore the Table of Contents or Download Sample Chapters!
Was this article worth reading? Share it with fellow developers too. Thanks!
Mahesh Sabnis is a DotNetCurry author and a Microsoft MVP having over two decades of experience in IT education and development. He is a Microsoft Certified Trainer (MCT) since 2005 and has conducted various Corporate Training programs for .NET Technologies (all versions), and Front-end technologies like Angular and React. Follow him on twitter @
maheshdotnet or connect with him on
LinkedIn