WCF Example

WCF Example

Warning

The following information is based on the Windows Vista® February CTP. Further changes are possible before the final release of the .NET Framework 3.0 runtime.

This section contains the C# implementation of a simple Windows Communication Foundation solution for managing proper names (for example, product names, trademarks, and technical terms). This example is excerpted in the section In-Depth: Windows Communication Foundation. The entire solution is divided into the following portions:

  • The WCF service source files IProperNamesService.cs, ProperNamesService.cs and ProgramService.cs, and the associated configuration file App.config.

  • The WCF client source files ProgramClient.cs and ProperNamesClient.cs and the configuration file App.config. The last two were generated from the service using SvcUtil.exe.

The client and service are both hosted as console applications.

Service Files

The proper names Windows Communication Foundation service is composed of three source files and a configuration file. The service project requires added references to two non-standard assemblies: System.ServiceModel.dll and System.Runtime.Serialization.dll.

IProperNamesService.cs

This file contains the declaration for the service, data and fault contracts that the service implements.

//IProperNamesService.cs
using System;
using System.Collections.Generic;
using System.ServiceModel;
using System.Runtime.Serialization;

namespace NamingServices
{
   //IProperNamesService contract declaration
   [ServiceContract]
   interface IProperNamesService
   {
      [OperationContract]
      [FaultContract(typeof(ProperNameRecord))]
      void AddProperName(String properName, int ownerID);
      [OperationContract]
      bool IsProperName(String properName);
      [OperationContract]
      int GetTotalByOwner(Int32 ownerID);
      [OperationContract]
      ProperNameRecord MatchClosestName(String properName);
   }

   [DataContract]
   public struct ProperNameRecord
   {
      [DataMember]
      public String properName;
      [DataMember]
      public Int32 ownerID;
      [DataMember]
      public DateTime entryDateTime;

      public ProperNameRecord(String name, Int32 id, DateTime dt)
      {
         properName = name;
         ownerID = id;
         entryDateTime = dt;
      }
   }
}

ProperNamesService.cs

This file implements the service operations declared in IProperNamesService.cs. Note the following:

  • The initialization of the InstanceContextMode property of ServiceBehaviorAttribute to indicate that only a single instance (singleton) of the service should be run for all clients. This was necessary because of the simplistic use of a List object for persistence.

  • The conditional generation of fault exceptions in response to logical errors in the construction of the proper name record.

//ProperNamesService.cs
using System;
using System.Collections.Generic;
using System.ServiceModel;
using System.Threading;

namespace NamingServices
{
   [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
   public class ProperNamesService : IProperNamesService
   {
      private List<ProperNameRecord> prnList = new 
            List<ProperNameRecord>();

      [OperationBehavior]
      public void AddProperName(String properName, int ownerID)
      {
            properName = properName.Trim();
            ProperNameRecord pnr = new ProperNameRecord(properName,
                 ownerID, DateTime.UtcNow);
            if (ownerID < 0)
                throw new FaultException<ProperNameRecord>(pnr,
                      "Invalid owner ID");
            if (IsProperName(properName))
                throw new FaultException<ProperNameRecord>(pnr,
                      "Duplicate Name");
         prnList.Add(new ProperNameRecord(properName, ownerID,
                      DateTime.UtcNow));
         Console.WriteLine("Registered " + properName + ".");
      }

      [OperationBehavior]
      public bool IsProperName(String properName)
      {
            properName = properName.Trim();
         foreach (ProperNameRecord prn in prnList)
         {
            if (prn.properName.Equals(properName, 
                 StringComparison.CurrentCultureIgnoreCase))
               return true;
         }
         return false;
      }
      
      [OperationBehavior]
      public int GetTotalByOwner(Int32 ownerID)
      {
         int count = 0;
         foreach (ProperNameRecord prn in prnList)
         {
            if (prn.ownerID == ownerID)
               count++;
         }
         return count;
      }

      [OperationBehavior]
      public ProperNameRecord MatchClosestName(String properName)
      {
         //For prototype, just return dummy record.
         ProperNameRecord dummy = new ProperNameRecord("????", -1,
           DateTime.Now);
         //Mimic long db lookup operation
         Thread.Sleep(4000);
         return dummy;
      }
   }
}

ProgramService.cs

This file hosts the Windows Communication Foundation service as a console application. Typically endpoints and other characteristics of the service are defined in the associated configuration file, which is shown next.

//ProgramService.cs
using System;
using System.ServiceModel;
using System.Configuration;

namespace NamingServices
{
    class Program
    {
        static void Main(string[] args)
        {
            Uri baseAddress = new Uri 
               (ConfigurationManager.AppSettings["baseAddress"]);
            using (ServiceHost sh = new ServiceHost 
               (typeof(ProperNamesService), baseAddress))
            {
                sh.Open();
                Console.WriteLine("The Proper Names Service is 
                    running... [Press Enter to close]");
                Console.ReadLine();
                Console.WriteLine("Closing service...");
                sh.Close();
            }
        }
    }
} 

App.config

The service's configuration file is divided into two portions: a general application settings portion and a WCF service model section. The latter describes the behaviors of the service and defines its endpoints.

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
   <appSettings>
      <add key="baseAddress" value="https://localhost:8000/ProperNamesService/" />
   </appSettings>

