Introduction to WCF - Part 5 - Multiple Endpoints

05 May 2011

Exposing Multiple Endpoints

For this example we will create two IIS/AppFabric-hosted services - one for internal users, the other external customers. The Internal service implements the following contracts:

IInternalServices

  • Services can be consumed by users internal to the organization
  • Uses a named pipe binding

ICommonServices

  • Services can be consumed by anyone
  • Uses a named pipe binding

The Customer service implements the following contracts:

ICustomerServices

  • Contract can be consumed by customers of the organization
  • Uses an HTTP binding

ICommonServices

  • Services can be consumed by anyone
  • Uses an HTTP binding

We’ll then create:

  • An ASP.NET client to consume ICustomerServices and ICommonServices
  • A WPF client to consume IInternalServices and ICommonServices

The arrangement is as shown in the following UML diagram:

Creating and Deploying the Services

  1. Create a new WCF Service Application project called WcfOrganization
  2. Define the three interfaces required as follows (from a reusability standpoint, it's better to define them in separate files):
  3. [ServiceContract]
    public interface ICommonServices
    {
        [OperationContract]
        string OrgName();
    }
    
    [ServiceContract]
    public interface IInternalServices
    {
        [OperationContract]
        string AddCust(int custID);
    
        [OperationContract]
        string RemoveCust(int custID);
    }
    
    [ServiceContract]
    public interface ICustomerServices
    {
        [OperationContract]
        string CustData();
    }
    

  4. Delete the auto-generated default service and add two new WCF services named Customer.svc and Internal.svc:
  5. public class Customer : ICommonServices, ICustomerServices
    {
        public string OrgName()
        {
            return "MyOrg";
        }
    
        public string CustData()
        {
            return "My Customer Data";
        }
    }
    
    public class Internal : ICommonServices, IInternalServices
    {
        public string OrgName()
        {
            return "MyOrg";
        }
    
        public string AddCust(int custID)
        {
            return "Customer " + custID.ToString() + " added";
        }
    
        public string RemoveCust(int custID)
        {
            return "Customer " + custID.ToString() + " removed";
        }
    }
    

  6. Either manually or by using the WCF Configuration Editor, modify the web.config file as follows:
  7. :
    :
      <service name="WcfOrganization.Internal">
        <endpoint address="Common" 
                  binding="netNamedPipeBinding" 
                  name="Common" 
                  contract="WcfOrganization.ICommonServices" />
        <endpoint address="Internal" 
                  binding="netNamedPipeBinding" 
                  name="Internal" 
                  contract="WcfOrganization.IInternalServices" />
        <host>
          <baseAddresses>
            <add baseAddress="net.pipe://localhost:9003/OrgInternal" />
          </baseAddresses>
        </host>
      </service>
      <service name="WcfOrganization.Customer">
        <endpoint address="Common" 
                  binding="ws2007HttpBinding" 
                  name="Common" 
                  contract="WcfOrganization.ICommonServices" />
        <endpoint address="Customer" 
                  binding="ws2007HttpBinding" 
                  name="Customer" 
                  contract="WcfOrganization.ICustomerServices" />
        <host>
          <baseAddresses>
            <add baseAddress="http://localhost:9003/OrgCustomer" />
          </baseAddresses>
        </host>
      </service>
    </services>
    <behaviors>
      <serviceBehaviors>
        <behavior>
          <serviceMetadata httpGetEnabled="true"/>
    :
    :
    

  8. Note that we define two services (named Internal and Customer) and that each service has two endpoints (the Internal service uses netNamePipeBinding, while Customer uses ws2007HttpBinding). We also define a base address for each service - Internal has a net.pipe address, while Customer has an http address)
  9. Build the solution and then switch to the IIS Management console app
  10. Create two new Applications - one named OrgInternal, the other OrgCustomer. The physical path for both applications should be set to the WcfOrganization project directory (make sure to modify the permissions for both apps as previously discussed)
  11. Select the OrgInternal application and then click Advanced Settings
  12. As shown in the previous Non-HHTP Bindings example, edit the Enabled Protocols property to be net.pipe,http
  13. Test that you can Browse to both services at:

    http://localhost/OrgCustomer/Customer.svc and
    http://localhost/OrgInternal/Internal.svc

Creating the Clients

  1. Create a new solution containing a WPF application and an ASP.NET application
  2. Add service references to both projects using the appropriate URI (e.g. http://localhost/OrgCustomer/Customer.svc for the ASP.NET app and http://localhost/OrgInternal/Internal.svc for the WPF app)
  3. Add a listbox and three buttons (and click event handlers for the buttons) to the WPF app, then add the following code to MainWindow.xaml.cs:
  4. public partial class MainWindow : Window
    {
        private InternalRef.CommonServicesClient commonServices;
        private InternalRef.InternalServicesClient internalServices;
    
        public MainWindow()
        {
            InitializeComponent();
    
            commonServices = new InternalRef.CommonServicesClient();
            internalServices = new InternalRef.InternalServicesClient();
        }
    
        private void button1_Click(object sender, RoutedEventArgs e)
        {
            listBox1.Items.Add(commonServices.OrgName());
        }
    
        private void button2_Click(object sender, RoutedEventArgs e)
        {
            listBox1.Items.Add(internalServices.AddCust(99));
        }
    
        private void button3_Click(object sender, RoutedEventArgs e)
        {
            listBox1.Items.Add(internalServices.RemoveCust(99));
        }
    }
    

  5. Notice how Visual Studio creates proxy objects for each service contract (CommonServicesClient and InternalServicesClient)
  6. Add a listbox to the ASP.NET app and then add the following code to Default.aspx.cs:
  7. public partial class _Default : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            CustomerRef.CommonServicesClient commonServices = 
              new CustomerRef.CommonServicesClient();
            CustomerRef.CustomerServicesClient customerServices = 
              new CustomerRef.CustomerServicesClient();
    
            ListBox1.Items.Add("OrgName() => " + commonServices.OrgName());
            ListBox1.Items.Add("CustData() => " + customerServices.CustData());
        }
    }
    

  8. Running both apps should produce the following: