WCF Self-Hosting multiple bindings

When building WCF Services there is this point when you have to make a critical decision: Which binding is the best for you service? Windows Communication Foundation comes with several different bindings, each of those created for specific situations and requirements. Here are some of the most used bindings with some of their characteristics as well:

Binding Characteristics
BasicHttpBinding Use the HTTP and HTTPS transport protocols and encodes messages as XML text.
WS2007HttpBinding Support distributed transactions and secure, reliable sessions.
WSDualHttpBinding Suitable for handling duplex communications. Duplex messaging allows a client and service to perform two-way communication without requiring any form of synchronization
WebHttpBinding Suitable for building WCF REST Web services
NetTcpBinding Uses the TCP transport protocol to transmit messages using a binary encoding. Great advantage it’s higher performace over the HTTP protocol

You can see that there are a lot of options but this post isn’t a tutorial about WCF bindings. Instead what I wanna show you is how to Self-Host the same or a even a different service over different WCF bindings. Let me explain in detail. Suppose that you have to host two different services in a console application. Why? Because the first service is supposed to be consumed over the internet, for example by customers of your company and the second one, is a service for your employees. So ideally, you would expose the first public service using a binding such as the BasicHttpBinding and the second service which is private and consumed under the local area network of your organization, would be exposed using the NetTcpBinding. To demonstrate this situation, we will build a project where the same service will be exposed under two different bindings using a single ServiceHost instance. More specifically, we will use the Chinook database which is one of my favorite for testing and you can download it for free in codeplex.com. Those who use MS SQL Server can use the sql script they will find inside project’s folder we will build (check download link at the bottom). We will create a WCF Service exposing two Operation Contracts, one to retrieve all artists and another to get all artists with their albums. Later we will host this same service in a console application, exposing it in two different bindings, BasicHttpBinding and NetTcpBinding. The client will be an ASP.NET Web Site which will consume the two different endpoints. Here is the diagram of our project.
SelfHostArchitect
Let’s start. You don’t have to type all the code by yourself, instead you can download the project at the bottom of the page. Make sure you change all the connections strings respectively. Create a solution named “ChinookWCFApplication” and add the first C# class library project named “ChinookModel”. Then add a new ADO.NET Entity Data Model item, named “ChinookEntities.edmx” and adding to it only the Artist and Album tables from the Chinook database.
wcfselfhost_01
Now let’s create the WCF service. Add a new C# class library project named “ChinookWcfService”. Add references to the ChinookModel project, to the System.ServiceModel and the System.Runtime.Serilization libraries as well. Last but not least, install the Entity Framework from the Nuget Packages... Add a new interface C# file, named IChinookService and paste the following code.

namespace ChinookWcfService
{
    [ServiceContract]
    public interface IChinookService
    {
        [OperationContract]
        List<ArtistDTO> GetArtists();

        [OperationContract]
        List<ArtistAlbums> GetArtistsAlbums();
    }

    [DataContract]
    public class ArtistDTO
    {
        [DataMember]
        public string Name { get; set; }
    }

    [DataContract]
    public class ArtistAlbums
    {
        [DataMember]
        public string Name { get; set; }
        [DataMember]
        public List<string> Albums { get; set; }
        public ArtistAlbums()
        {
            Albums = new List<string>();
        }
    }
}

Our WCF service exposes two operations and can exchange two different objects, ArstisDTO and ArtistAlbums. Add a new C# class named ChinookService to implement the interface.

namespace ChinookWcfService
{
    public class ChinookService : IChinookService
    {

        public List<ArtistDTO> GetArtists()
        {
            List<ArtistDTO> artists = new List<ArtistDTO>();

            using (var context = new ChinookEntities())
            {
                var query = context.Artists.ToList();
                foreach (var artist in query)
                {
                    artists.Add(new ArtistDTO
                    {
                        Name = artist.Name
                    });
                }
            }

            return artists;
        }

        public List<ArtistAlbums> GetArtistsAlbums()
        {
            List<ArtistAlbums> artistAlbums = new List<ArtistAlbums>();
            using (var context = new ChinookEntities())
            {
                var query = context.Artists.ToList();
                foreach (var artist in query)
                {
                    ArtistAlbums artAlbums = new ArtistAlbums();
                    artAlbums.Name = artist.Name;
                    foreach (var album in artist.Albums)
                    {
                        artAlbums.Albums.Add(album.Title);
                    }
                    artistAlbums.Add(artAlbums);
                }
            }

            return artistAlbums;
        }
    }
}