   <system.serviceModel>
      <services>
         <service 
             name="NamingServices.ProperNamesService" 
             behaviorConfiguration="ProperNameBehavior">
            <!-- use base address provided by host via appSettings -->
            <endpoint address=""
                      binding="basicHttpBinding"
                      bindingConfiguration="Binding1" 
                      contract="NamingServices.IProperNamesService" />
         </service>
      </services>
      <bindings>
         <basicHttpBinding>
            <binding name="Binding1" />
         </basicHttpBinding>
      </bindings>
      <behaviors>
         <behavior 
             name="ProperNameBehavior"
             returnUnknownExceptionsAsFaults="True" >
         </behavior>
      </behaviors>
   </system.serviceModel>
</configuration>

Client Files

The client is composed of two code files and a configuration file.

ProperNamesClient.cs

This file was renamed from output.cs as generated by SvcUtil.exe and contains the proxy and contract definitions. The listing below has been reformatted and simplified slightly to aid comprehension.

using System;
using System.Runtime.Serialization;
using System.ServiceModel;

namespace NamingServices
{
    using System.Runtime.Serialization;

    [DataContractAttribute()]
    public partial struct ProperNameRecord : IExtensibleDataObject
    {
        private ExtensionDataObject extensionDataField;
        private DateTime entryDateTimeField;
        private int ownerIDField;
        private string properNameField;

        public ExtensionDataObject ExtensionData
        {
            get
            { return this.extensionDataField; }
            set
            { this.extensionDataField = value; }
        }

        [DataMemberAttribute()]
        public System.DateTime entryDateTime
        {
            get
            { return this.entryDateTimeField; }
            set
            { this.entryDateTimeField = value; }
        }

        [DataMemberAttribute()]
        public int ownerID
        {
            get
            { return this.ownerIDField; }
            set
            { this.ownerIDField = value; }
        }

        [DataMemberAttribute()]
        public string properName
        {
            get
            { return this.properNameField; }
            set
            { this.properNameField = value; }
        }
    }
}

[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")]
[ServiceContractAttribute()]
public interface IProperNamesService
{
    [OperationContractAttribute]
    [FaultContractAttribute(typeof(NamingServices.ProperNameRecord)]
    void AddProperName(string properName, int ownerID);

    [OperationContractAttribute]
    int GetTotalByOwner(int ownerID);

    [OperationContractAttribute]
    bool IsProperName(string properName);

    [OperationContractAttribute]
    NamingServices.ProperNameRecord MatchClosestName(string properName);
}

[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")]
public interface IProperNamesServiceChannel : IProperNamesService, IClientChannel
{
}

[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")]
public partial class ProperNamesServiceProxy : ClientBase<IProperNamesService>, IProperNamesService
{

    public ProperNamesServiceProxy()
    {
    }

    public ProperNamesServiceProxy(string endpointConfigurationName)
        :  base(endpointConfigurationName)
    {
    }

    public ProperNamesServiceProxy(string endpointConfigurationName, string remoteAddress)
        :  base(endpointConfigurationName, remoteAddress)
    {
    }

    public ProperNamesServiceProxy(string endpointConfigurationName, EndpointAddress remoteAddress)
        :  base(endpointConfigurationName, remoteAddress)
    {
    }

    public ProperNamesServiceProxy(System.ServiceModel.Channels.Binding binding, EndpointAddress remoteAddress)
        :  base(binding, remoteAddress)
    {
    }

    public void AddProperName(string properName, int ownerID)
    {
        base.InnerProxy.AddProperName(properName, ownerID);
    }

    public int GetTotalByOwner(int ownerID)
    {
        return base.InnerProxy.GetTotalByOwner(ownerID);
    }

    public bool IsProperName(string properName)
    {
        return base.InnerProxy.IsProperName(properName);
    }

    public NamingServices.ProperNameRecord MatchClosestName(string properName)
    {
        return base.InnerProxy.MatchClosestName(properName);
    }
}

ProgramClient.cs

This file is an implementation of a simple Windows Communication Foundation client for the proper names service. It requires references to be added to the project for the nonstandard System.ServiceModel.dll and System.Runtime.Serialization.dll assemblies. The primary task the client needs to perform is the creation of a proxy to access its operations. Also note that the hierarchical handling of fault exceptions towards the end of the file.

//ProgramClient.cs
using System;
using System.Runtime.Serialization;
using System.ServiceModel;

namespace ProperNamesClient
{
   class Program
   {
      static void Main(string[] args)
      {
         Console.WriteLine("Starting client...");
         // Create a proxy:
         using (ProperNamesServiceProxy proxy = new 
             ProperNamesServiceProxy())
         {
            try
            {
               String bd = "Bob Dobbs";
               //Use the proxy to call service operations.
               proxy.AddProperName(bd, 1234);
               if (proxy.IsProperName("bob dobbs "))
                  Console.WriteLine(bd + " was registered properly.");
               proxy.AddProperName("Mike LaRosa", 1234);
               Console.WriteLine("Employee 1234 has registered " +
                   proxy.GetTotalByOwner(1234) + " proper names.");
               NamingServices.ProperNameRecord pnr = 
                    proxy.MatchClosestName(bd);
               Console.WriteLine("Closest match to is: " + 
                    pnr.properName);
               //IAsyncResult arMatchClosestName = proxy.
                     BeginMatchClosestName(bd, 
                          proxy.EndMatchClosestName, proxy);
               //Test SOAP fault mechanism:
               proxy.AddProperName(bd, 1234);
            }
            catch (FaultException<NamingServices.ProperNameRecord> e)
            {
               Console.WriteLine("Name registration problem: " + 
                        e.Message);
            }
            catch (FaultException e)
            {
               Console.WriteLine("General FaultException: " + 
                        e.GetType().Name + " - " + e.Message);
            }
            catch (Exception e)
            {
               Console.WriteLine("Exception thrown: " + e.Message);
            }
         }
         Console.WriteLine();
         Console.WriteLine("Press ENTER to shut down client");
         Console.ReadLine();
      }
   }
} 

App.config

The client configuration file was completely generated by SvcUtil.exe. The listing below has been simplified by eliminating some of the SOAP message format and security attributes.

<?xml version="1.0" encoding="utf-8"?>
<configuration>
   <system.serviceModel>
      <bindings>
         <basicHttpBinding>
            <binding name="BasicHttpBinding_IProperNamesService" 
                 bypassProxyOnLocal="false"
               messageEncoding="Text" textEncoding="utf-8" 
                 transferMode="Buffered"
               useDefaultWebProxy="true">
               <security mode="None">
               </security>
            </binding>
         </basicHttpBinding>
      </bindings>
      <client>
         <endpoint address="https://localhost:8000/ProperNamesService/"
            binding="basicHttpBinding" 
            bindingConfiguration="BasicHttpBinding_IProperNamesService"
            contract="IProperNamesService" 
            name="BasicHttpBinding_IProperNamesService" />
      </client>
   </system.serviceModel>
</configuration>