Click here to Skip to main content
15,867,453 members
Articles / Programming Languages / XML

WCF: A few tips

Rate me:
Please Sign up or sign in to vote.
4.61/5 (66 votes)
2 Dec 2010CPOL6 min read 153.1K   699   162   35
A few tips when working with WCF.

Introduction

I am by no means a WCF expert, but I have been using it a bit at work of late, and I have come across a few issues that I thought may be worth mentioning in as a top-tips style article.

So that is what this article will be.

This article assumes you know a little bit about WCF, and that you know enough to edit config files.

Please make sure to read the "Running the code" section before you try to run it.

Tip 1: Debugging WCF

One of the first things that you will most likely want to do is call a WCF Service and debug it. Now when I first started creating WCF Services at my company, I created IIS web hosted services. While this may be a great end solution, it doesn't really offer the opportunity to be debugged that easily. So my advice would be to create a Console based implementation of your WCF Service and use this for debugging purposes, and then when you are happy, you can push the tested code back into an IIS web based service.

The attached demo app uses a Console hosted (self hosted) WCF Service, which is hosted as follows:

C#
using System;
using System.ServiceModel;

namespace WcfServiceLibrary1
{
    class Program
    {
        static void Main(string[] args)
        {

            Console.WriteLine("----------------------------" + 
                    "-----------------------------------------");
            Console.WriteLine("This is the WCF Service " + 
                    "hosting within a console application");
            Console.WriteLine("The service can also be hosted with a web browser");
            Console.WriteLine("");
            Console.WriteLine("Initializing WCF Service...");

            // The service configuration is loaded from app.config
            using (ServiceHost host = new ServiceHost(typeof(Service)))
            {
                host.Open();

                Console.WriteLine("WCF Service is ready for requests." +  
                "Press any key to close the service.");
                Console.WriteLine();
                Console.Read();

                Console.WriteLine("Closing service...");
            }
        }
    }
}

So this is enough to host the WCF Service.

Tip 2: Adding a Service Reference

If we consider how the attached demo app WCF Service is configured at the server end:

XML
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <appSettings>
    <add key="InitialTransactionValue" value="12" />
  </appSettings>
  <system.serviceModel>
    <behaviors>
      <serviceBehaviors>
        <behavior name="MetadataBehavior">
          <serviceMetadata httpGetEnabled="true" 
                           httpGetUrl="http://localhost:8001/ServiceHost/mex" />
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <services>
      <service behaviorConfiguration="MetadataBehavior" 
               name="WcfServiceLibrary1.Service">
        <endpoint
          address="service"
          binding="netTcpBinding"
          contract="WcfServiceLibrary1.IService"
          name="TcpBinding" />
        <endpoint
          address="service"
          binding="wsDualHttpBinding"
          contract="WcfServiceLibrary1.IService"
          name="HttpBinding" />
        <endpoint
          address="mex"
          binding="mexHttpBinding"
          contract="IMetadataExchange"
          name="MexBinding"/>
        <host>
          <baseAddresses>
            <add baseAddress="net.tcp://localhost:8000/ServiceHost/" />
            <add baseAddress="http://localhost:8001/ServiceHost/" />
          </baseAddresses>
        </host>
      </service>
    </services>
  </system.serviceModel>
</configuration>

It can be seen that there are three possible end points:

  • netTcpBinding
  • HttpBinding
  • MexBinding

and two possible host addresses:

  • net.tcp://localhost:8000/ServiceHost/
  • http://localhost:8001/ServiceHost/

My personal experience was that, I could never find the running service if I tried to add a service reference using the "tcp" host address. I always had to use the "http" host address to find the running service. This is shown below:

Image 1

So this failed where I tried to use "tcp" host address. But if I used the "http" host address, all is cool.

Image 2

So I would recommend always creating an HTTP binding to allow you to gain access to the service even if you don't end up using the HTTP binding.

Amendment

One of the readers of this article, Benoît Dion, suggested that I could fix this with altering the mexBinding end point in the service end.

So this is what I did with this small App.Config change at the service end:

XML
<!--<endpoint
  address="mex"
  binding="mexHttpBinding"
  contract="IMetadataExchange"
  name="MexBinding"/>-->
<endpoint 
  address="mex" 
  binding="mexTcpBinding" 
  contract="IMetadataExchange" />

And I am pleased to inform that this now allows you to add the service reference using the TCP address: net.tcp://localhost:8000/ServiceHost/.

So thanks Benoît.

Tip 3: Service Configuration

In the attached demo app, the WCF Service definition looks like the following:

C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;
using System.Net.Security;

namespace WcfServiceLibrary1
{
    /// <summary>
    /// The service contract
    /// </summary>
    [ServiceContract(
    Name = "IService",
    Namespace = "WcfServiceLibrary1.Service")]
    public interface IService
    {
        [OperationContract]
        [ReferencePreservingDataContractFormat] 
        List<Person> GetPeople();

        [OperationContract]
        List<Person> GetPeopleWithOutCicularReferencing();
    }

    //A simple DataContract serializable class
    [DataContract]
    public class Person
    {
        int age = 0;
        string name = string.Empty;
        List<Person> children = new List<Person>();
        Person parent = null;

        [DataMember]
        public int Age
        {
            get { return age; }
            set { age = value; }
        }

        [DataMember]
        public string Name
        {
            get { return name; }
            set { name = value; }
        }

        [DataMember]
        public List<Person> Children
        {
            get { return children; }
            set { children = value; }
        }

        [DataMember]
        public Person Parent
        {
            get { return parent; }
            set { parent = value; }
        }
    }
}

It can be seen that the service contract operations (methods) return a generic List<T>, which is a personal choice. But what you must make sure is that the proxy (client) for the WCF Service actually returns the same sort of objects. This is achieved by configuring the WCF Service reference when you add it.

To add a reference, you must ensure that you have access to the running WCF Service host (i.e., the actual service); in the attached demo, this is the Console app.

So, providing you have access to the actual WCF Service, you should ensure that you configure the service to use the same types as the operation contracts that were defined within the service contract interface. Which in the case shown above was the generic List<T>, so we can change this in the dropdown combo boxes provided within the Data Type section of the Service Reference Settings dialog.

Image 3

If you do not have a Service Reference and are in the process of adding it, this Service Reference Settings dialog is accessable from the Advanced button of the Add Service Reference dialog.

Image 4

Tip 4: Service Parameters

When the WCF Service was eventually added, it was found that some of the default parameters just were not big enough. Shown below is the before and after App.Config for the client:

Image 5

So I changed some of these parameters to improve the service throughput:

Image 6

Tip 5: Circular References

As part of something we were doing, I needed to have a circular reference; you know when object A holds a reference to object B, and object B holds a reference to object A.

What was found was that the default DataContractSerializer couldn't be to set to deal with serializing a cyclic object graph in a config file, even though it is totally capable of doing this task. It appeared that some of the properties of the DataContractSerializer can only be set via a constructor call, not via config files. So I did a bit of research into this and found some interesting code that allowed you to create a specialized DataContractSerializer that would allow cyclic object graphs to be serialized. The code that I found allows to adorn your service interface operation contracts with a custom attribute called ReferencePreservingDataContractFormatAttribute.

C#
using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.Runtime.Serialization;
using System.ServiceModel.Description;

namespace WcfServiceLibrary1
{
    /// <summary>
    /// Allows us to adorn the IService contract with this
    /// attribute to indicate that a specialized DataContractSerializer
    /// should be used that has preserveObjectReferences set true
    /// </summary>
    public class ReferencePreservingDataContractFormatAttribute 
        : Attribute, IOperationBehavior
    {
        #region IOperationBehavior Members
        public void AddBindingParameters(OperationDescription description,
            BindingParameterCollection parameters)
        {
        }

        public void ApplyClientBehavior(OperationDescription description,
            System.ServiceModel.Dispatcher.ClientOperation proxy)
        {
            IOperationBehavior innerBehavior =
              new ReferencePreservingDataContractSerializerOperationBehavior(description);
            innerBehavior.ApplyClientBehavior(description, proxy);
        }

        public void ApplyDispatchBehavior(OperationDescription description,
            System.ServiceModel.Dispatcher.DispatchOperation dispatch)
        {
            IOperationBehavior innerBehavior =
              new ReferencePreservingDataContractSerializerOperationBehavior(description);
            innerBehavior.ApplyDispatchBehavior(description, dispatch);
        }

        public void Validate(OperationDescription description)
        {
        }

        #endregion
    }
}

Which in turns create a new ReferencePreservingDataContractSerializerOperationBehavior object that inherits from IOperationBehavior. It is this class that is responsible for creating the actual DataContractSerializer that allows serializing a cyclic object graph. The code for this class is as follows:

C#
using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.Runtime.Serialization;
using System.ServiceModel.Description;

namespace WcfServiceLibrary1
{
    /// <summary>
    /// A specialized DataContractSerializer that has 
    /// preserveObjectReferences set true, which allows for
    /// circular references to be serialized
    /// </summary>
    public class ReferencePreservingDataContractSerializerOperationBehavior :
        DataContractSerializerOperationBehavior
    {
        #region Ctor
        public ReferencePreservingDataContractSerializerOperationBehavior(
            OperationDescription operationDescription)
            : base(operationDescription) { }
        #endregion

        #region Public Methods

        public override XmlObjectSerializer CreateSerializer(Type type,
               XmlDictionaryString name, XmlDictionaryString ns, 
               IList<Type> knownTypes)
        {
            return new DataContractSerializer(type, name, ns, knownTypes,
                2147483646 /*maxItemsInObjectGraph*/,
                false/*ignoreExtensionDataObject*/,
                true/*preserveObjectReferences*/,
                null/*dataContractSurrogate*/);
        }
        #endregion
    }
}

The important part is the constructor of the DataContractSerializer where we set the preserveObjectReferences value. This is the all important part.

The importance of this class may become clearer if we see an example screenshot from the attached demo where the following applies:

  • The first WCF call uses ReferencePreservingDataContractFormatAttribute, so is safe to return circular references.
  • The second WCF call uses the standard DataContractSerializer, so causes a communication Exception.

If we just remind ourselves what the service contract looks like:

C#
/// <summary>
/// The service contract
/// </summary>
[ServiceContract(
Name = "IService",
Namespace = "WcfServiceLibrary1.Service")]
public interface IService
{
    [OperationContract]
    [ReferencePreservingDataContractFormat] 
    List<Person> GetPeople();

    [OperationContract]
    List<Person> GetPeopleWithOutCicularReferencing();

}

We can see that the first call worked just fine, as we used ReferencePreservingDataContractFormatAttribute, so is safe to return circular references.

But the next call failed completely, and resulted in a communication Exception. This is because the standard DataContractSerializer doesn't have the preserveObjectReferences parameter turned on.

Image 7

I can take no resposibility for creating either the ReferencePreservingDataContractFormatAttribute or the ReferencePreservingDataContractSerializerOperationBehavior code. This is down to Sowmy Srinivasan, whose blog I found it on. I totally get what it's about though, as I hope you do now. You can read more at Sowmy Srinivasan's blog.

Wrap Up

I am fully aware that these tips may not be suitable for everyone's WCF Services, but they are things that I found helped my own code; as such, I thought it may be worth sharing what I found. I hope at least one of these tips is useful to you.

Running the Code

You will need to change the App.Config in the ServiceClientTestApp project to use your own PC user. So you will need to change the following line:

XML
<userPrincipalName value="XXXXXX" />

to be something like:

XML
<userPrincipalName value="YOUR_PC_NAME\YOUR_USER"/>

If you don't change this, the ServiceClientTestApp project will not work.

We're Done

If you liked this article and found it useful, please leave a vote/comment.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
United Kingdom United Kingdom
I currently hold the following qualifications (amongst others, I also studied Music Technology and Electronics, for my sins)

- MSc (Passed with distinctions), in Information Technology for E-Commerce
- BSc Hons (1st class) in Computer Science & Artificial Intelligence

Both of these at Sussex University UK.

Award(s)

I am lucky enough to have won a few awards for Zany Crazy code articles over the years

  • Microsoft C# MVP 2016
  • Codeproject MVP 2016
  • Microsoft C# MVP 2015
  • Codeproject MVP 2015
  • Microsoft C# MVP 2014
  • Codeproject MVP 2014
  • Microsoft C# MVP 2013
  • Codeproject MVP 2013
  • Microsoft C# MVP 2012
  • Codeproject MVP 2012
  • Microsoft C# MVP 2011
  • Codeproject MVP 2011
  • Microsoft C# MVP 2010
  • Codeproject MVP 2010
  • Microsoft C# MVP 2009
  • Codeproject MVP 2009
  • Microsoft C# MVP 2008
  • Codeproject MVP 2008
  • And numerous codeproject awards which you can see over at my blog

Comments and Discussions

 
QuestionXML in tags Pin
Joezer BH4-Aug-13 5:51
professionalJoezer BH4-Aug-13 5:51 
QuestionVery helpful article Pin
ravithejag19-Nov-12 18:13
ravithejag19-Nov-12 18:13 
QuestionGreat article. I have one hint about the hosting client and the using statement. Pin
MagicMarvin18-Oct-12 1:01
MagicMarvin18-Oct-12 1:01 
AnswerRe: Great article. I have one hint about the hosting client and the using statement. Pin
Sacha Barber18-Oct-12 1:33
Sacha Barber18-Oct-12 1:33 
GeneralMy vote of 5 Pin
mayuri2027-Jun-12 21:48
mayuri2027-Jun-12 21:48 
GeneralMy vote of 5 Pin
Pankaj Chamria2-Feb-12 22:53
Pankaj Chamria2-Feb-12 22:53 
GeneralMy vote of 5 Pin
valamas22-Mar-11 13:51
valamas22-Mar-11 13:51 
GeneralRe: My vote of 5 Pin
Sacha Barber25-Jul-11 20:06
Sacha Barber25-Jul-11 20:06 
GeneralMy vote of 5 Pin
prasad0213-Dec-10 6:51
prasad0213-Dec-10 6:51 
GeneralMy vote of 5 Pin
Glenn Hudson7-Dec-10 5:10
Glenn Hudson7-Dec-10 5:10 
GeneralSimple solution for cyclic graphs Pin
Ido Flatow6-Dec-10 0:50
Ido Flatow6-Dec-10 0:50 
GeneralRe: Simple solution for cyclic graphs Pin
Sacha Barber6-Dec-10 1:03
Sacha Barber6-Dec-10 1:03 
GeneralRe: Simple solution for cyclic graphs Pin
Ido Flatow6-Dec-10 1:07
Ido Flatow6-Dec-10 1:07 
GeneralRe: Simple solution for cyclic graphs Pin
Sacha Barber6-Dec-10 1:43
Sacha Barber6-Dec-10 1:43 
GeneralTip 3 Pin
Simon Capewell3-Dec-10 0:08
Simon Capewell3-Dec-10 0:08 
GeneralRunning this code on Vista/Windows 7 [modified] Pin
Sky Sanders12-Feb-10 0:22
Sky Sanders12-Feb-10 0:22 
GeneralRe: Running this code on Vista/Windows 7 Pin
Sacha Barber12-Feb-10 2:18
Sacha Barber12-Feb-10 2:18 
GeneralRe: Running this code on Vista/Windows 7 Pin
Sky Sanders12-Feb-10 3:43
Sky Sanders12-Feb-10 3:43 
GeneralMy vote of 2 Pin
Christian Weyer24-Jun-09 2:02
Christian Weyer24-Jun-09 2:02 
GeneralRe: My vote of 2 Pin
Sacha Barber12-Feb-10 2:17
Sacha Barber12-Feb-10 2:17 
GeneralCool Article.. Pin
Islam ElDemery30-Jun-08 20:55
Islam ElDemery30-Jun-08 20:55 
GeneralRe: Cool Article.. Pin
Sacha Barber30-Jun-08 20:59
Sacha Barber30-Jun-08 20:59 
GeneralA better solution I believe for tip1 Pin
aSarafian28-Jun-08 10:00
aSarafian28-Jun-08 10:00 
GeneralRe: A better solution I believe for tip1 Pin
Sacha Barber28-Jun-08 20:14
Sacha Barber28-Jun-08 20:14 
Generaladd a service reference using the "tcp" host address Pin
Benoît Dion27-Jun-08 3:12
Benoît Dion27-Jun-08 3:12 
Well, it is possible.

Simply add the following meta data end point

<endpoint address="mex" binding="mexTcpBinding" contract="IMetadataExchange" />

instead of the mexHttpBinding one.

Nice article though.

Benoît Dion

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.