Our service is ready to be hosted. Add a new Console Application project to the solution, named “ChinookHostConsole” and add references both to the ChinookWcfService project and the System.ServiceModel library as well. Add the following code inside the Main method of your Program.cs file.

static void Main(string[] args)
        {
            //Create two different URIs to serve as the base address
            // One for http requests and another for net.tcp
            Uri httpUrl = new Uri("http://localhost:8090/ChinookHttpService");
            Uri netTcpUrl = new Uri("net.tcp://localhost:8080/ChinookHttpService");

            //Create ServiceHost to host the service in the console application
            ServiceHost host = new ServiceHost(typeof(ChinookWcfService.ChinookService), httpUrl,netTcpUrl);
            
            //Enable metadata exchange - you need this so others can create proxies
            //to consume your WCF service
            ServiceMetadataBehavior serviceMetaBehavior = new ServiceMetadataBehavior();
            serviceMetaBehavior.HttpGetEnabled = true;
            host.Description.Behaviors.Add(serviceMetaBehavior);
            //Start the Service
            host.Open();

            Console.WriteLine("Service is host at " + DateTime.Now.ToString());
            Console.WriteLine("Host is running... Press <Enter> key to stop");
            Console.ReadKey();
            host.Close();
        }

The code is quite self-explanatory I think. Notice the constructor we used for the ServiceHost object. We passed the service implementation class and the two different URIs we created before. Believe it or not, this is the only thing you have to do in order to host this service over HTTP and TCP protocols. Before testing if the WCF service is hosted correctly, copy and paste the connection string from the Model project to the console application as well. Set the console application as the Start up project and run it.
wcfselfhost_02
The last step to test is consuming the two different WCF service endpoints. Add a new ASP.NET Empty web site named “ChinookWebClient”. We need to create a proxy class in order to consume the service. We have two options, let Visual Studio create it for us or use the Svcutil.exe. We will make it through the Visual Studio this time so make sure you start the console application so it is running when we try to add a service reference. While the service is hosted, right click the Client project and select Add Service Reference... Paste the HTTP Uri and press Go. Name the service “ChinookServiceRef”.
wcfselfhost_03
We need a Web Form to test the service so create one naming it “Defaut.aspx”. This form will have just two buttons and a gridview. The first button will consume the GetArtists() OperationContract over the basicHttpBinding resulting in filling the gridview. The second button will consume the second operation contract GetArtistsAlbums resulting in writing the results in the page. Here is the Default’s html code.

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %>

<!DOCTYPE html>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title></title>
    <style type="text/css">
        .auto-style1 {
            color: #0099FF;
            font-size: x-large;
        }
    </style>
</head>
<body>
    <form id="form1" runat="server">
    <div class="auto-style1">
    
        <strong>
        <br />
        <asp:Button ID="Button1" runat="server" OnClick="Button1_Click" Text="Get artists with BasicHttpBinding" Width="351px" />
&nbsp;<asp:Button ID="Button2" runat="server" OnClick="Button2_Click" Text="Print artist albums with NetTcpBinding" />
        <br />
        <asp:GridView ID="GridView1" runat="server" BackColor="White" BorderColor="#CCCCCC" BorderStyle="None" BorderWidth="1px" CellPadding="3">
            <FooterStyle BackColor="White" ForeColor="#000066" />
            <HeaderStyle BackColor="#006699" Font-Bold="True" ForeColor="White" />
            <PagerStyle BackColor="White" ForeColor="#000066" HorizontalAlign="Left" />
            <RowStyle ForeColor="#000066" />
            <SelectedRowStyle BackColor="#669999" Font-Bold="True" ForeColor="White" />
            <SortedAscendingCellStyle BackColor="#F1F1F1" />
            <SortedAscendingHeaderStyle BackColor="#007DBB" />
            <SortedDescendingCellStyle BackColor="#CAC9C9" />
            <SortedDescendingHeaderStyle BackColor="#00547E" />
        </asp:GridView>
        </strong>
    
    </div>
    </form>
</body>
</html>

Before showing you the code behind file, I wanna show you the changes have been mmade in the Web.config file when you added the service reference.

<?xml version="1.0"?>
<configuration>
  <system.web>
    <compilation debug="true" targetFramework="4.5"/>
    <httpRuntime targetFramework="4.5"/>
  </system.web>
  <system.serviceModel>
    <bindings>
      <basicHttpBinding>
        <binding name="BasicHttpBinding_IChinookService"/>
      </basicHttpBinding>
      <netTcpBinding>
        <binding name="NetTcpBinding_IChinookService"/>
      </netTcpBinding>
    </bindings>
    <client>
      <endpoint address="http://localhost:8090/ChinookHttpService" binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_IChinookService" contract="ChinookServiceRef.IChinookService" name="BasicHttpBinding_IChinookService"/>
      <endpoint address="net.tcp://localhost:8080/ChinookHttpService" binding="netTcpBinding" bindingConfiguration="NetTcpBinding_IChinookService" contract="ChinookServiceRef.IChinookService" name="NetTcpBinding_IChinookService">
        <identity>
          <userPrincipalName value="developer-pc\developer"/>
        </identity>
      </endpoint>
    </client>
  </system.serviceModel>
</configuration>

Notice that two different endpoints have been created, one for the basicHttpBinding and another for the netTcpBinding. We will use the name of those endpoints when we create WCF clients. Here is the code for the code behind file for the Default Web Form.

protected void Button1_Click(object sender, EventArgs e)
    {
        ChinookServiceClient client = new ChinookServiceClient("BasicHttpBinding_IChinookService");
        List<ArtistDTO> artists = client.GetArtists();
        List<string> artistNames = new List<string>();
        foreach (var artist in artists)
        {
            artistNames.Add(artist.Name);
        }
        GridView1.DataSource = artistNames;
        GridView1.DataBind();
    }
    protected void Button2_Click(object sender, EventArgs e)
    {
        GridView1.Visible = false;
        ChinookServiceClient client = new ChinookServiceClient("NetTcpBinding_IChinookService");
        List<ArtistAlbums> artistAlbums = new List<ArtistAlbums>();
        artistAlbums = client.GetArtistsAlbums();
        foreach (var artistAlbum in artistAlbums)
        {
            if (artistAlbum.Albums.Count > 0)
            {
                Response.Write("Artist : " + "<strong>"+ artistAlbum.Name + "</strong><br/> Albums: <br/>");
                foreach (var album in artistAlbum.Albums)
                {
                    Response.Write("-------   " + "<span style='color:crimson'>"
                       + album + "</span>   ------- <br/>");
                }
                Response.Write("------------------------------------------------------------- <br/><br/>");
            }
        }           
    }

As we have already mentioned, the gridview control will be filled consuming the “GetArtists()” method under the BasicHttpBinding_IChinookService and all albums will be displayed consuming the GetArtistAlbums() under the NetTcpBinding_IChinookService binding. Set as start up projects both the ChinookHostConsole (it should start first) and the ChinookWebClient. Build and run your solution.
wcfselfhost_04
wcfselfhost_05
Let’s try something interesting to ensure that these actions are indeed consumed under different protocols. In the console application remove the httpUrl parameter some that the service is hosted only under the TCP protocol. Also comment out all the ServiceMetadataBehavior code we wrote so we can create the proxy class.

ServiceHost host = new ServiceHost(typeof(ChinookWcfService.ChinookService), netTcpUrl);
//ServiceMetadataBehavior serviceMetaBehavior = new ServiceMetadataBehavior();
//serviceMetaBehavior.HttpGetEnabled = true;
//host.Description.Behaviors.Add(serviceMetaBehavior);

Build and run your application again. Press the button to print all artist albums, using the TCP protocol. It should work just fine. Now press the button to consume the service using the BasicHttpBinding.
wcfselfhost_06
Obviously, you got an error since the service is hosted only under TCP protocol. That’s it, we saw how to self-host a WCF service in a console application under different bindings. I hope you enjoyed the post. You can download the project we created from here . Make sure you follow this blog to get notified for newly posts!



Categories: ASP.NET, WCF

Tags: , , , ,

3 replies

  1. Really good sample!

  2. Great Article, but how we can implement security if we have multiple end point in our service like this post.

    • Hi Asad,

      In WCF you set the Security mode in the Binding level (not endpoint). Hence in our case, you could set different security modes in the configuration file, one for BasicHttpBinding and another one for NetTcpBinding. This link, will guide you how to do it.

Leave a comment