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

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

    Understanding Events in WCF

    Introduction

    Events allow the client or clients to be notified that something has occurred on the service side, An event may result from a direct client call, or may be the result of something the service monitors.

    Background

    While events in WCF are nothing more than call back operations, however by their very nature events usually imply a looser relationship between the publisher and the subscriber than a typical relationship between a client and a service. The Service firing the event is called the publisher, and the client receiving the event is called the subscriber. service typically publishes the same event to multiple subscribing clients. the publisher often does not care about the order of invocation of the subscribers, or any errors, all the publishers knows is that it should deliver the event to the subscribers.

    Since service does not care about the returned results from the subscribers. Consequently, event handling operations

    • Should have void return type. 
    • Should not have any out/ref parameters.
    • Should be marked as one way.

    Using the code

    This article has been divided into 3 modules:

    • WCF Service Library (EventsLib.dll): Actual Service logic, which defines a Service Contract Interface, OperationContract, and implements them, and exposes few functions to the world to use them
    • Console based Host Application to host the WCF Service Library EventsLibHost.exe): Host the WCF library
    • Console Client Application (EventsClient.exe): Client Application which will use this service.

    First Module: WCF Service Library (EventsLib.dll)

    To create this project, you can simply take a "Class Library" project, while choosing from project wizard option. let’s name it "EventsLib", 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 ICalcService  to the project, a new file ICalcService.cs will be added to the project.
    • Add a new Class named CalcService, to the project. that will implement the ICalcService interface, a new file CalcService.cs will be added to the project.
    • Add a new Interface named ICalcServiceEvents  to the project, a new file ICalcServiceEvents.cs will be added to the project.

    Defining Interface ICalcServiceEvents (ICalcServiceEvents.cs)

    so let’s define interface for the events published by the service.

    //  Listing of ICalcServiceEvents.cs
    using System;
    using System.Text;
    using System.ServiceModel;

    namespace EventsLib

    {

        public interface ICalcServiceEvents

        {

            [OperationContract(IsOneWay = true)]

            void Calculated(int nOp, double dblNum1, double dblNum2, double dblResult);

            [OperationContract(IsOneWay = true)]

            void CalculationFinished();

        }

    }

    Explanation

    Interface simply publishes 2 method. which are basically events for the subscriber. note that, every method in this interface has been

    • Marked as OneWay
    • Does not return any value (void)
    • Has no out/ref parameter

    First method Calculated is an event for the subscriber, it is fired, when calculation is done, it also passes the result to the subscribers. along with the operands and operation type.

    Second Method CalculationFinished is the event, it is fired, when the Calculation is finished.

     

    Defining Interface ICalcService (ICalcService.cs)

    so let’s define interface for the service.

    //  Listing of ICalcService.cs
    using System;
    using System.Text;
    using System.ServiceModel ;

    namespace EventsLib

    {

        [ServiceContract(CallbackContract = typeof(ICalcServiceEvents))]

        public interface ICalcService

        {

            [OperationContract]

            void Calculate(int nOp, double dblNum1, double dblNum2);

            [OperationContract]

            void SubscribeCalculatedEvent();

            [OperationContract]

            void SubscribeCalculationFinishedEvent();

        }

    }

    Explanation

    Interface simple defines 3 method.

    First Method Calculate is the method, that is related to business logic. that does the actual job. the other two methods are helper methods that are called by the client to subscribe a event published by the client. as you have 2 events, so you have one method for each event.

    Implementing ICalcService interface (CalcService.cs)

    //  Listing of CalcService.cs

    using System;

    using System.Text;

    using System.ServiceModel;

    namespace EventsLib

    {

        public  class CalcService : ICalcService

        {

           static Action<int, double, double, double> m_Event1 = delegate { };

           static Action m_Event2 = delegate { };

          public void SubscribeCalculatedEvent()

           {

               ICalcServiceEvents subscriber = OperationContext.Current.GetCallbackChannel<ICalcServiceEvents>();

               m_Event1 += subscriber.Calculated;

           }

           
            public void SubscribeCalculationFinishedEvent()

            {

                ICalcServiceEvents subscriber = OperationContext.Current.GetCallbackChannel<ICalcServiceEvents>();

                m_Event2 += subscriber.CalculationFinished ;

            }

                  
            public void Calculate(int nOp, double dblX, double dblY)

            {

                double dblResult = 0;

                switch (nOp)

                {

                    case 0: dblResult = dblX + dblY; break;

                    case 1: dblResult = dblX – dblY; break;

                    case 2: dblResult = dblX * dblY; break;

                    case 3: dblResult = (dblY == 0) ? 0 : dblX / dblY; break;

                }

               
                m_Event1(nOp, dblX, dblY, dblResult);

                m_Event2();

           }

        }

    }

    Explanation
    Member variables

    static Action<int, double, double, double> m_Event1 = delegate { };

    static Action m_Event2 = delegate { };

    Action keyword can be used define a delegate, can be used to pass a method as a parameter without explicitly declaring a custom delegate.

    static Action <int, double, double, double> m_Event1 = delegate { };

    m_Event1 is declared as it takes  four parameters. parameter list which is defined as

    <int, double, double, double>

    that explains its parameters list and order. the encapsulated method must correspond to the method signature that is defined by this delegate. that is first parameter is an int and the consecutive 3 are double.

    m_Event2 is declared without any parameter

    static Action m_Event2 = delegate { };

    public void SubscribeCalculatedEvent()

    public void SubscribeCalculatedEvent()

    you simple take take the reference of Call back channel (that is the reference of sink, that implements ICallbackServiceEvents, and assign the reference of Calculated method to m_Event1.

    public void SubscribeCalculationFinishedEvent()

    you simple take take the reference of Call back channel (that is the reference of sink, that implements ICallbackServiceEvents, and assign the reference of Calculated method to m_Event2.

    these so this method gives a choice, whether the client want to notified about this particular event or not.

    public void Calculate(int nOp, double dblX, double dblY)()

    This function implements the business logic, simple do the desired operation as specified by nOp parameter,

          double dblResult = 0;

        switch (nOp)

        {

            case 0: dblResult = dblX + dblY; break;

            case 1: dblResult = dblX – dblY; break;

            case 2: dblResult = dblX * dblY; break;

            case 3: dblResult = (dblY == 0) ? 0 : dblX / dblY; break;

        }

         

    after that it simply calls both the events in orderly fashion using defined delegates.

         m_Event1(nOp, dblX, dblY, dblResult);

        m_Event2();

    Second Module: WCF Service Library Host (EventsLibHost.exe)

    To create this project, you can simply take a "Console Application" project, while choosing from project wizard option. let’s name it "EventsLibHost", 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.

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

    <service name="EventsLib.CalcService" behaviorConfiguration="CalcServiceBehavior">

       <host>

          <baseAddresses>

             <add baseAddress="http://localhost:9011/CalcService"/&gt;

          </baseAddresses>

        </host>

           
        <endpoint address="" binding="wsDualHttpBinding"  contract="EventsLib.ICalcService"/>

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

    </service>

    Explanation

    The service defines, one endpoint with wsDualHttpBinding, if you notice, you will see, that the address field has been omitted, in the end point definition. you can do this, if you define a base address for the said type.  wsDualHttpBinding provides the same support for web service protocol as WSHttpBinding, but for use with duplex contracts

    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="CalcServiceBehavior">

            <serviceMetadata httpGetEnabled="true"/>

            <serviceDebug includeExceptionDetailInFaults="true "/>

          </behavior>

        </serviceBehaviors>

    </behaviors>

    Explanation

    each service behavior 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, Let’s write code to host the service.

    // Listing of Program.cs

    using System;

    using System.Text;

    using System.ServiceModel;

    namespace EventsLibHost

    {

        class Program

        {

            static void Main(string[] args)

            {

                try

                {

                    ServiceHost host = new ServiceHost(typeof(EventsLib.CalcService));

                    host.Open();

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

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

                    Console.ReadLine();

                    host.Close();

                }

                catch (Exception eX)

                {

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

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

                    Console.ReadLine();

                }

            }

        }

    }

    Build and Execute the Host.

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

    image

    Third Module: Client Application (EventsClient.exe)

    To create this project, you can simply take a "Console based Application" project, while choosing from project wizard option. let’s name it "EventsClient", 

    a new Project EventsClient will be added to the workspace.  

    Generating proxy for the CalcService

    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 CalcService as shown below.

    image

    press OK, this will add an item named CalcServiceReference under Service References Node in client’s project workspace. and will also add an configuration file (app.config) to the client.

    Declaring and Implementing the Callback Interface (Events Sink)

    Add a new Class to the client project, let say call it CalcServiceCallbackSink, this is where you will implement the event handlers, for both the events fired by the service. below is the code.

    using System;

    using System.Text;

    namespace EventLibClient

    {

        class CalcServiceCallbackSink:CalcServiceReference.ICalcServiceCallback

        {

            public void Calculated(int nOp, double dblNum1, double dblNum2, double dblResult)

            {

                switch (nOp)

                {

                    case 0: Console.WriteLine("\nOperation : Addition"); break;

                    case 1: Console.WriteLine("\nOperation : Subtraction"); break;

                    case 2: Console.WriteLine("\nOperation : Multiplication"); break;

                    case 3: Console.WriteLine("\nOperation : Division"); break;

                }

                Console.WriteLine("Operand 1 …: {0}", dblNum1);

                Console.WriteLine("Operand 2 …: {0}", dblNum2);

                Console.WriteLine("Result ……: {0}", dblResult);

            }

            public void CalculationFinished()

            {

                Console.WriteLine("Calculation completed");

            }

        }

    }

    Explanation

    this simply handles both the events fired by the service. here is where you display the output/ or whatever is intended  by that event.

    Calling Service

    and finally write the code to call the service.

    Create the Sink object and InstanceContext object.

    CalcServiceCallbackSink objsink = new CalcServiceCallbackSink ();

    InstanceContext iCntxt = new InstanceContext(objsink);

    Create the service proxy object.

    CalcServiceReference.CalcServiceClient objClient =

                   new CalcServiceReference.CalcServiceClient(iCntxt);

    Subscribe the events we desire to handle,

    objClient.SubscribeCalculatedEvent ();

    objClient.SubscribeCalculationFinishedEvent ();

    Call our methods.

                
    double dblNum1 = 1000, dblNum2 = 2000 ;

    objClient.Calculate (0, dblNum1, dblNum2);

    dblNum1 = 2000; dblNum2 = 4000;

    objClient.Calculate(1, dblNum1, dblNum2);

    dblNum1 = 2000; dblNum2 = 4000;

    objClient.Calculate(2, dblNum1, dblNum2);

    dblNum1 = 2000; dblNum2 = 400;

    objClient.Calculate(3, dblNum1, dblNum2);

    and here is the complete code for the client,

    //  Listing of Program.cs

    using System;

    using System.Text;

    using System.ServiceModel;

    namespace EventLibClient

    {

        class Program

        {

            static void Main(string[] args)

            {

                
                CalcServiceCallbackSink objsink = new CalcServiceCallbackSink ();

                InstanceContext iCntxt = new InstanceContext(objsink);

                    
                CalcServiceReference.CalcServiceClient objClient = new CalcServiceReference.CalcServiceClient(iCntxt);

                objClient.SubscribeCalculatedEvent ();

                objClient.SubscribeCalculationFinishedEvent ();

               
                double dblNum1 = 1000, dblNum2 = 2000 ;

                objClient.Calculate (0, dblNum1, dblNum2);

                dblNum1 = 2000; dblNum2 = 4000;

                objClient.Calculate(1, dblNum1, dblNum2);

                dblNum1 = 2000; dblNum2 = 4000;

                objClient.Calculate(2, dblNum1, dblNum2);

                dblNum1 = 2000; dblNum2 = 400;

                objClient.Calculate(3, dblNum1, dblNum2);

                Console.WriteLine("Press any key to close …" );

                Console.ReadKey();

            }

        }

    }

    and here is the output.

    image           

    that’s all folks.

     

    October 4, 2013 Posted by | .NET, SOA, WCF | , , | Leave a comment

    Understanding Operations in WCF

    WCF supports 3 ways for a client to call a service.

    1. Request Reply : default way of invocation, the client would issue a call, block while the call was in progress, and would continue executing once the method returned. If service doesn’t respond to the service within receiveTimeout, client will receive TimeOutException.
    2. One way calls: fire and forget type operations. that simply means client will send a request to the server and does not care whether service execution was a success or failure. There is no return value from the server side. Client will be blocked only for a moment till it dispatches its call to service. Client can continue to execute its statement, after making one-way call to the service. sometime calls are queued at the server side to be dispatched one at a time. if the number of queued messages has exceeded the queue’s capacity, client will be blocked for the moment till the message is being queued. and as soon as the call is queued, the client will be unblocked and can continue executing, while the service processes the operation in the background.
    3. Call back Operation: WCF Supports allowing a service to call back to its clients. They are specially useful, for notifying client(s) that something happened on the service side. call backs are also commonly referred as duplex operations. not all binding types support call back operations. for example HTTP (for obvious reasons of being a connectionless nature). only two commonly used binding that offer call backs are NetTcpBinding and NetNamedBinding. to offer call backs over HTTP, WCF offers the WSDualHttpBinding, which actually sets up two WS channels. 

    Now to see all these things in action let’s create a service.

    Creating the Class Library

    let’s create a service, create a new project, take a Class Library template, name it OperationLib, let’s do some house keeping with the newly created project.

    • Delete the file Class1.cs from the project workspace.
    • Add a new Interface named IRequestReplyService to the project, a new file IRequestReplyService.cs will be added to the project. 5t6
    • Add a new Class named RequestReplyService, to the project. that will implement the IRequestReplyService interface, a new file RequestReplyService.cs will be added to the project.
    • Add a new Interface named IOneWayCallService to the project, a new file IOneWayCallService.cs will be added to the project.
    • Add a new Class named OneWayCallService, to the project. that will implement the IOneWayCallService interface, a new file OneWayCallService.cs will be added to the project.
    • Add a new Interface named IDuplexService to the project, a new file IDuplexService.cs will be added to the project.
    • Add a new Class named DuplexService, to the project. that will implement the IDuplexService interface, a new file DuplexService.cs will be added to the project.

    to keep the things simple, let’s assume that each interface defines only one method, which simply adds a passed value, to  its member variable.

    Defining Interfaces

    so let’s define interface for each service.

    // Listing of IRequestReplyService.cs

    using System;
    using System.Text;
    using System.ServiceModel;

    namespace OperationLib
    {
        [ServiceContract]
        interface IRequestReplyService
        {
            [OperationContract]
            double AddValue(double dblNum1, double dblNum2);
        }
    }


    // Listing of RequestReplyService.cs

    using System;
    using System.Text;

    namespace OperationLib
    {
        public class RequestReplyService : IRequestReplyService
        {
            public double AddValue(double dblNum1, double dblNum2)
            {
                return (dblNum1 + dblNum2);
            }
        }
    }

    // Listing of IOneWayCallService.cs

    using System;
    using System.ServiceModel;
    using System.Text;

    namespace OperationLib
    {
    [ServiceContract(SessionMode = SessionMode.Required)]
       interface IOneWayCallService
       {
            [OperationContract(IsOneWay = true)]
            void AddValue(double dblNum );

            [OperationContract]
            double GetResult();
        }

    }


    // Listing of OneWayCallService.cs

    public class OneWayCallService : IOneWayCallService
        {
            private double m_dblResult = 0;

            public void AddValue(double dblVal)
            {
                m_dblResult += dblVal;
            }

            public double GetResult()
            {
                return m_dblResult;
            }
        }

    // Listing of IDuplexService.cs

    using System;
    using System.ServiceModel;
    using System.Text;

    namespace OperationLib
    {
        interface IDuplexServiceCallback
        {
            [OperationContract]
            void OnValueAdded(double dblNum1, double dblNum2, double dblResult);
        }

        [ServiceContract(CallbackContract = typeof(IDuplexServiceCallback))]
        interface IDuplexService
        {
            [OperationContract()]
            void AddValue(double dblNum1, double dblNum2);
        }
    }


    // Listing of DuplexService.cs

    using System;
    using System.ServiceModel ;
    using System.Text;

    namespace OperationLib
    {
        public class DuplexService : IDuplexService
        {
            public void AddValue(double dblNum1, double dblNum2)
            {
                double dblResult = dblNum1 + dblNum2;
                IDuplexServiceCallback callbackInstance = OperationContext.Current.GetCallbackChannel<IDuplexServiceCallback>();
                callbackInstance.OnValueAdded(dblNum1, dblNum2, dblResult);
            }
        }
    }

    Now to see all these things in action let’s create a service.

    Hosting the Service

    for the sake of simplicity, i have have hosted this service as a self hosted service, to create the Host application, create a new Console based application.

    Before we write any code for the Host application, let’s define the configuration file for the Service(s).

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

    <!–************************* Request Reply Service **************************** –>
          <service name="OperationLib.RequestReplyService" behaviorConfiguration="RequestReplyServiceBehavior">
            <host>
              <baseAddresses>
                <add baseAddress="http://localhost:9011/RequestReplyService"/&gt;
                <add baseAddress="net.tcp://localhost:9012/RequestReplyService"/>
              </baseAddresses>
            </host>
           
            <endpoint address="http://localhost:9011/RequestReplyService" binding="wsHttpBinding" contract="OperationLib.IRequestReplyService"/>
            <endpoint address="net.tcp://localhost:9012/RequestReplyService" binding="netTcpBinding" contract="OperationLib.IRequestReplyService"/>
            <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
            <endpoint address="mex" binding="mexTcpBinding" contract="IMetadataExchange"/>
          </service>

          <!—******************** OnewayCall Service ************************* –>

    <service name="OperationLib.OneWayCallService" behaviorConfiguration="OneWayCallServiceBehavior">
           <host>
             <baseAddresses>
               <add baseAddress="http://localhost:9013/OneWayCallService"/&gt;
               <add baseAddress="net.tcp://localhost:9014/OneWayCallService"/>
             </baseAddresses>
           </host>

           <endpoint address="http://localhost:9013/OneWayCallService" binding="wsHttpBinding" contract="OperationLib.IOneWayCallService"/>
           <endpoint address="net.tcp://localhost:9014/OneWayCallService" binding="netTcpBinding" contract="OperationLib.IOneWayCallService"/>
           <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
           <endpoint address="mex" binding="mexTcpBinding" contract="IMetadataExchange"/>
         </service>

         <!—********************* Duplex Service ***************************** –>

    <service name="OperationLib.DuplexService" behaviorConfiguration="DuplexServiceBehavior" >
            <host>
              <baseAddresses>
                <add baseAddress="http://localhost:9015/DuplexService"/&gt;
                <add baseAddress="net.tcp://localhost:9016/DuplexService"/>
              </baseAddresses>
            </host>

            <endpoint address="http://localhost:9015/DuplexService" binding="wsDualHttpBinding" contract="OperationLib.IDuplexService"/>
            <endpoint address="net.tcp://localhost:9016/DuplexService" binding="netTcpBinding" contract="OperationLib.IDuplexService"/>
            <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
            <endpoint address="mex" binding="mexTcpBinding" contract="IMetadataExchange"/>
          </service>

    </services>

        <!– ********************************** behaviors ********************************** –>
       

    <behaviors>
         <serviceBehaviors>

           <!– Single Call Service Behavior –>
           <behavior name="RequestReplyServiceBehavior">
             <serviceMetadata httpGetEnabled="true"/>
             <serviceDebug includeExceptionDetailInFaults="true "/>
           </behavior>

           <!–Single Session Service Behavior –>
           <behavior name="OneWayCallServiceBehavior">
             <serviceMetadata httpGetEnabled="true"/>
             <serviceDebug includeExceptionDetailInFaults="true "/>
           </behavior>

           <!–Singleton Service Behavior –>
           <behavior name="DuplexServiceBehavior" >
             <serviceMetadata httpGetEnabled="true"  />
             <serviceDebug includeExceptionDetailInFaults="true "/>
           </behavior>
          
         </serviceBehaviors>
       </behaviors>

       
      </system.serviceModel>
    </configuration>

    Now let’s explain the configuration of each service.

     

    <service name="OperationLib.RequestReplyService" behaviorConfiguration="RequestReplyServiceBehavior">
            <host>
              <baseAddresses>
               <add baseAddress="http://localhost:9011/RequestReplyService"/>
                <add baseAddress="net.tcp://localhost:9012/RequestReplyService"/>
              </baseAddresses>
            </host>
           
            <endpoint address="http://localhost:9011/RequestReplyService&quot; binding="wsHttpBinding" contract="OperationLib.IRequestReplyService"/>
            <endpoint address="net.tcp://localhost:9012/RequestReplyService" binding="netTcpBinding" contract="OperationLib.IRequestReplyService"/>
            <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
            <endpoint address="mex" binding="mexTcpBinding" contract="IMetadataExchange"/>
          </service>

    Explanation:

    >> to do

          <!– ******************** Single Session Service ************************* –>
          <service name="OperationLib.OneWayCallService" behaviorConfiguration="OneWayCallServiceBehavior">
            <host>
              <baseAddresses>
               <add baseAddress="http://localhost:9013/OneWayCallService"/&gt;
                <add baseAddress="net.tcp://localhost:9014/OneWayCallService"/>

              </baseAddresses>
            </host>

            <endpoint address="http://localhost:9013/OneWayCallService" binding="wsHttpBinding" contract="OperationLib.IOneWayCallService"/>
            <endpoint address="net.tcp://localhost:9014/OneWayCallService" binding="netTcpBinding" contract="OperationLib.IOneWayCallService"/>
            <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
            <endpoint address="mex" binding="mexTcpBinding" contract="IMetadataExchange"/>
          </service>

    Explanation:

    >> to do

    <!– ********************* Singleton Service ***************************** –>
          <service name="OperationLib.DuplexService" behaviorConfiguration="DuplexServiceBehavior">
            <host>
              <baseAddresses>
                <add baseAddress="http://localhost:9015/DuplexService"/&gt;
                <add baseAddress="net.tcp://localhost:9016/DuplexService"/>
              </baseAddresses>
            </host>

            <endpoint address="http://localhost:9015/DuplexService&quot; binding="wsDualHttpBinding" contract="OperationLib.IDuplexService"/>
            <endpoint address="net.tcp://localhost:9016/DuplexService" binding="netTcpBinding" contract="OperationLib.IDuplexService"/>
            <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
            <endpoint address="mex" binding="mexTcpBinding" contract="IMetadataExchange"/>
          </service>

    Explanation:

    Now to see all these things in action let’s create a service.
        

    Writing code to host the service.

    // Listing of Program.cs

    using System;
    using System.Text;
    using System.ServiceModel;

    namespace OperationLibHost
    {
        class Program
        {
            static void Main(string[] args)
            {
                ServiceHost m_RequestReplyHost = null;
                ServiceHost m_OneWayCallHost = null;
                ServiceHost m_DuplexServiceHost = null;

                Console.WriteLine("\nHosting RequestReplyService at >> ");
                Console.WriteLine("    http://localhost:9011/RequestReplyService&quot;);
                Console.WriteLine("    net.tcp://localhost:9012/RequestReplyService");

                try
                {
                    m_RequestReplyHost = new ServiceHost(typeof(OperationLib.RequestReplyService));
                    m_RequestReplyHost.Open();
                }
                catch (Exception eX)
                {
                    Console.WriteLine("Failed while starting RequestReplyService [" + eX.Message + "]");
                    m_RequestReplyHost = null;
                }

                if (m_RequestReplyHost != null) Console.WriteLine("RequestReplyService hosted successfully . . .");

                Console.WriteLine("\nHosting OneWayCallService at >> ");
                Console.WriteLine("   
    http://localhost:9013/OneWayCallService");
                Console.WriteLine("    net.tcp://localhost:9014/OneWayCallService");
                try
                {
                    m_OneWayCallHost = new ServiceHost(typeof(OperationLib.OneWayCallService));
                    m_OneWayCallHost.Open();
                }
                catch (Exception eX)
                {
                    Console.WriteLine("Failed while starting OneWayCallService [" + eX.Message + "]");
                    m_OneWayCallHost = null;
                }

                if (m_OneWayCallHost != null) Console.WriteLine("OneWayCallService hosted successfully . . .");

                Console.WriteLine("\nHosting DuplexService  at >> ");
                Console.WriteLine("   
    http://localhost:9015/DuplexService");
                Console.WriteLine("    net.tcp://localhost:9016/DuplexService");

                try
                {
                    m_DuplexServiceHost = new ServiceHost(typeof(OperationLib.DuplexService ));
                    m_DuplexServiceHost.Open();
                }
                catch (Exception eX)
                {
                    Console.WriteLine("Failed while starting Service [" + eX.Message + "]");
                    m_DuplexServiceHost = null;
                }
                if (m_DuplexServiceHost != null) Console.WriteLine("DuplexService hosted successfully . . .");

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

                m_RequestReplyHost.Close();
                m_OneWayCallHost.Close();
                m_DuplexServiceHost.Close();

                m_RequestReplyHost = null;
                m_OneWayCallHost = null;
                m_DuplexServiceHost = null;
            }
        }
    }

    Explanation:

    Now to see all these things in action let’s create a service.

    Build and Execute the host application.

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

    image

    Now when the service is hosted and running, let’s now create a client, which is actually going to use this service.

    Creating Client

    Creating the base project

    Take a console based application.

    Generating proxies

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

    Generate Proxy for RequestReplyService.

    References –> Add Service Reference

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

    image

    Generate Proxy for OneWayCallService.

    References –> Add Service Reference

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

    image

    Generate Proxy for DuplexService.

    References –> Add Service Reference

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

    image

    Adding Callback implementation to the client.

    Add a new Class to the Client Project, name it DuplexServiceCallbackSink, below is the listing.

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;

    namespace OperationClient
    {
        class DuplexServiceCallbackSink : DuplexServiceReference.IDuplexServiceCallback
        {
            public void OnValueAdded(double dblNum1, double dblNum2, double dblResult)
            {
                Console.WriteLine("\n\n>> Duplex Service <<CallBack>> called in Client >> Value 1 : {0:F2} Value 2 : {1:F2} Result : {2:F2}", dblNum1, dblNum2, dblResult);
            }
        }
    }

    Calling Services

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.ServiceModel;

    namespace OperationClient
    {
        class Program
        {
            static void Main(string[] args)
            {
                double dblVal1 = 100; double dblVal2 = 200;

                try
                {
                    RequestReplyServiceReference.RequestReplyServiceClient obj1 = new RequestReplyServiceReference.RequestReplyServiceClient("WSHttpBinding_IRequestReplyService");
                    RequestReplyServiceReference.RequestReplyServiceClient obj2 = new RequestReplyServiceReference.RequestReplyServiceClient("NetTcpBinding_IRequestReplyService");

                    Console.WriteLine("Calling Request Reply Service");

                    double dblResult1 = obj1.AddValue(dblVal1, dblVal2);
                    Console.WriteLine("Using HTTP Binding >> Value 1: {0:F2} Value 2: {1:F2}  Returns : {2:F2}", dblVal1, dblVal2, dblResult1);

                    dblVal1 = 100; dblVal2 = 200;
                    double dblResult2 = obj2.AddValue(dblVal1, dblVal2);
                    Console.WriteLine("Using TCP Binding >> Value 1: {0:F2} Value 2: {1:F2}  Return : {2:F2}", dblVal1, dblVal2, dblResult2);
                }
                catch (Exception eX)
                {
                    Console.WriteLine("Error while calling Request Reply Service [ " + eX.Message + "]");
                }
               
                try
                {
                    OneWayCallServiceReference.OneWayCallServiceClient obj3 = new OneWayCallServiceReference.OneWayCallServiceClient("WSHttpBinding_IOneWayCallService");
                    OneWayCallServiceReference.OneWayCallServiceClient obj4 = new OneWayCallServiceReference.OneWayCallServiceClient("NetTcpBinding_IOneWayCallService");

                    Console.WriteLine("Calling OneWayCall Service");

                    obj3.AddValue(dblVal1);
                    obj3.AddValue(dblVal2);
                    double dblResult3 = obj3.GetResult();
                    Console.WriteLine("Using HTTP Binding >> Value 1: {0:F2} Value 2: {1:F2}", dblVal1, dblVal2 );
                    Console.WriteLine("Result : {0:F2}", dblResult3);

                    obj4.AddValue(dblVal1);
                    obj4.AddValue(dblVal2);
                    double dblResult4 = obj4.GetResult();
                    Console.WriteLine("Using TCP Binding >> Value 1: {0:F2} Value 2: {1:F2}", dblVal1, dblVal2);
                    Console.WriteLine("Result : {0:F2}", dblResult4);
                }
                catch (Exception eX)
                {
                    Console.WriteLine("Error while calling One way Service [ " + eX.Message + "]");
                }

                try
                {
                    DuplexServiceCallbackSink callback = new DuplexServiceCallbackSink();
                    InstanceContext insCntxt = new InstanceContext(callback);

                    DuplexServiceReference.DuplexServiceClient obj5 = new DuplexServiceReference.DuplexServiceClient(insCntxt, "WSDualHttpBinding_IDuplexService");
                    DuplexServiceReference.DuplexServiceClient obj6 = new DuplexServiceReference.DuplexServiceClient(insCntxt, "NetTcpBinding_IDuplexService");

                    obj5.AddValue(dblVal1, dblVal2);
                    obj6.AddValue(dblVal1, dblVal2);
                }
                catch (Exception eX)
                {
                    Console.WriteLine("Error while calling Duplex Service [ " + eX.Message + "]");
                }

                Console.WriteLine("\n\nPress any key …");
                Console.ReadKey();
            }
        }
    }

    and here is the output.

    image

    October 4, 2013 Posted by | .NET, SOA, WCF | , , | Leave a comment

    WCF Hosting with Windows Service

    There are several ways to host a WCF service library (IIS, Windows Service, Self Hosting), Windows Service is one of them. Windows service hosting option is suitable for a long-running WCF service hosted outside of IIS in a secure environment that is not message activated. The lifetime of the service is controlled instead by the operating system. In this article, I am going to explain how to host a WCF library in a Windows service.

    his article has been divided into 4 modules:

    • WCF Service Library (WCFCalcLib.dll): Actual Service logic, which defines a Service Contract Interface, OperationContract, and implements them, and exposes few functions to the world to use them
    • Windows Service To Host the WCF Service Library WinSvcHostedCalcService.exe): Host the WCF library
    • Console Client Application (CalcClient.exe): Client Application which will use this service
    • Web based Client Application (CalcWebClient.exe): Web Application which will use this service

    First Module: WCF Service Library (WCFCalcLib.dll)

    To create this project, you can simply take a "Class Library" project, while choosing from project wizard option. let’s name it "WCFCalcLib", it is the actual service which implements the business logic. The project already contains a file Class1.cs, rename that file as CalcService.cs, add one more file (ICalcService.cs) to define the interface, although you can define interface in this file also.

    Definition of Service Contract

        [ServiceContract]
        public interface ICalcService
        {
            [OperationContract]
            double Add(double dblNum1, double dblNum2);
            [OperationContract]
            double Subtract(double dblNum1, double dblNum2);
            [OperationContract]
            double Multiply(double dblNum1, double dblNum2);
            [OperationContract]
            double Divide(double dblNum1, double dblNum2);
        } 
    Explanation

    Interface simply defines 4 operations supported by this service.

    Implementation of Service Contract

    public class CalcService : ICalcService
        {
            public double Add(double dblNum1, double dblNum2)
            {
                return (dblNum1 + dblNum2);
            }
     
            public double Subtract(double dblNum1, double dblNum2)
            {
                return (dblNum1 - dblNum2);
            }
     
            public double Multiply(double dblNum1, double dblNum2)
            {
                return (dblNum1 * dblNum2);
            }
     
            public double Divide(double dblNum1, double dblNum2)
            {
                return ((dblNum2 == 0) ? 0 : (dblNum1 / dblNum2));
            }
        } 
    ServiceContract Attribute

    Service Contract describes which related operations can be tied together as a single functional unit that the client can perform on the service.

    OperationContract Attribute

    An Operation contract specifies that the said operation is exposed by the service, service defines the parameters and return type of an operation.

    As one can see, there is nothing special here, just a Service Contract definition and its implementation.

    Second Module: WCF Service Library (WinSvcHostedCalcService.exe)

    To create this project, you can simply take a "Windows Service", while choosing from project wizard option. let’s name it "WinSVCHostedCalcService". This module is a Windows service, which is going to host our WCF Library (business logic), providing endpoints to the outside world to use WCF library. In this module at the start of the service, an infrastructure will be created to expose the service, and when the service is stopped, all objects are disposed.

    Little housekeeping

    Rename Service1.cs as MyCalcWinService.cs.

    Add a member variable ServiceHost type. As the name implies, ServiceHost provides a host for services. Name it, for instance m_svcHost.

    private ServiceHost m_svcHost = null ; 
    Implement the OnStart () and OnStop () handler for the service. 
    Creating end points when Service is started
    protected override void OnStart(string[] args)
            {
                if (m_svcHost != null) m_svcHost.Close();
                    
                string strAdrHTTP = "http://localhost:9001/CalcService";
                string strAdrTCP = "net.tcp://localhost:9002/CalcService";
     
                Uri[] adrbase = { new Uri(strAdrHTTP), new Uri(strAdrTCP) };
                m_svcHost = new ServiceHost(typeof(WCFCalcLib.CalcService), adrbase);
     
                ServiceMetadataBehavior mBehave = new ServiceMetadataBehavior();
                m_svcHost.Description.Behaviors.Add(mBehave);
     
                BasicHttpBinding httpb = new BasicHttpBinding();
                m_svcHost.AddServiceEndpoint(typeof(WCFCalcLib.ICalcService), httpb, strAdrHTTP);
                m_svcHost.AddServiceEndpoint(typeof(IMetadataExchange), 
                MetadataExchangeBindings.CreateMexHttpBinding(), "mex");
     
                NetTcpBinding tcpb = new NetTcpBinding();
                m_svcHost.AddServiceEndpoint(typeof(WCFCalcLib.ICalcService), tcpb, strAdrTCP);
                m_svcHost.AddServiceEndpoint(typeof(IMetadataExchange), 
                MetadataExchangeBindings.CreateMexTcpBinding(), "mex");
     
                m_svcHost.Open();
            }  
    Explanation

    The OnStart() handler is called, when the said service is started (through service control panel, or command line or otherwise), it does the following things.

     

    Defines two end points.

    string strAdrHTTP = "http://localhost:9001/CalcService";
    string strAdrTCP = "net.tcp://localhost:9002/CalcService"; 

    Creates and initializes a new instance of ServiceHost class, with the instance of the service (WCFCalcLib.CalcService) and its base addresses (adrbase) specified.

    Uri[] adrbase = { new Uri(strAdrHTTP), new Uri(strAdrTCP) };
    m_svcHost = new ServiceHost(typeof(WCFCalcLib.CalcService), adrbase)  
    Adding Service Behavior

    ServiceMetadataBehavior class controls the publication of service metadata and associated information, although.

    ServiceMetadataBehavior mBehave = new ServiceMetadataBehavior();
    m_svcHost.Description.Behaviors.Add(mBehave); 
    Adding service endpoints to the hosted service
    Add Basic TCP end point

    ServiceMetadataBehavior class controls the publication of service metadata and associated information, although.

    NetTcpBinding tcpb = new NetTcpBinding();
    m_svcHost.AddServiceEndpoint(typeof(WCFCalcLib.ICalcService), tcpb, strAdrTCP);  
    Add Basic HTTP end point

    BasicHttpBinding httpb = new BasicHttpBinding();

    m_svcHost.AddServiceEndpoint(typeof(WCFCalcLib.ICalcService), httpb, strAdrHTTP);

    Adding just behavior to the service is not sufficient for publication of metadata, you must add an IMetadataExchange endpoint to your service for each supported binding type.

    m_svcHost.AddServiceEndpoint(typeof(IMetadataExchange),

    MetadataExchangeBindings.CreateMexHttpBinding(), "mex");

    m_svcHost.AddServiceEndpoint(typeof(IMetadataExchange),

    MetadataExchangeBindings.CreateMexTcpBinding(), "mex"); 

    Finally open the host, and we are ready to go.

    m_svcHost.Open();

    Disposing, when Service is Stopped

    protected override void OnStop()

            {

                if (m_svcHost != null)

                {

                    m_svcHost.Close();

                    m_svcHost = null;

                }

            } 

    When the service is stopped either by service console/command prompt, or by any other means, Service host is closed, and object is assigned null.

    Installing the Service

    Windows service needs to be installed, and should be running, you can install the service using InstallUtil.exe utility. This utility is part of the SDK, the path is set, when you run this utility through Visual Studio command prompt.

    Open Visual Studio Command Prompt through:

    Start -> All Programs -> Microsoft Visual Studio 2010 -> Visual Studio Tools -> Visual Studio Command Prompt

    Switch to the folder, where WinSvcHostedCalcService.exe has been built, or preferably where you want to deploy it.

    Issue the following command to Install the service:

    Command Prompt > InstallUtil  WinSvcHostedCalcService.exe <return>
    Starting the Service

    You can start the service using command prompt using the following command:

    Command Prompt > sc start WinSvcHostedCalcService <return> 

    Another way to start the service is to:

    Computer -> Manage -> Services and Applications -> Services

    Locate the Service named as WinSvcHostedCalcService, right click and select Start.

    Third Module: Console Client Application (CalcClient.exe)

    Once the service has been built, and deployed, it is time to create the client. I have chosen the console based application, to keep things simple. Select the project template "Console Application", name it CalcClient.

    Generating the Service Proxy

    To use the service, we have to generate the proxy. Switch to the folder where client project has been created. Open a command prompt and issue a command to generate the proxy.

    Command Prompt >  SvcUtil http://localhost:9001/CalcService /out:CalcServiceProxy.cs /config:App.config <return>

    Two files – a Service Proxy file and a client configuration file is generated.

    • App.config
    • CalcServiceProxy.cs

    Add these to files to the newly generated client project. When we closely examine the configuration file, we find the following sections:

    <?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>
          <basicHttpBinding>
               <binding name="BasicHttpBinding_ICalcService" />
          </basicHttpBinding>
          <netTcpBinding>
               <binding name="NetTcpBinding_ICalcService" />
          </netTcpBinding>
    </bindings>

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

    <client>
         <endpoint address="http://localhost:9001/CalcService" binding="basicHttpBinding"
                    bindingConfiguration="BasicHttpBinding_ICalcService" 
                    contract="ICalcService"  name="BasicHttpBinding_ICalcService" />
         <endpoint address="net.tcp://localhost:9002/CalcService" 
                binding="netTcpBinding" bindingConfiguration="NetTcpBinding_ICalcService" 
                    contract="ICalcService" name="NetTcpBinding_ICalcService">
               <identity>
                        <servicePrincipalName value="host/PRAVEEN-WIN7" />
               </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;
    namespace CalcClient
    {
        class Program
        {
            static void Main(string[] args)
            {
                double dblX = 2000.0;
                double dblY = 100.0;
                double dblResult = 0;
                try
                {
                    Console.WriteLine("Using TCP Binding", dblX, dblY, dblResult);
                    CalcServiceClient objCalcClient1 = new CalcServiceClient
                    ("NetTcpBinding_ICalcService");
                    dblResult = objCalcClient1.Add(dblX, dblY);
                    Console.WriteLine("Calling Add >>  X : {0:F2}  Y : {1:F2}  
                    Result : {2:F2}", dblX, dblY, dblResult);
                    dblResult = objCalcClient1.Subtract(dblX, dblY);
                    Console.WriteLine("Calling Sub >>  X : {0:F2}  Y : {1:F2}  
                    Result : {2:F2}", dblX, dblY, dblResult);
                    dblResult = objCalcClient1.Multiply(dblX, dblY);
                    Console.WriteLine("Calling Mul >>  X : {0:F2}  Y : {1:F2}  
                    Result : {2:F2}", dblX, dblY, dblResult);
                    dblResult = objCalcClient1.Divide(dblX, dblY);
                    Console.WriteLine("Calling Sub >>  X : {0:F2}  Y : {1:F2}  
                    Result : {2:F2}", dblX, dblY, dblResult);
                    Console.WriteLine("Using Basic HTTP Binding", dblX, dblY, dblResult);
                    CalcServiceClient objCalcClient2 = new CalcServiceClient
                    ("BasicHttpBinding_ICalcService");
                    dblResult = objCalcClient2.Add(dblX, dblY);
                    Console.WriteLine("Calling Add >>  X : {0:F2}  Y : {1:F2}  
                    Result : {2:F2}", dblX, dblY, dblResult);
                    dblResult = objCalcClient2.Subtract(dblX, dblY);
                    Console.WriteLine("Calling Sub >>  X : {0:F2}  Y : {1:F2}  
                    Result : {2:F2}", dblX, dblY, dblResult);
                    dblResult = objCalcClient2.Multiply(dblX, dblY);
                    Console.WriteLine("Calling Mul >>  X : {0:F2}  Y : {1:F2}  
                    Result : {2:F2}", dblX, dblY, dblResult);
                    dblResult = objCalcClient2.Divide(dblX, dblY);
                    Console.WriteLine("Calling Sub >>  X : {0:F2}  Y : {1:F2}  
                    Result : {2:F2}", dblX, dblY, dblResult);
                }
                catch (Exception eX)
                {
                    Console.WriteLine("There was an error while calling Service 
                    [" + eX.Message + "]");
                }
            }
        }
    } 
    Explanation

    It simply does two things:

    Creates the proxy object for the TCP binding

    CalcServiceClient objCalcClient1 = new CalcServiceClient("NetTcpBinding_ICalcService");

    Call the service, and print the result:

    dblResult = objCalcClient1.Add(dblX, dblY);

    Console.WriteLine("Calling Add >>  X : {0:F2}  Y : {1:F2}  Result : {2:F2}", dblX, dblY, dblResult);

     
    dblResult = objCalcClient1.Subtract(dblX, dblY);

    Console.WriteLine("Calling Sub >>  X : {0:F2}  Y : {1:F2}  Result : {2:F2}", dblX, dblY, dblResult);

     
    dblResult = objCalcClient1.Multiply(dblX, dblY);

    Console.WriteLine("Calling Mul >>  X : {0:F2}  Y : {1:F2}  Result : {2:F2}", dblX, dblY, dblResult);

     
    dblResult = objCalcClient1.Divide(dblX, dblY);

    Console.WriteLine("Calling Sub >>  X : {0:F2}  Y : {1:F2}  Result : {2:F2}", dblX, dblY, dblResult);

    Creates the proxy object for the HTTP binding

    CalcServiceClient objCalcClient2 = new CalcServiceClient("BasicHttpBinding_ICalcService");

    Call the service, and print the result:

    dblResult = objCalcClient2.Add(dblX, dblY);

    Console.WriteLine("Calling Add >>  X : {0:F2}  Y : {1:F2}  Result : {2:F2}", dblX, dblY, dblResult);

     
    dblResult = objCalcClient2.Subtract(dblX, dblY);

    Console.WriteLine("Calling Sub >>  X : {0:F2}  Y : {1:F2}  Result : {2:F2}", dblX, dblY, dblResult);

     
    dblResult = objCalcClient2.Multiply(dblX, dblY);

    Console.WriteLine("Calling Mul >>  X : {0:F2}  Y : {1:F2}  Result : {2:F2}", dblX, dblY, dblResult);

     
    dblResult = objCalcClient2.Divide(dblX, dblY);

    Console.WriteLine("Calling Sub >>  X : {0:F2}  Y : {1:F2}  Result : {2:F2}", dblX, dblY, dblResult);

    Build and execute and here is the output:

    Fourth Module: Client Web page, which can use this service

    Create a new Web Site, open the default page (default.aspx), put some controls, and set the properties as described.

    • asp:Label ID="Label1" Text ="Value 1 :", runat="server"
    • asp:Label ID="Label2" Text ="Value 2 :", runat="server"
    • asp:TextBox ID="txtVal1" runat="server"
    • asp:TextBox ID="txtVal2" runat="server"
    • asp:Button = ID="btnCalc", onclick="btnCalc_Click", Text="Call Service" , runat="server"

    Below is the listing of default.aspx:

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

    font-size: 9pt;"><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"

    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"></span><pre><html

    xmlns="http://www.w3.org/1999/xhtml"&gt;

    <head runat="server">

        <title></title>

    </head>

    <body>

        <form id="form1" runat="server">

        <div>

            <asp:Label ID="Label1" runat="server"

            Text="Value 1 : "></asp:Label>

            <asp:TextBox ID="txtVal1" runat="server"></asp:TextBox>

            <br />

            <asp:Label ID="Label2" runat="server"

            Text="Value 2 : "></asp:Label>

            <asp:TextBox ID="txtVal2" runat="server"></asp:TextBox>

            <br />

            <asp:Button ID="btnCalc" runat="server"

            onclick="btnCalc_Click" Text="Button"

                Width="91px" />

            <br />

            <asp:Label ID="lblOutput" runat="server"

            BorderStyle="None" Height="152px"

            Width="606px"></asp:Label>

        </div>

        </form>

    </body>

    </html>

    Generating the Service Proxy

    Right click on the website project node, select the option Add service reference. type any mex end point address of the service in the Address : field:

    http://localhost:9001/CalcService/mex

    or:

    net.tcp://localhost:9002/CalcService/mex

    In the name give any name of your choice, it can be the name of the client’s namespace, let’s say for instance name it CalcServiceReference.

    Click OK as shown below:

    It will add a new App_WebReferences folder to your web site and will modify your web.config file, let’s analyze the changes, it made to the web.config. It made a new node in the last of the existing web.config named <system.ServiceModel> as shown below:

    <system.serviceModel>

        <bindings>

          <basicHttpBinding>

            <binding name="BasicHttpBinding_ICalcService"/>

          </basicHttpBinding>

          <netTcpBinding>

           <binding name="NetTcpBinding_ICalcService"/>

          </netTcpBinding>

        </bindings>

        <client>

          <endpoint address="http://localhost:9001/CalcService"

          binding="basicHttpBinding"

          bindingConfiguration="BasicHttpBinding_ICalcService"

          contract="CalcServiceReference.ICalcService"

          name="BasicHttpBinding_ICalcService"/>

          <endpoint address="net.tcp://localhost:9002/CalcService"

          binding="netTcpBinding"

          bindingConfiguration="NetTcpBinding_ICalcService"

          contract="CalcServiceReference.ICalcService"

          name="NetTcpBinding_ICalcService">

            <identity>

              <servicePrincipalName value="host/PRAVEEN-WIN7"/>

            </identity>

          </endpoint>

        </client>

    </system.serviceModel>

    If you analyze it carefully, you will see two sections bindings and client, binding sections simply defines the default binding names supported by this service, and client section defines the endpoints available for this service. Each endpoint has been named, so that we know, which endpoint we are using to call the service.

    Calling the Service

    Call the service, I have called the service on the click event of Call Service button. Code for the event handler is shown below:

    protected void btnCalc_Click(object sender, EventArgs e)

        {

            double dblX = 0, dblY = 0 ;

            bool b1 = double.TryParse(txtVal1.Text, out dblX);

            bool b2 = double.TryParse(txtVal2.Text, out dblY);

     
            if ((b1) && (b2))

            {

                StringBuilder sbTmp = new StringBuilder( "<font size=3 color=#000080>");

                sbTmp.Append("<p>Value 1 : " + dblX.ToString("F2"));

                sbTmp.Append("<br>Value 2 : " + dblY.ToString("F2"));

                sbTmp.Append ("<p>Using TCP Binding");

                CalcServiceReference.CalcServiceClient calcProxy1 = new CalcServiceReference.CalcServiceClient("NetTcpBinding_ICalcService");

                double dblResult = calcProxy1.Add(dblX, dblY);

                sbTmp.Append("<br>Calling Add >>  X : " + dblX.ToString("F2") + "  Y : " + dblY.ToString("F2") + " Result : " + dblResult.ToString("F2"));

                dblResult = calcProxy1.Subtract(dblX, dblY);

                sbTmp.Append("<br>Calling Add >>  X : " + dblX.ToString("F2") + "  Y : " + dblY.ToString("F2") + " Result : " + dblResult.ToString("F2"));

                dblResult = calcProxy1.Multiply(dblX, dblY);

                sbTmp.Append("<br>Calling Add >>  X : " + dblX.ToString("F2") + "  Y : " + dblY.ToString("F2") + " Result : " + dblResult.ToString("F2"));

                dblResult = calcProxy1.Divide(dblX, dblY);

                sbTmp.Append("<br>Calling Add >>  X : " + dblX.ToString("F2") + "  Y : " + dblY.ToString("F2") + " Result : " + dblResult.ToString("F2"));

                sbTmp.Append("<p>Using Basic HTTP Binding");

                CalcServiceReference.CalcServiceClient calcProxy2 = new CalcServiceReference.CalcServiceClient("BasicHttpBinding_ICalcService");

                dblResult = calcProxy2.Add(dblX, dblY);

                sbTmp.Append("<br>Calling Add >>  X : " + dblX.ToString("F2") + "  Y : " + dblY.ToString("F2") + " Result : " + dblResult.ToString("F2"));

                dblResult = calcProxy2.Subtract(dblX, dblY);

                sbTmp.Append("<br>Calling Add >>  X : " + dblX.ToString("F2") + "  Y : " + dblY.ToString("F2") + " Result : " + dblResult.ToString("F2"));

                dblResult = calcProxy2.Multiply(dblX, dblY);

                sbTmp.Append("<br>Calling Add >>  X : " + dblX.ToString("F2") + "  Y : " + dblY.ToString("F2") + " Result : " + dblResult.ToString("F2"));

                dblResult = calcProxy2.Divide(dblX, dblY);

                sbTmp.Append("<br>Calling Add >>  X : " + dblX.ToString("F2") + "  Y : " + dblY.ToString("F2") + " Result : " + dblResult.ToString("F2"));

                sbTmp.Append ( "<font>");

                lblOutput.Text = sbTmp.ToString();

            }

            else

            {

                lblOutput.Text = "<font size=4 COLOR=#ff0000> Invalid Input </font>";

            }

        }

    Output

    And here is the output:

    Points of Interest

    • There was no configuration file on the Hosting side (neither with Windows service, nor with WCF library). at the hosting side (in Windows service). End points are generated on the fly, this way, the port value can be made configurable.

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