Praveen Katiyar' Blog

Learning along the way . . . .

Understanding Contracts in WCF

Introduction

Contracts in WCF, provide interoperability they need to communicate with the client. In this article i am going to explain the type of contracts, and how these contracts can be used in a real life application. in this article i have tried to explain all these things, using a simple example, in this example i have taken a real world cuboid, which has 3 properties (length, width and height), our service calculates volume and total surface area of the cuboid. demonstrating both way, using message contract and data contract. 

Background

it is contracts that that client and service agree as to the type of operation and structure they will use during communication. it is a formal agreement between a client and service to define a platform-neutral and standard way of describing what the service does. WCF defines four types of contracts

  to defines four types of contracts.

  • Service Contract
  • Data Contract
  • Message Contract
  • Fault Contract

Service Contract

Service contract describes the operations, or methods, that are available on the service endpoint, and exposed to the outside world. A Service contract describes the client-callable operations (functions) exposed by the service, apart from that it also describes. 

  • location of operations, interface and methods of your service to a platform-independent description
  • message exchange patterns that the service can have with another party. might be one-way/request-reply/duplex.

To create a service contract you define an interface with related methods representative of a collection of service operations, and then decorate the interface/class with the ServiceContract Attribute to indicate it is a service contract. Methods in the interface that should be included in the service contract are decorated with the OperationContract Attribute.

[ServiceContract]
interface ICuboidService
{
    [OperationContract]
    CuboidDetail CalculateDetails2(CuboidInfo cInfo);
}

Data Contract

In one line, data contract describes the data to be exchanged. it is formal agreement between service and client, that contains information about the data they will be exchanging. the important point, which needs to be mentioned here is that the two parties don’t have to share the same data types to communicate, they only need the share the same data contracts. Data contract defines which parameter & return type will be serialized/de-serialized to and from (Binary <==> XML) in order to be transmitted between one party to another.  Data contracts can be defined by annotating a class, enumeration, or even a structure, but not an interface.

to define a data contract, you simply decorate a class or enumeration with [DataContract] attribute, as shown below.

[DataContract]
public class CuboidInfo
{
}

Message Contract

WCF uses SOAP message for communication. Most of the time developer concentrates more on developing the DataContract, Serializing the data, etc. Some time developer will also require control over the SOAP message format. In that case WCF provides Message Contract to customize the message as per requirement.

A Message Contract is used to control the structure of a message body and serialization process. It is also used to send / access information in SOAP headers. By default WCF takes care of creating SOAP messages according to service DataContracts and OperationContracts.

image

So when you need full control on SOAP messages structure and how serialization happens specially when your service needs to be interoperable for consuming by different types of clients or service needs to provide extra layer of security on messages and message parts, A message is nothing but a packet and WCF uses this packet to transfer information from source to destination. This message contains an envelope, header and body. There are some rules, which one needs to follow, while working with  message contract.

  1. When using Message contract type as parameter, Only one parameter can be used in Operation.
  2. Service operation either should return MessageContract type or it should not return any value.
  3. Operation will accept and return only message contract type. Other data types are not allowed.

Choosing between DataContract and MessageContract.

90 % of the time, DataContract will be sufficient, You’ll only need message contracts if you need to very closely and very specifically control the layout of your SOAP messages. In most of the time,  you don’t need to.

A message contract allows you to specifically say which elements (scalar types or compound types as DataContracts) will be in the SOAP header, and which will be in the SOAP body.

You might need this if you have a communication partner, with whom you have agreed to a very specific format and you have to tweak your SOAP messages to match that given layout exactly. That’s just about the only valid scenario when you’ll need to and should use message contracts.

However, sometimes complete control over the structure of a SOAP message is just as important as control over its contents. This is especially true when interoperability is important or to specifically control security issues at the level of the message or message part. In these cases, you can create a message contract that enables you to use a type for a parameter or return value that serializes directly into the precise SOAP message that you need.

So, to making the long story short: always use data contracts, practically never use message contracts (unless you really have to).

Fault Contract

Fault Contract provides documented view for error accorded in the service to client. This help as to easy identity the what error has occurred, and where. By default when we throw any exception from service, it will not reach the client side. The less the client knows about what happened on the server side, the more dissociated the interaction will be, this phenomenon (not allowing the actual cause of error to reach client). is known as error masking. By default all exceptions thrown on the service side always reach the client as FaultException, as by having all service exceptions indistinguishable from one another, WCF decouples the client from service.

While the default policy of error-masking is best practice of WCF, but there are times when client is required to respond to these exceptions in a prescribed and meaningful way. WCF provides the option to handle and convey the error message to client from service using SOAP Fault contract.  SOAP faults are based on industry standard that is independent of any technology specific exceptions, it is a way to map technology specific exceptions to some neutral error information. So when service meets as unexpected error, instead of throwing a raw CLR exception (technology specific), service can throw an instance of the FaultException <T> class.    

Using the code

This article has been divided into 4 modules:

  • WCF Service Library (ContractLib.dll): Actual Service logic, which defines and implements the service, and exposes few functions to the world to use them
  • Console based Host Application to host the WCF Service Library (ContractLibHost.exe): Host the WCF library
  • Console Client Application (ContractClient.exe): Client Application which will use this service
  • Web Client  (ContractWebClient) Web site which will use this service

First Module: WCF Service Library (ContractLib.dll)

To create this project, you can simply take a "Class Library" project, while choosing from project wizard option. let’s name it "ContractLib", it is the actual service which implements the business logic. The project already contains a file Class1.cs, let us do some house keeping, before we write any code for the service library  

Little House Keeping

  • Delete the file Class1.cs from the project workspace.
  • Add a new Interface named ICuboidService to the project, a new file ICuboidService.cs will be added to the project.
  • Add a new Class named CuboidService, to the project. that will implement the ICuboidService interface, a new file CuboidService.cs will be added to the project.

Defining Service Interface – Data Contract(s)

Definition of CuboidFaulltException

[DataContract]
public class CuboidFaultException
{
      private string _reason;
      private string _source;
      private string _detail;
      private string _helpLink;

      [DataMember]
      public string Reason
      {
          get { return _reason; }
          set { _reason = value; }
      }

      [DataMember]
      public string Source
      {
          get { return _source; }
          set { _source = value; }
      }

      [DataMember]
      public string Detail
      {
          get { return _detail; }
          set { _detail = value; }
      }

        [DataMember]
        public string HelpLink
        {
            get { return _helpLink; }
            set { _helpLink = value; }
        }

}

Explanation

This DataContract defines the data the data to be exchanged when a CuboidFaultException is thrown from service. As the service is also implementing Fault contract, this information is sent to client from the service. I have defined 4 fields, to send the error information to the client, you can define more fields you you need to to send some other contextual information to client.

Reason: The actual cause of the exception, why the service has thrown an CuboidFaultException.

Source : The source information, which has thrown the exception, in this field you can Send the name of the function, or line number where exception occurred, or what ever information you think will fit as source field of the error.

Detail: here you can pack the information regarding, the actual detail of the error.     

HelpLink: a web link about the documentation about this kind of error, any thing which you can think can be a help.

Definition of CuboidDimension

  [DataContract]
  public class CuboidDimension
  {
      [DataMember]    public int Length;
      [DataMember]    public int Width;
      [DataMember]    public int Height;
  }

Explanation

This Data contract simply defines the 3 dimensions of a cuboid, which is length, width and height.

Definition of CuboidInfo

  [DataContract]
  public class CuboidInfo
  {
      [DataMember] public int ID;
      [DataMember] public CuboidDimension Dimension = new CuboidDimension();
  }

Explanation

This Data contract simply defines a class which contains two fields, first field is the ID of the Cuboid and second field is an object of CuboidDimension class, which was defined previously, that contains the dimensions (length, width and height) of the cuboid. 

Definition of CuboidDetail

  [DataContract]
  public class CuboidDetail
  {
      [DataMember]        public int ID;
      [DataMember]        public double SurfaceArea;
      [DataMember]        public double Volume;
  }

Explanation

This Data contract simply defines a class which contains 3  fields, first field is the ID of the Cuboid and second field is a double, that will hold the value of surface area of the Cuboid, and the 3rd field is a double that will contain the Volume of the cuboid. 

Defining Service Interface – Message Contract

Defining CuboidInfoRequest

  [MessageContract]
  public class CuboidInfoRequest
  {
      private int m_nID ;
      private CuboidDimension m_Dims = new CuboidDimension();

      [MessageHeader]
      public int ID
      {
          get { return m_nID; }
          set { m_nID = value; }
      }

      [MessageBodyMember]
      public CuboidDimension Dimension
      {
          get { return m_Dims; }
          set { m_Dims = value; }
      }
  }

Explanation

This Message contract simply defines a class which contains two fields, first field is the ID of the Cuboid, which has been defined as Message Header. In the message body contains a single member CuboidDimension, that is details (dimensions) of the cuboid.

Defining CuboidDetailResponse

  [MessageContract]
  public class CuboidDetailResponse
  {
      private int m_nID;
      private double m_dblArea;
      private double m_dblVolume;

      [MessageHeader]
      public int ID
      {
          get { return m_nID; }
          set { m_nID = value; }
      }

      [MessageBodyMember]
      public double SurfaceArea
      {
          get { return m_dblArea ; }
          set { m_dblArea = value ; }
      }

      [MessageBodyMember]
      public double Volume
      {
          get { return m_dblVolume; }
          set { m_dblVolume = value; }
      }
  }

Explanation

This Message contract defines a class which contains 3  fields, ID of the cuboid has been defined as Message Header. Message body contains a two members, that is the Volume and Area of the Cuboid.

Defining Service Interface – Service Contract, Operation Contract, Fault Contract

Defining ServiceContract ICuboidService

[ServiceContract]
interface ICuboidService
{
    [OperationContract]
    [FaultContract(typeof(CuboidFaultException))]
    CuboidDetailResponse CalculateDetails1(CuboidInfoRequest cInfo);
   
    [OperationContract]
    [FaultContract(typeof(CuboidFaultException))]
    CuboidDetail CalculateDetails2(CuboidInfo cInfo);
}

Explanation

Service defines two methods as Exposed Operation(s) of the service.

The first method CalculateDetails1 of the service takes a Message Contract type CuboidInfoRequest  as an input parameter, and returns a Message Contract type CuboidDetailResponse , abiding by the rules.

    1. When using Message contract type as parameter, Only one parameter can be used in Operation.
    2. Service operation either should return MessageContract type or it should not return any value.
    3. Operation will accept and return only message contract type. Other data types are not allowed.

implements a FaultContract, which raises CuboidFaultException, if there is something not the way, as it supposed to be.

The second method CalculateDetails2 of the service takes a Data Contract type CuboidInfo  as an input parameter, and returns a Data Contract type CuboidDetail. although method takes only one parameter.

implements a FaultContract, which raises CuboidFaultException, if there is something not the way, as it supposed to be.

Implementing Service Interface

Implementing method CalculateDetails1

public CuboidDetailResponse CalculateDetails1(CuboidInfoRequest cInfo)
{
    if ((cInfo.Dimension.Length<=0)||(cInfo.Dimension.Width<=0)||(cInfo.Dimension.Height <= 0))
    {
        CuboidFaultException faultEx = new CuboidFaultException ();
        faultEx.Reason = "Any dimension of Cuboid (length/width/height) can not be zero or negative value";
        faultEx.Source = "CalculateDetails1()";
        faultEx.HelpLink = "
https://praveenkatiyar.wordpress.com";
        StringBuilder sbDetail = new StringBuilder ("");
        if (cInfo.Dimension.Length <= 0) sbDetail.Append ( "[Length is <= 0] ");
        if (cInfo.Dimension.Width <= 0) sbDetail.Append ( "[Width is <= 0] ");
        if (cInfo.Dimension.Height <= 0) sbDetail.Append ( "[Height is <= 0]") ;
        faultEx.Detail = sbDetail.ToString();
        throw new FaultException<CuboidFaultException>(faultEx);
    }

    CuboidDetailResponse dInfo = new CuboidDetailResponse();
    dInfo.ID = cInfo.ID ;
    dInfo.Volume = (cInfo.Dimension.Length * cInfo.Dimension.Width * cInfo.Dimension.Height);
    dInfo.SurfaceArea = 2 * ( (cInfo.Dimension.Length * cInfo.Dimension.Width) + 

                              (cInfo.Dimension.Width * cInfo.Dimension.Height) + 

                              (cInfo.Dimension.Length * cInfo.Dimension.Height ));
    return dInfo;
}

Explanation

Operation takes a MessageContrat (CuboidInfoRequest) type parameter, checks whether the attributes (length, width, height) of the cuboid is not zero or negative. if yes then simply creates and raises a FaultException of type CuboidFaultExeception, assigning the values for the field of CuboidFaultExeception.

and if everything is in place, calculates the desired values, creates a new MessageContract object (CuboidDetailResponse) and assigns its field and returns that object.

Implementing method CalculateDetails2

public CuboidDetail CalculateDetails2(CuboidInfo cInfo)
{
    if ((cInfo.Dimension.Length<=0)||(cInfo.Dimension.Width<=0)||(cInfo.Dimension.Height <= 0))
    {
        CuboidFaultException faultEx = new CuboidFaultException ();
        faultEx.Reason = "Any dimension of Cuboid (length/width/height) can not be zero or negative value";
        faultEx.Source = "CalculateDetails2()";
        faultEx.HelpLink = "
https://praveenkatiyar.wordpress.com";
        StringBuilder sbDetail = new StringBuilder ("");
        if (cInfo.Dimension.Length <= 0) sbDetail.Append ( "[Length is <= 0] ");
        if (cInfo.Dimension.Width <= 0) sbDetail.Append ( "[Width is <= 0] ");
        if (cInfo.Dimension.Height <= 0) sbDetail.Append ( "[Height is <= 0]") ;
        faultEx.Detail = sbDetail.ToString();
        throw new FaultException<CuboidFaultException>(faultEx);
    }

    CuboidDetail dInfo = new CuboidDetail();
            dInfo.ID = cInfo.ID;
            dInfo.Volume = (cInfo.Dimension.Length * cInfo.Dimension.Width * cInfo.Dimension.Height);
            dInfo.SurfaceArea = 2 * ((cInfo.Dimension.Length * cInfo.Dimension.Width) +    

                                     (cInfo.Dimension.Width * cInfo.Dimension.Height) + 

                                     (cInfo.Dimension.Length * cInfo.Dimension.Height));
    return dInfo;
}

Explanation

Operation takes a DataContrat (CuboidInfo) type parameter, checks whether the attributes (length, width, height) of the cuboid is not zero or negative. if yes then simply creates and raises a FaultException of type CuboidFaultExeception, assigning the values for the field of CuboidFaultExeception.

and if everything is in place, calculates the desired values, creates a new DataContract object (CuboidDetail) and assigns its field and returns that object.

build the project (library), this complete the first module

Second Module: Application to Host the WCF Service Library (ContractLibHost.exe)

To create this project, you can simply take a "Console Application" project, while choosing from project wizard option. let’s name it "ContractLibHost", this application will self host the service. let us do some house keeping, before we write any code for the host application.

Adding application configuration

  • Add an Application configuration (App.config) file to the project.

Defining configuration

before we write any code for the host, let’s define the configuration.

Assumption

The Host application will expose the following endpoints for the service.

  • CuboidService will expose HTTP endpoint at Port 9011
  • Corresponding mex End Point (IMetadatExchange) for the HTTP end point.
Defining configuration for CuboidService
Defining Endpoints

<service name="ContractLib.CuboidService" behaviorConfiguration="CuboidServiceBehavior">

      <host>
          <baseAddresses>
            <add baseAddress="http://localhost:9011/CuboidService"/>
          </baseAddresses>
        </host>

         <endpoint address="http://localhost:9011/CuboidService" binding="wsHttpBinding" contract="ContractLib.ICuboidService"/>
         <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
     </service>

Explanation

The service defines, one endpoint with wsHttpBinding,  another endpoint is defined as mex, are defined for publication of metadata, IMetadataExchange is standard contract for mex endpoints.

    Defining Behavior

    <behaviors>
         <serviceBehaviors>
           <!– CalcService Behavior –>
           <behavior name="CuboidServiceBehavior">
             <serviceMetadata httpGetEnabled="true"/>
             <serviceDebug includeExceptionDetailInFaults="true "/>
           </behavior>
         </serviceBehaviors>
    </behaviors>

    Explanation

    service behaviour defines two attributes,  

    <serviceMetadata httpGetEnabled="true" /> 

    Gets or sets a value that indicates whether to publich service medtadata for retrieval using an HTTP/GET request, true means yes, metadata is available for retrieval using a HTTP/GET request.

    <serviceDebug includeExceptionDetailInFaults="true "/>

    Set IncludeExceptionDetailsInFaults to true to enable clients to obtain information about internal service method exceptions; it is only recommended as a way of temporarily debugging a service application. this property must be set to false on production servers.

    Hosting Service

    As we have defined the configuration for service, before we write any code

    • add reference of System.ServiceModel to the project.

    Let’s write code to host the service.

    // Listing of Program.cs

    using System;

    using System.ServiceModel;

    using System.Text;

    namespace ContractLibHost

    {

        class Program

        {

            static void Main(string[] args)

            {

                try

                {

                    ServiceHost host = new ServiceHost(typeof(ContractLib.CuboidService));

                    host.Open();

                    Console.WriteLine("Service is Hosted as http://localhost:9011/CuboidService");

                    Console.WriteLine("\nPress  key to stop the service.");

                    Console.ReadKey();

                    host.Close();

                }

                catch (Exception eX)

                {

                    Console.WriteLine("There was en error while Hosting Service [" + eX.Message + "]");

                    Console.WriteLine("\nPress  key to close.");

                    Console.ReadKey();

                }

            }

        }

    }

    Explanation

    ServiceHost class provides a host for services.

    Build and Execute the Host.

    Open a command prompt in administrator mode and execute the host. here is the output.

    image

    Creating Client (ContractClient.exe)

    Creating the base project

    Take a console based application. name it ContractClient.

    Generating proxies

    while the host application is running, right click on the client application project, click on

    References –> Add Service Reference

    in the address bar type the address of the mex endpoint address of CuboidService as shown below.

    image

    this will

    • Add Service References Node in the project workspace, and add an item CuboidServiceReference in that node.
    • add app.config files to your client project.

    before we write any code, let’s examine the app.config file.

    <?xml version="1.0" encoding="utf-8"?>
    <configuration>
        <system.serviceModel>
            <bindings>
    		. . . .
            </bindings>
            <client>
    		. . . .
            </client>
        </system.serviceModel>
    </configuration>

     

    The binding section holds the collection of standard and custom bindings. Each entry is a binding element that can be identified by its unique name.

    <bindings>

        <wsHttpBinding>

            <binding name="WSHttpBinding_ICuboidService" />

        </wsHttpBinding>

    </bindings>

    The client section contains a list of endpoints a client uses to connect to a service.

    <client>

        <endpoint address="
    http://localhost:9011/CuboidService" binding="wsHttpBinding"

                   bindingConfiguration="WSHttpBinding_ICuboidService" contract="CuboidServiceReference.ICuboidService"

                   name="WSHttpBinding_ICuboidService">

            <identity>

                       <userPrincipalName value="PRAVEEN-WIN7\BriskTech" />

            </identity>

        </endpoint>

    </client>

    The client section contains a list of endpoints a client uses to connect to a service.

    Note: Identity might be different on your machine.

    Calling the service, using the Proxy

    Open programs.cs file of the client project and write code to use the service, using the generated proxy class, as shown below:

    Listing of Client Program (Program.cs):

    using System;

    using System.Text;

    using System.ServiceModel ;

    namespace ContractClient

    {

        class Program

        {

            static void Main(string[] args)

            {

                int nID = 0;

                int nLength = 0;

                int nWidth = 0;

                int nHeight = 0;

               
                Console.WriteLine();

                Console.Write("Enter ID : ");

                string strID = Console.ReadLine();

                bool bID = int.TryParse(strID, out nID);

               
                Console.WriteLine();

                Console.Write("Enter Length : ");

                string strLength = Console.ReadLine();

                bool bLength = int.TryParse(strLength, out nLength);

                Console.WriteLine();

                Console.Write("Enter Width : ");

                string strWidth = Console.ReadLine();

                bool bWidth = int.TryParse(strWidth, out nWidth);

               
                Console.WriteLine();

                Console.Write("Enter Height : ");

                string strHeight = Console.ReadLine();

                bool bHeight = int.TryParse(strHeight, out nHeight);

                bool bValid = ((bID) && (bWidth) && (bLength) && (bHeight));

                if (bValid)

                {

                    CuboidServiceReference.CuboidServiceClient objClient = null;

                    try

                    {

                        objClient = new CuboidServiceReference.CuboidServiceClient();

                    }

                    catch (Exception eX)

                    {

                        objClient = null;

                        Console.WriteLine("\nFailed while creating Service proxy …..");

                        return;

                    }

                  
                    //  Issue Request

                    try

                    {

                        CuboidServiceReference.CuboidInfoRequest msgRequest = new CuboidServiceReference.CuboidInfoRequest();

                        msgRequest. Dimension = new CuboidServiceReference.CuboidDimension();

                        msgRequest.ID = nID;

                        msgRequest.Dimension.Length = nLength;

                        msgRequest.Dimension.Width = nWidth;

                        msgRequest.Dimension.Height = nHeight;

                        Console.WriteLine("\nMessage Request ");

                        Console.WriteLine("Cube ID …………. : {0:D2}", msgRequest.ID);

                        Console.WriteLine("Cube Length ……… : {0:F2}", msgRequest.Dimension.Length);

                        Console.WriteLine("Cube Width ………. : {0:F2}", msgRequest.Dimension.Width);

                        Console.WriteLine("Cube Height ……… : {0:F2}", msgRequest.Dimension.Height);

                        CuboidServiceReference.CuboidDetailResponse msgResponse = new CuboidServiceReference.CuboidDetailResponse();

                        msgResponse.SurfaceArea = objClient.CalculateDetails1 ( ref msgRequest.ID, msgRequest.Dimension, out msgResponse.Volume  );

                        Console.WriteLine("\nMessage Response");

                        Console.WriteLine("Total Surface Area .. : {0:F2}", msgResponse.SurfaceArea);

                        Console.WriteLine("Total Volume …….. : {0:F2}", msgResponse.Volume);

                    }

                    catch (FaultException<CuboidServiceReference.CuboidFaultException> eX)

                    {

                        Console.WriteLine("\nThere was an error while fetching details . . .");

                        Console.WriteLine("Reason ………….. : " + eX.Detail.Reason);

                        Console.WriteLine("Source ………….. : " + eX.Detail.Source);

                        Console.WriteLine("Details …………. : " + eX.Detail.Detail);

                        Console.WriteLine("Help Link ……….. : " + eX.Detail.HelpLink);

                    }


                    //  Issue Request

                    try

                    {

                        CuboidServiceReference.CuboidInfo dataRequest = new CuboidServiceReference.CuboidInfo();

                        dataRequest.Dimension = new CuboidServiceReference.CuboidDimension();

                        dataRequest.ID = nID;

                        dataRequest.Dimension.Length = nLength;

                        dataRequest.Dimension.Width = nWidth;

                        dataRequest.Dimension.Height = nHeight;

                        Console.WriteLine("\nData Request ");

                        Console.WriteLine("Cube ID …………. : {0:D2}", dataRequest.ID);

                        Console.WriteLine("Cube Length ……… : {0:F2}", dataRequest.Dimension.Length);

                        Console.WriteLine("Cube Width ………. : {0:F2}", dataRequest.Dimension.Width);

                        Console.WriteLine("Cube Height ……… : {0:F2}", dataRequest.Dimension.Height);

                        CuboidServiceReference.CuboidDetail dataResponse = new CuboidServiceReference.CuboidDetail();

                        dataResponse = objClient.CalculateDetails2(dataRequest);

                        Console.WriteLine("\nData Response");

                        Console.WriteLine("Total Surface Area .. : {0:F2}", dataResponse.SurfaceArea);

                        Console.WriteLine("Total Volume …….. : {0:F2}", dataResponse.Volume);

                    }

                    catch (FaultException<CuboidServiceReference.CuboidFaultException> eX)

                    {

                        Console.WriteLine("\nThere was an error while fetching details . . .");

                        Console.WriteLine("Reason ………….. : " + eX.Detail.Reason);

                        Console.WriteLine("Source ………….. : " + eX.Detail.Source);

                        Console.WriteLine("Details …………. : " + eX.Detail.Detail);

                        Console.WriteLine("Help Link ……….. : " + eX.Detail.HelpLink);

                    }

                }

                else

                {

                    if (bID == false) Console.WriteLine("ID should be a positive  numeric value");

                    if (bLength == false) Console.WriteLine("Length should be a positive numeric value");

                    if (bWidth == false) Console.WriteLine("Width should be a positive numeric value");

                    if (bHeight == false) Console.WriteLine("Height should be a positive numeric value");

                }

                Console.WriteLine("\nPress any key to close . . . ");

                Console.ReadKey();

            }

        }

    }

    Explanation

    Creates the proxy object for the service

    CuboidServiceReference.CuboidInfoRequest msgRequest = new CuboidServiceReference.CuboidInfoRequest();

    Prompt user to enter the ID and dimensions of the cuboid in interactive way, and validate, that he has entered a numeric value as well.

    Console.Write("Enter ID : ");

    string strID = Console.ReadLine();

    bool bID = int.TryParse(strID, out nID);

               
    Console.WriteLine();

    Console.Write("Enter Length : ");

    string strLength = Console.ReadLine();

    bool bLength = int.TryParse(strLength, out nLength);

    Console.WriteLine();

    Console.Write("Enter Width : ");

    string strWidth = Console.ReadLine();

    bool bWidth = int.TryParse(strWidth, out nWidth);

               
    Console.WriteLine();

    Console.Write("Enter Height : ");

    string strHeight = Console.ReadLine();

    bool bHeight = int.TryParse(strHeight, out nHeight);

    check if all values are numeric, (0 and negative values are allowed).

    bool bValid = ((bID) && (bWidth) && (bLength) && (bHeight));

    Create a proxy object for the service.

    CuboidServiceReference.CuboidServiceClient objClient = null;

    try

    {

        objClient = new CuboidServiceReference.CuboidServiceClient();

    }

    catch (Exception eX)

    {

        objClient = null;

        Console.WriteLine("\nFailed while creating Service proxy …..");

        return;

    }

    if object has been created successfully.

    put everything inside a try block .

    try

    {

    Create  Request Message

    CuboidServiceReference.CuboidInfoRequest msgRequest=new CuboidServiceReference.CuboidInfoRequest(); 

    Create a dimension data (Width, Length, and Height

    msgRequest. Dimension = new CuboidServiceReference.CuboidDimension();

    Assign values message request,  or prepare input message to be sent.

    msgRequest.ID = nID; 
    msgRequest.Dimension.Length = nLength; 
    msgRequest.Dimension.Width = nWidth; 
    msgRequest.Dimension.Height = nHeight;

    Display what is being sent.

    Console.WriteLine("\nMessage Request ");

    Console.WriteLine("Cube ID …………. : {0:D2}", msgRequest.ID);

    Console.WriteLine("Cube Length ……… : {0:F2}", msgRequest.Dimension.Length);

    Console.WriteLine("Cube Width ………. : {0:F2}", msgRequest.Dimension.Width);

    Console.WriteLine("Cube Height ……… : {0:F2}", msgRequest.Dimension.Height);

    Create a Response object to hold the returned reponse from service.

    CuboidServiceReference.CuboidDetailResponse msgResponse=new CuboidServiceReference.CuboidDetailResponse();

    Call the service, with the message request, and collect the message response. 

    msgResponse.SurfaceArea = objClient.CalculateDetails1 ( ref msgRequest.ID, msgRequest.Dimension, out   msgResponse.Volume  );

    Display output.

    Console.WriteLine("\nMessage Response");

    Console.WriteLine("Total Surface Area .. : {0:F2}", msgResponse.SurfaceArea);

    Console.WriteLine("Total Volume …….. : {0:F2}", msgResponse.Volume);

    }

    Something went wrong, catch the CuboidFaultExcepton

    catch (FaultException<CuboidServiceReference.CuboidFaultException> eX)

    {

    Display the custom values returned by implemented FaultContract from the service, why service has thrown CuboidFaultException.

        Console.WriteLine("\nThere was an error while fetching details . . .");

        Console.WriteLine("Reason ………….. : " + eX.Detail.Reason);

        Console.WriteLine("Source ………….. : " + eX.Detail.Source);

        Console.WriteLine("Details …………. : " + eX.Detail.Detail);

        Console.WriteLine("Help Link ……….. : " + eX.Detail.HelpLink);

    }

    Call Service again, this time with the object of Data Contract

    //  Issue Request

    try

    {

    Create the object for data to be sent to the service.

    CuboidServiceReference.CuboidInfo dataRequest = new CuboidServiceReference.CuboidInfo();

    dataRequest.Dimension = new CuboidServiceReference.CuboidDimension();

    Assign values.

    dataRequest.ID = nID;

    dataRequest.Dimension.Length = nLength;

    dataRequest.Dimension.Width = nWidth;

    dataRequest.Dimension.Height = nHeight;

    Display what is being sent.

    Console.WriteLine("\nData Request ");

    Console.WriteLine("Cube ID …………. : {0:D2}", dataRequest.ID);

    Console.WriteLine("Cube Length ……… : {0:F2}", dataRequest.Dimension.Length);

    Console.WriteLine("Cube Width ………. : {0:F2}", dataRequest.Dimension.Width);

    Console.WriteLine("Cube Height ……… : {0:F2}", dataRequest.Dimension.Height);

    Create object which will hold the returned value.

    CuboidServiceReference.CuboidDetail dataResponse = new CuboidServiceReference.CuboidDetail();

    Call the service and save the returned value.

    dataResponse = objClient.CalculateDetails2(dataRequest);

    Display output, the values inside, returned object

    Console.WriteLine("\nData Response");

    Console.WriteLine("Total Surface Area .. : {0:F2}", dataResponse.SurfaceArea);

    Console.WriteLine("Total Volume …….. : {0:F2}", dataResponse.Volume);

    }

    If some went wrong, catch the CuboidFaultException.

    catch (FaultException<CuboidServiceReference.CuboidFaultException> eX)

    {

    Display the custom values returned by implemented FaultContract from service, why service has thrown CuboidFaultException. Console.WriteLine("\nThere was an error while fetching details . . .");

    Console.WriteLine("Reason ………….. : " + eX.Detail.Reason);

    Console.WriteLine("Source ………….. : " + eX.Detail.Source);

    Console.WriteLine("Details …………. : " + eX.Detail.Detail);

    Console.WriteLine("Help Link ……….. : " + eX.Detail.HelpLink);

    }

    Output

    and here is the output  with difference conditions.

    Output when you provide all valid inputs.

    image

    Output when you one or more invalid input.

    image

    image

    Creating Web Client

    Creating the base project

    Create a web site.

    Generating proxies

    while the host application is running, right click on the client application project, right click on the website

    Add Service Reference

    in the address bar type the address of the mex endpoint address of CuboidService as shown below.

    image

    Designing UI for web client

    Add some controls to the page as shown below.

    image

    Input

     

    ID Type Text
    lblID Label Label
    txtID TextBox  
    lblLength Label Length :
    txtLength TextBox  
    lblWidth Label Width :
    txtWidth TextBox  
    lblHeight Label Height :
    txtHeight TextBox  
    lblCap1 Label Calling using Message Contract
    lblStatus1 Label  
    lblArea1 Label  
    lblVolume1 Label  
    lblCap2 Label Calling using Data Contract
    lblStatus2 Label  
    lblArea2 Label  
    lblVolume2 Label  
    btnCalc Button Call Service

    Calling Service.

    Service is called, when the Call Service button is clicked. handler for Click event of btnCalc is given below.

    protected void btnCalc_Click(object sender, EventArgs e)

    {

         int nID = 0;

         bool bID = int.TryParse ( txtID.Text,  out nID);

         int nLength = 0;

         bool bLength = int.TryParse ( txtLength.Text,  out nLength );

          
           int  nWidth = 0 ;

           bool bWidth = int.TryParse ( txtWidth.Text,  out nWidth );

          
           int nHeight = 0 ;

           bool bHeight= int .TryParse ( txtHeight.Text,  out nHeight);

           bool bValid = ((bID) && (bWidth) && (bLength) && (bHeight));

           StringBuilder sbTmp= new StringBuilder () ;

           if ( bValid )

           {

               Session["ID"] = nID.ToString () ;

               Session["Length"] = nLength .ToString ();

               Session["Width"] = nWidth .ToString ();

               Session["Height"] = nHeight.ToString () ;

               bool bObjCreated = true ;

               CuboidServiceReference.CuboidServiceClient objClient = null ;

               try

               {

                       objClient = new CuboidServiceReference.CuboidServiceClient();

               }

               catch ( Exception eX)

               {

                   objClient = null ;

                   lblVolume2.Text = "<Font color=#ff0000>Failed while creating Service proxy ….. </font>" ;

                   bObjCreated = false ;

               }

               if ( bObjCreated == false ) return ;

              
               //  Issue Request 1

               try

               {

                   CuboidServiceReference.CuboidInfoRequest msgRequest = new CuboidServiceReference.CuboidInfoRequest ();

                   msgRequest.Dimension = new CuboidServiceReference.CuboidDimension();

                  
                   msgRequest.ID = nID ;

                   msgRequest.Dimension.Length = nLength ;

                   msgRequest.Dimension.Width = nWidth ;

                   msgRequest.Dimension.Height = nHeight ;

                  
                   
                   CuboidServiceReference.CuboidDetailResponse msgResponse = new CuboidServiceReference.CuboidDetailResponse ();

                   msgResponse.SurfaceArea = objClient.CalculateDetails1 ( ref msgRequest.ID, msgRequest.Dimension, out msgResponse.Volume );

                   lblCap1.Text = "<font Color=#0000ff>Calling using Message Contract </font>";

                   lblStatus1.Text = "<font Color=#008000>Status : Success </font>";

                   lblArea1.Text = "<Font color=#000080>Total Surface Area .. : " +  msgResponse.SurfaceArea.ToString ("F2") + "</font>" ;

                   lblVolume1.Text = "<Font color=#000080>Total Volume .. : " +  msgResponse.Volume.ToString ("F2") + "</font>" ;

               }

               catch (FaultException<CuboidServiceReference.CuboidFaultException> eX)

               {

                   StringBuilder sbErr = new StringBuilder ("<font Color=#ff0000>Status : Error ");

                   sbErr .Append ( "<br>Reason   : " ); sbErr.Append ( eX.Detail.Reason );

                   sbErr .Append ( "<br>Source   : " ); sbErr.Append ( eX.Detail.Source );

                   sbErr .Append ( "<br>Details  : " ); sbErr.Append ( eX.Detail.Detail  );

                   sbErr .Append ( "<br>HelpLink : " ); sbErr.Append ( eX.Detail.HelpLink );

                   sbErr .Append ( "</Font>");

                   lblCap2.Text = "<font Color=#0000ff>Calling using Message Contract </font>";

                   lblStatus1.Text = sbErr.ToString(); ;

                   lblArea1.Text = "" ;

                   lblVolume1.Text = "";

               }

               //  Issue Request 2

               try

               {

                   CuboidServiceReference.CuboidInfo dataRequest = new CuboidServiceReference.CuboidInfo();

                   dataRequest.Dimension = new CuboidServiceReference.CuboidDimension();

                   dataRequest.ID = nID;

                   dataRequest.Dimension.Length = nLength;

                   dataRequest.Dimension.Width = nWidth;

                   dataRequest.Dimension.Height = nHeight;


                   CuboidServiceReference.CuboidDetail dataResponse = new CuboidServiceReference.CuboidDetail();

                   dataResponse = objClient.CalculateDetails2(dataRequest);

                   lblCap2.Text = "<font Color=#0000ff>Calling using Data Contract </font>";

                   lblStatus2.Text = "<font Color=#008000>Status : Success </font>";

                   lblArea2.Text = "<Font color=#000080>Total Surface Area .. : " + dataResponse.SurfaceArea.ToString("F2") + "</font>";

                   lblVolume2.Text = "<Font color=#000080>Total Volume .. : " + dataResponse.Volume.ToString("F2") + "</font>";

               }

               catch (FaultException<CuboidServiceReference.CuboidFaultException> eX)

               {

                   StringBuilder sbErr = new StringBuilder("<font Color=#ff0000>Status : Error ");

                   sbErr.Append("<br>Reason   : "); sbErr.Append(eX.Detail.Reason);

                   sbErr.Append("<br>Source   : "); sbErr.Append(eX.Detail.Source);

                   sbErr.Append("<br>Details  : "); sbErr.Append(eX.Detail.Detail);

                   sbErr.Append("<br>HelpLink : "); sbErr.Append(eX.Detail.HelpLink);

                   sbErr.Append("</Font>");

                   lblCap2.Text = "<font Color=#0000ff>Calling using Data Contract </font>";

                   lblStatus2.Text = sbErr.ToString();

                   lblArea2.Text = "";

                   lblVolume2.Text = "";

               }

           }

           else

           {

               sbTmp.Append ( "<Font Color=#ff0000>");

               if ( bID ) sbTmp.Append ( "<p><Font Color=#ff0000> ID should be a positive  numeric value");

               if ( bLength ) sbTmp.Append ( "<p><Font Color=#ff0000> Length should be a positive numeric value");

               if ( bWidth ) sbTmp.Append ( "<p><Font Color=#ff0000> Width should be a positive numeric value");

               if ( bHeight ) sbTmp.Append ( "<p><Font Color=#ff0000> Height should be a positive numeric value");

               sbTmp.Append ( "</Font>");

               lblVolume2.Text = sbTmp.ToString ();

           }

       }

    Parse all inputs, from their respective  text box.

    int nID = 0;

    bool bID = int.TryParse ( txtID.Text,  out nID);

    int nLength = 0;

    bool bLength = int.TryParse ( txtLength.Text,  out nLength ); 
           
    int  nWidth = 0 ;

    bool bWidth = int.TryParse ( txtWidth.Text,  out nWidth ); 
           
    int nHeight = 0 ;

    bool bHeight= int .TryParse ( txtHeight.Text,  out nHeight);

    Make sure that all inputs are valid.

    bool bValid = ((bID) && (bWidth) && (bLength) && (bHeight));

    if everything is good, store valid values to session, this is useful, to retain values, during post back

    StringBuilder sbTmp= new StringBuilder () ;

    if ( bValid )

    {

        Session["ID"] = nID.ToString () ;

        Session["Length"] = nLength .ToString ();

        Session["Width"] = nWidth .ToString ();

        Session["Height"] = nHeight.ToString () ;

    Create service proxy, display error message, it failed, and set a flag.

    bool bObjCreated = true ;

    CuboidServiceReference.CuboidServiceClient objClient = null ;

    try

    {

        objClient = new CuboidServiceReference.CuboidServiceClient();

    }

    catch ( Exception eX)

    {

        objClient = null ;

        lblVolume2.Text = "<Font color=#ff0000>Failed while creating Service proxy ….. </font>" ;

        bObjCreated = false ;

    }

    Return if failed.

    if ( bObjCreated == false ) return ;

              
    //  Issue Request 1

    //  Issue Request 2, this time using MessageContract

    try

    {

    Prepare Message Request

        CuboidInfoRequest msgRequest=new CuboidInfoRequest ();

        msgRequest.Dimension = new CuboidDimension();

    Assign values to Message Request               
        msgRequest.ID = nID ;

        msgRequest.Dimension.Length = nLength ;

        msgRequest.Dimension.Width = nWidth ;

        msgRequest.Dimension.Height = nHeight ;

                   
        CuboidDetailResponse msgResponse = new  CuboidDetailResponse ();

    Call Service and collect Message Response.

        msgResponse.SurfaceArea = objClient.CalculateDetails1 ( ref msgRequest.ID, msgRequest.Dimension, out msgResponse.Volume );

    Display output.

    lblCap1.Text = "<font Color=#0000ff>Calling using Message Contract </font>";

    lblStatus1.Text = "<font Color=#008000>Status : Success </font>";

    lblArea1.Text = "<Font color=#000080>Total Surface Area .. : " +  msgResponse.SurfaceArea.ToString ("F2") + "</font>" ;

    lblVolume1.Text = "<Font color=#000080>Total Volume .. : " +  msgResponse.Volume.ToString ("F2") + "</font>" ;

    }

    catch (FaultException<CuboidFaultException> eX)

    {

    if something was wrong, display error information, passed through FaultContract

        StringBuilder sbErr = new StringBuilder ("<font Color=#ff0000>Status : Error ");

        sbErr .Append ( "<br>Reason   : " ); sbErr.Append ( eX.Detail.Reason );

        sbErr .Append ( "<br>Source   : " ); sbErr.Append ( eX.Detail.Source );

        sbErr .Append ( "<br>Details  : " ); sbErr.Append ( eX.Detail.Detail  );

        sbErr .Append ( "<br>HelpLink : " ); sbErr.Append ( eX.Detail.HelpLink );

        sbErr .Append ( "</Font>");

        lblCap1.Text = "<font Color=#0000ff>Calling using Message Contract </font>";

        lblStatus1.Text = sbErr.ToString(); ;

        lblArea1.Text = "" ;

        lblVolume1.Text = "";

    }

    //  Issue Request 2, this time using DataContract

    try

    {

    // Prepare Data object to be passed

        CuboidInfo dataRequest = new CuboidInfo();

        dataRequest.Dimension = new CuboidDimension();

        dataRequest.ID = nID;

        dataRequest.Dimension.Length = nLength;

        dataRequest.Dimension.Width = nWidth;

        dataRequest.Dimension.Height = nHeight;

    // Collect  the returned Data object.

        CuboidDetail dataResponse = new CuboidServiceReference.CuboidDetail();

        dataResponse = objClient.CalculateDetails2(dataRequest);

    // Display Output

        lblCap2.Text = "<font Color=#0000ff>Calling using Data Contract </font>";

        lblStatus2.Text = "<font Color=#008000>Status : Success </font>";

        lblArea2.Text = "<Font color=#000080>Total Surface Area .. : " + dataResponse.SurfaceArea.ToString("F2") + "</font>";

        lblVolume2.Text = "<Font color=#000080>Total Volume .. : " + dataResponse.Volume.ToString("F2") + "</font>";

    }

    catch (FaultException<CuboidFaultException> eX)

    {

    //     if something was wrong, display error information, passed through FaultContract

        StringBuilder sbErr = new StringBuilder("<font Color=#ff0000>Status : Error ");

        sbErr.Append("<br>Reason   : "); sbErr.Append(eX.Detail.Reason);

        sbErr.Append("<br>Source   : "); sbErr.Append(eX.Detail.Source);

        sbErr.Append("<br>Details  : "); sbErr.Append(eX.Detail.Detail);

        sbErr.Append("<br>HelpLink : "); sbErr.Append(eX.Detail.HelpLink);

        sbErr.Append("</Font>");

        lblCap2.Text = "<font Color=#0000ff>Calling using Data Contract </font>";

        lblStatus2.Text = sbErr.ToString();

        lblArea2.Text = "";

        lblVolume2.Text = "";

    }

    else

    {

    // Input field validation failed, show error message(s)

        sbTmp.Append ( "<Font Color=#ff0000>");

        if ( bID ) sbTmp.Append ( "<p>ID should be a positive  numeric value");

        if ( bLength ) sbTmp.Append ( "<p>Length should be a positive numeric value");

        if ( bWidth ) sbTmp.Append ( "<p>Width should be a positive numeric value");

        if ( bHeight ) sbTmp.Append ( "<p>Height should be a positive numeric value");

        sbTmp.Append ( "</Font>");

        lblVolume2.Text = sbTmp.ToString ();

    }

    Output

    Build the web site, and execute,

    Output when you provide all valid inputs.

    image

    Output when you one or more invalid input.

    webOutput2

    image

    Advertisements

    October 10, 2013 Posted by | .NET, SOA, WCF | , , | 1 Comment