Praveen Katiyar' Blog

Learning along the way . . . .

Understanding Transactions in WCF

Introduction

A Transaction is a set of complex operations, where failure of any single operation causes entire set to fail. A successful transaction, that transfers the system from one consistent state (A) to another consistent state (B), called a committed transaction.

 

TransactionBlock[5]

Transaction Properties

When you write a transactional service, one must abide by four core properties, known as ACID (atomic, consistent, isolated, and durable), and they are not optional.

  1. Atomic : To qualify as atomic, when a transaction completes. all individual operations must be made, as if they were one indivisible operation.
  2. Consistent:  any transaction must leave the system in consistent state (that makes sense), so transaction is required to transfer the system from one consistent state to another.
  3. Isolated : No other entity (transactional or not) is able to see the intermediate state of the resources, for obvious reasons (as they may be inconsistent).
  4. Durable : For a transaction to be durable, it must maintain its committed state, even if there is a failure (power outage, hardware failure).  The transaction must survive, regardless of the type of failure.

Transaction Propagation

In WCF, transaction can be propagated across service boundary. This enables service to participate in a client transaction and it also allows client, to include operations on multiple services in a sme transaction. By default, transaction aware bindings do not propagate transactions. You can specify whether or not client transaction is propagated to service by changing Binding and operational contract configuration. as shown below.

    <bindings > 
     <wsHttpBinding > 
        <binding name ="MandatoryTransBinding" transactionFlow ="true"> 
              <reliableSession enabled ="true"/> 
        </binding> 
     </wsHttpBinding> 
   </bindings>

Note:

by default transaction does not require reliable messaging, how ever enabling reliability will decrease the likelihood of aborted transactions, means that the transactions will be less likely to abort due to communication problems.

Just enabling transactionFlow, will not be sufficient to state, that a service wants to use client’s transaction in its each operation. You need to specify the “TransactionFlowAttribute" attribute in each operation contract, to enable transaction flow, as shown below.

  [OperationContract] 
  [TransactionFlow(TransactionFlowOption.Mandatory)] 
  [FaultContract(typeof(CuboidFaultException))] 
  void AddTM(int nID, double dblL, double dblW, double dblH);
or
  [OperationContract] 
  [TransactionFlow(TransactionFlowOption.Allowed)] 
  [FaultContract(typeof(CuboidFaultException))] 
  void AddTM(int nID, double dblL, double dblW, double dblH);

the values for the TransactionFlowOption property come from TransactionFlowOption enumeration as shown below.

  • Allowed : Transaction Flow may be flowed, (Client may or may not create a transaction, to be flowed for this operation.)
  • Mandatory : Transaction Flow must be flowed, (Client must create a transaction to be flowed, for this operation.)
  • NotAllowed : Transaction Flow Can not be flowed. , (Transaction can not be flowed, for this operation.)

Creating WCF Transaction

Once the interface has been defined with the proper TransactionFlow attribute, we need to create the service class which implements the service contract and set the operation behavior stating that operation needs a transaction, with TransactionScopeRequired = true.  This attribute enables the service transaction, if the client’s (propagated) transaction is not available.

    public class CuboidService:ICuboidService
    {
        [OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = true)] 
        public void AddTM(int nID, double dblL, double dblW, double dblH) 
        {
            . . . 
        }
    }

another important attribute TransactionAutoComplete, specifies whether to commit the current transaction automatically. If set true, and no unhandled exceptions are found, the current transaction is automatically committed. the default value for this property is true.

Transaction Propagation

In WCF, transaction can be propagated across service boundary. This enables service to participate in a client transaction and it allows client to include operations on multiple services in same transaction.  By default, transaction aware bindings do not propagate transactions. You can specify whether or not client transaction is propagated to service by changing Binding and operational contract configuration. as shown below.

<bindings >

      <wsHttpBinding >

        <binding name ="MandatoryTransBinding" transactionFlow ="true" >

          <reliableSession enabled ="true"/>

        </binding>

      </wsHttpBinding>

    </bindings>

Note:

by default transaction does not require reliable messaging, how ever enabling reliability will decrease the likelihood of aborted transactions, means that the transactions will be less likely to abort due to communication problems.

Background

In this article, i have taken a Cuboid as an example, Cuboid is a rectangular shaped cube, which has 3 dimensions (length, width and height).

for the database, there are two tables, one is called CuboidInfo, which simply contains the dimensions of the said cuboid, every cuboid is identified by its ID (primary key). The schema of CuboidInfo table is shown below.

cuboidInfoTable

the other table is called CuboidDetail, which contains the Volume and Surface Area of the said Cuboid (identified by its ID). The schema of CuboidDetail table is shown below.

cuboidDetailTable

So, our service demonstrates the transaction in following ways.

  • when you add a new cuboid to the database, its information is added to the CuboidInfo table, and its volume and surface area must be added to the CuboidDetail table.
  • when you update an existing cuboid in the database, its information is updated in the CuboidInfo table, and its volume and surface area must be updated, to the CuboidDetail table.
  • when you delete an existing cuboid from  the database, its information is deleted from CuboidInfo table, and its details must be deleted from the CuboidDetail table too.

Using the code

This article has been divided into 3 modules:

  • WCF Service Library (TransactionLib.dll): Service logic, which defines a Service Contract Interface, OperationContract, and implements them, and exposes few functions to the world to use them, and also implements transaction infrastructure.
  • Windows Form based Application to host the WCF Service Library TransactionLibHost.exe): Host the WCF library
  • Windows Form based Application (TransactionClient.exe): Client Application which will use this service

First Module: WCF Service Library (TransactionLib.dll)

To create this project, you can simply take a "Class Library" project, while choosing from project wizard option. let’s name it "TransactionLib", 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 ICuboidService Interface (ICuboidService.cs).

ICuboidService interface has seven methods, out of which

  • two methods with mandatory transactions as shown below.

void AddTM(int nID, double dblL, double dblW, double dblH);

   void UpdateTM(int nID, double dblL, double dblW, double dblH);

void DeleteTM(int nID);

notice that, each method has been decorated with

[TransactionFlow(TransactionFlowOption.Mandatory)]

mandates the transaction flow, from the client. that says, that client must create a transaction and propagate its transaction to the service, before executing these methods. if these methods are called without transaction, an exception is generated.

[FaultContract(typeof(CuboidFaultException))]

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

  • the next two methods with allowed transactions as shown below.

void AddTA(int nID, double dblL, double dblW, double dblH);

   void UpdateTA(int nID, double dblL, double dblW, double dblH);

void DeleteTA(int nID);

notice that, each method has been decorated with

[TransactionFlow(TransactionFlowOption.Allowed)]

that says, that client may create a transaction and propagate its transaction, to the service, before executing these methods. if client does not create a transaction,  and  method implementation is marked with,

[OperationBehavior(TransactionScopeRequired = true)]

then service itself will create a transaction scope, to execute its method.

[FaultContract(typeof(CuboidFaultException))]

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

  • the last method simply implements a fault contract, and does not mention anything about transaction, that means this method can be executed without any transaction overhead.

[OperationContract]

[FaultContract(typeof(CuboidFaultException))]

Dictionary<int, CuboidData> GetList();

Implement ICuboidService Interface (CuboidService.cs).

CuboidService class implements the ICuboidService interface, as shown below. Service has been decorated with some attributes.

[ServiceBehavior(TransactionIsolationLevel = System.Transactions.IsolationLevel.Serializable)]

public class CuboidService : ICuboidService

{

The isolation level of a transaction determines what level of access other transactions have to volatile data before a transaction completes.  The data affected by a transaction is called volatile. When you create a transaction, you can specify the isolation level that applies to the transaction. The isolation level of a transaction determines what level of access other transactions have to volatile data before a transaction completes. The highest isolation level, Serializable, provides a high degree of protection against interruptive transactions, but requires that each transaction complete before any other transactions are allowed to operate on the data.

Method AddTM ( int nID, double dblL, double dblW, double dblH)

AddTM is used to Add data to the table, passed cuboid information its information is added to the CuboidInfo table, and its volume and surface area is calculated and added to the CuboidDetail table.

[OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = true)]

public void AddTM(int nID, double dblL, double dblW, double dblH)

{

Operations are decorated with

TransactionScopeRequired = true, that simply says that this operation requires a TransactionScope, if client does not propagate its transaction, transaction is created at the service level itself.

TransactionAutoComplete = true signifies, to complete the transaction scope automatically on successful execution of the operation;

if ((nID <= 0) || (dblL <= 0) || (dblW <= 0) || (dblH <= 0))

{

    CuboidFaultException faultEx = new CuboidFaultException();

    faultEx.Reason = "Any dimension of Cuboid (length/width/height) can not be zero or negative value";

    faultEx.Source = "AddUpdateTM";

    StringBuilder sbDetail = new StringBuilder("");

    if (nID <= 0) sbDetail.Append("[ID is <= 0] ");

    if (dblL <= 0) sbDetail.Append("[Length is <= 0] ");

    if (dblW <= 0) sbDetail.Append("[Width is <= 0] ");

    if (dblW <= 0) sbDetail.Append("[Height is <= 0]");

    faultEx.Detail = sbDetail.ToString();

    throw new FaultException<CuboidFaultException>(faultEx);

}

then method checks the passed parameters, in case they are zero or negative a FaultException is thrown. if the input parameters are permissible, then usual business logic proceeds, that includes.

  • calculating the values to be added/updated in CuboidDetail table.

double dblVolume = (dblL * dblW * dblH);

double dblSArea = 2 * ((dblL * dblW) + (dblW * dblH) + (dblL * dblH));
        

  • preparing appropriate query to be run on database, for both the tables, according to the method called (add/update).

    strQuery1 = "INSERT INTO CuboidInfo (ID, Length, Width, Height ) VALUES (" + nID.ToString() + ", " + dblL.ToString("F2") + ", " + dblW.ToString("F2") + ", " + dblH.ToString("F2") + " )";

    strQuery2 = "INSERT INTO CuboidDetail (ID, SurfaceArea, Volume) VALUES (" + nID.ToString() + ", " + dblSArea.ToString("F2") + ", " + dblVolume.ToString("F2") + " )";

  • Reading Connection string from the App.config file of the host application.

// Get the all keys from the appSettings section from the configuration file.

System.Configuration.Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);

AppSettingsSection appSettings = (AppSettingsSection)config.GetSection("appSettings");

string[] keys = appSettings.Settings.AllKeys;

// Get the connection string.

string strConnStr = appSettings.Settings["ConnectionString"].Value;

  • Now the business logic, open Connection, execute both the queries, if there is any error throw a fault exception encapsulating error information.

try

{

     //  Open Connection

     dbConn = new SqlConnection(strConnStr);

     dbConn.Open();

     //  Create Command for Query 1

     SqlCommand dbCmd1 = new SqlCommand(strQuery1, dbConn);

     nCnt1 = dbCmd1.ExecuteNonQuery();

     //  Create Command for Query 2

     SqlCommand dbCmd2 = new SqlCommand(strQuery2, dbConn);

     nCnt2 = dbCmd2.ExecuteNonQuery();

     //  If TransactionAutoComplete=false, you need the line below.

     //  OperationContext.Current.SetTransactionComplete();

     //  Close Connection.

     dbConn.Close();

     dbConn = null;

}

catch (Exception eX)

{

     //  Close Connection.

     if (dbConn != null)

     {

          if (dbConn.State == ConnectionState.Open) dbConn.Close();

          dbConn = null;

     }

     CuboidFaultException faultEx = new CuboidFaultException();

     faultEx.Reason = "Failed while performing Add " ; 
     faultEx.Source = "AddUpdateTM";

     faultEx.Detail = eX.Message;

     throw new FaultException<CuboidFaultException>(faultEx);

}

Method AddTA (int nID, double dblL, double dblW, double dblH)

business logic for method AddTA is same as for AddTM. 

Method UpdateTM (int nID, double dblL, double dblW, double dblH)

UpdateTM is used to Update data to the table, passed cuboid information is updated to the CuboidInfo table, and its volume and surface area is calculated and updated to the CuboidDetail table.

[OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = true)]

public void UpdateTM(int nID, double dblL, double dblW, double dblH)

{

Operations are decorated with

TransactionScopeRequired = true, that simply says that this operation requires a TransactionScope, if client does not propagate its transaction, transaction is created at the service level itself.

TransactionAutoComplete = true signifies, to complete the transaction scope automatically on successful execution of the operation;

if ((nID <= 0) || (dblL <= 0) || (dblW <= 0) || (dblH <= 0))

{

    CuboidFaultException faultEx = new CuboidFaultException();

    faultEx.Reason = "Any dimension of Cuboid (length/width/height) can not be zero or negative value";

    faultEx.Source = "AddUpdateTM";

    StringBuilder sbDetail = new StringBuilder("");

    if (nID <= 0) sbDetail.Append("[ID is <= 0] ");

    if (dblL <= 0) sbDetail.Append("[Length is <= 0] ");

    if (dblW <= 0) sbDetail.Append("[Width is <= 0] ");

    if (dblW <= 0) sbDetail.Append("[Height is <= 0]");

    faultEx.Detail = sbDetail.ToString();

    throw new FaultException<CuboidFaultException>(faultEx);

}

then method checks the passed parameters, in case they are zero or negative a FaultException is thrown. if the input parameters are permissible, then usual business logic proceeds, that includes.

  • calculating the values to be added/updated in CuboidDetail table.

double dblVolume = (dblL * dblW * dblH);

double dblSArea = 2 * ((dblL * dblW) + (dblW * dblH) + (dblL * dblH));
        

  • preparing appropriate query to be run on database, for both the tables, according to the method called (add/update).

 strQuery1 = "Update CuboidInfo SET Length=" + dblL.ToString("F2") + ", Width=" + dblW.ToString() + ", Height=" + dblH.ToString() + " where ID=" + nID.ToString();

    strQuery2 = "Update CuboidDetail SET SurfaceArea=" + dblSArea.ToString("F2") + ", Volume=" + dblVolume.ToString() + " where ID=" + nID.ToString();

  • Reading Connection string from the App.config file of the host application.

// Get the all keys from the appSettings section from the configuration file.

System.Configuration.Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);

AppSettingsSection appSettings = (AppSettingsSection)config.GetSection("appSettings");

string[] keys = appSettings.Settings.AllKeys;

// Get the connection string.

string strConnStr = appSettings.Settings["ConnectionString"].Value;

  • Now the business logic, open Connection, execute both the queries, if there is any error throw a fault exception encapsulating error information.

try

{

     //  Open Connection

     dbConn = new SqlConnection(strConnStr);

     dbConn.Open();

     //  Create Command for Query 1

     SqlCommand dbCmd1 = new SqlCommand(strQuery1, dbConn);

     nCnt1 = dbCmd1.ExecuteNonQuery();

     //  Create Command for Query 2

     SqlCommand dbCmd2 = new SqlCommand(strQuery2, dbConn);

     nCnt2 = dbCmd2.ExecuteNonQuery();

     //  If TransactionAutoComplete=false, you need the line below.

     //  OperationContext.Current.SetTransactionComplete();

     //  Close Connection.

     dbConn.Close();

     dbConn = null;

}

catch (Exception eX)

{

     //  Close Connection.

     if (dbConn != null)

     {

          if (dbConn.State == ConnectionState.Open) dbConn.Close();

          dbConn = null;

     }

     CuboidFaultException faultEx = new CuboidFaultException();

     faultEx.Reason =  "Failed while performing Update";

     faultEx.Source = "UpdateTM";

     faultEx.Detail = eX.Message;

     throw new FaultException<CuboidFaultException>(faultEx);

}

Method UpdateTA (int nID, double dblL, double dblW, double dblH)

business logic for method UpdateTA is same as for UpdateTM. 

Method void DeleteTM(int nID)

  [OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = true)]

public void DeleteTM(int nID )

{

Operations are decorated with

TransactionScopeRequired = true, that simply says that this operation requires a TransactionScope, if client does not propagate its transaction, transaction is created at the service level itself.

TransactionAutoComplete = true signifies, to complete the transaction scope automatically on successful execution of the operation;

preparing appropriate query to be run on database, for both the tables, as records from both the tables need to be deleted.StringBuilder sbErr = new StringBuilder();

string strQuery1 = "delete from CuboidInfo where ID=" + nID.ToString();

string strQuery2 = "delete from CuboidDetail where ID=" + nID.ToString();

Fetch connection string from the app.config file.

System.Configuration.Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);

AppSettingsSection appSettings = (AppSettingsSection)config.GetSection("appSettings");

string[] keys = appSettings.Settings.AllKeys;

// Get the connection string.

string strConnStr = appSettings.Settings["ConnectionString"].Value;

Do the business logic, which involves, opening connection, deleting the matching row from both the tables. execute both the queries inside a try block,  if there is any error throw a fault exception encapsulating error information.

try

{

        //  Open Connection

        dbConn = new SqlConnection(strConnStr);

        dbConn.Open();

        //  Create Command for Query 1

        SqlCommand dbCmd1 = new SqlCommand(strQuery1, dbConn);

        nCnt1 = dbCmd1.ExecuteNonQuery();

        //  Create Command for Query 2

        SqlCommand dbCmd2 = new SqlCommand(strQuery2, dbConn);

        nCnt2 = dbCmd2.ExecuteNonQuery();

        //  As TransactionAutoComplete = false, so you manually need to notify, that transaction is compplete.

        //  OperationContext.Current.SetTransactionComplete();

        //  Close Connection.

        dbConn.Close();

        dbConn = null;

    }

    catch (Exception eX)

    {

        //  Close Connection.

        if (dbConn != null)

        {

            if (dbConn.State == ConnectionState.Open) dbConn.Close();

            dbConn = null;

        }

        CuboidFaultException faultEx = new CuboidFaultException();

        faultEx.Reason = "Failed while performing Delete";

        faultEx.Source = "DeleteTM";

        faultEx.Detail = eX.Message;

        throw new FaultException<CuboidFaultException>(faultEx);

    }

Method void DeleteTA(int nID)

business logic for method DeleteTA is same as for DeleteTM. 

Method  public  Dictionary<int, CuboidData> GetList()

This method simply reads the list all the Cuboid from the database. all inside a try block.

get connection string from app.config file.

Find application’s Settings through config file.

System.Configuration.Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);

AppSettingsSection appSettings = (AppSettingsSection)config.GetSection("appSettings");

string[] keys = appSettings.Settings.AllKeys;

// Get the connection string.

string strConnStr = appSettings.Settings["ConnectionString"].Value;

Open Connection

dbConn = new SqlConnection(strConnStr);

dbConn.Open();

Create Query to read ID, Length, Width, Height from CuboidInfo Table, while as SurfaceArea and Volume is fetched from CuboidDetail table. 

string strQuery = "select CuboidInfo.*, CuboidDetail.SurfaceArea, CuboidDetail.Volume from CuboidInfo join CuboidDetail on CuboidDetail.ID=CuboidInfo.ID";`

execute query and collect the result in the list.

//  Create Command for Query 1

string strQuery = "select CuboidInfo.*, CuboidDetail.SurfaceArea, CuboidDetail.Volume from CuboidInfo join CuboidDetail on CuboidDetail.ID=CuboidInfo.ID";

SqlCommand dbCmd = new SqlCommand(strQuery, dbConn);

dbReader = dbCmd.ExecuteReader();

while (dbReader.HasRows)

{

     bool bReaded = dbReader.Read();

     if (bReaded == false) break;

     CuboidData cInfo = new CuboidData();

     cInfo.ID = int.Parse(dbReader["ID"].ToString());

     cInfo.Length = int.Parse(dbReader["Length"].ToString());

     cInfo.Width = int.Parse(dbReader["Width"].ToString());

     cInfo.Height = int.Parse(dbReader["Height"].ToString());

     cInfo.SurfaceArea = int.Parse(dbReader["SurfaceArea"].ToString());

     cInfo.Volume = int.Parse(dbReader["Volume"].ToString());

     cuboidList.Add(cInfo.ID, cInfo);

}

Close reader and Connection.

dbReader.Close();   //  Close Reader

dbConn.Close();     //  Close Connection.

dbConn = null;

as everything was inside try block, if there is any error throw a fault exception encapsulating error information.

catch (Exception eX)

{

     //  Close Connection.

     if (dbConn != null)

     {

          if (dbReader.IsClosed == false) dbReader.Close();

          if (dbConn.State == ConnectionState.Open) dbConn.Close();

          dbConn = null;

     }

     CuboidFaultException faultEx = new CuboidFaultException();

     faultEx.Reason = "Failed while fetching QuboidData";

     faultEx.Source = "GetList";

     faultEx.Detail = eX.Message;

     throw new FaultException<CuboidFaultException>(faultEx);

}

otherwise simply return the populated CuboidList, and close the function block.

    return cuboidList;

}

Second Module: Service Host Application (TransactionLibHost.dll)

Add a new Project to the workspace, To create this project, i have taken a simple Console based application , while choosing from project wizard option. let’s name it "TransactionLibHost", this application will host our WCF Service library.

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 Service base address and Service Endpoints

<services>

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

       <host>

         <baseAddresses>

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

         </baseAddresses>

       </host>

      
       <endpoint address="" binding="wsHttpBinding" contract="TransactionLib.ICuboidService" bindingConfiguration ="transBinding" />

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

     
     </service>

   </services>

Explanation

behaviorConfiguration="TransactionLib.CuboidServiceBehavior

specifies that Service behavior of service has been defined in <serviceBehaviors> section,  which is named as "CuboidServiceBehavior".

<baseAddresses>  </baseAddresses>

Represents a collection of <baseAddress> elements, which are base addresses for a service host in a self-hosted environment. If a base address is present, endpoints can be configured with addresses relative to the base address.

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

Represents a configuration element that specifies the base addresses used by the service host.

<endpoint address="" binding="wsHttpBinding" contract="TransactionLib.ICuboidService" bindingConfiguration ="transBinding" />

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

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 behaviors

<behaviors>

     <serviceBehaviors>

       <behavior name="TransactionLib.CuboidServiceBehavior">

         <!– To avoid disclosing metadata information,

         set the value below to false and remove the metadata endpoint above before deployment –>

        <serviceMetadata httpGetEnabled="True"/>

         <!– To receive exception details in faults for debugging purposes,

         set the value below to true.  Set to false before deployment to avoid disclosing exception information –>

         <serviceDebug includeExceptionDetailInFaults="true" />

       </behavior>

     </serviceBehaviors>

   </behaviors>

Explanation

<serviceBehaviors> section represents all the behaviors defined for a specific service.

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.  

Defining Bindings

<bindings >

    <wsHttpBinding>

       <binding name ="transBinding" transactionFlow="true" >

          <reliableSession enabled ="true"/>

       </binding>

    </wsHttpBinding>

</bindings>

Explanation

<bindings> section used to configure system provided bindings.

<binding name ="transBinding" transactionFlow="true" >

By default, transaction aware bindings do not propagate transactions. You can specify whether or not client transaction is propagated to service by changing Binding and operational contract configuration. as transactionFlow="true".

<reliableSession enabled ="true"/>

Enabling reliability will decrease the likelihood of aborted transactions, means that the transactions will be less likely to abort due to communication problems.

Writing Code to Host the service.

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

  • add reference of System.ServiceModel to the project. 

Let’s write code to host the service.

try

{

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

     host.Open();

     Console.WriteLine("\nService is Hosted as http://localhost:9011/CuboidService&quot;);

     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();

}

Explanation

ServiceHost class provides a host for services.

Build and Execute the Host.

Third Module, Creating Client (TransactionClient.exe)

Creating the base project 

Take a Windows Form based based application. name it TransactionClient.

Creating GUI for the Client Project.

I have designed the GUI for the client project, to test all the aspects of the service as shown below.

ClientGUI

Control Control Name Purpose
TextBox txtID ID of the Cuboid has to be entered here.
TextBox txtLength Length of the Cuboid has to be entered here.
TextBox txtWidth Width of the Cuboid has to be entered here.
TextBox txtHeight Height of the Cuboid has to be entered here.
Button btnDeleteTMYes Calls DeleteTM method, with transaction
Button btnDeleteTMNo Calls DeleteTM method, without transaction
Button btnDeleteTAYes Calls DeleteTA method, with transaction
Button btnDeleteTANo Calls DeleteTA method, without transaction
Button btnAddTMYes Calls AddTM method, with transaction
Button btnAddTMNo Calls AddTM method, without transaction
Button btnAddTAYes Calls AddTA method, with transaction
Button btnAddTANo Calls AddTA method, without transaction
Button btnUpdateTMYes Calls UpdateTM method, with transaction
Button btnUpdateTMNo Calls UpdateTM method, without transaction
Button btnUpdateTAYes Calls UpdateTA method, with transaction
Button btnUpdateTANo Calls UpdateTA method, without transaction
Button btnRefresh Calls GetList method
Button btnClose Closes the Application.
LuistView lvOutput Displays Output.

 

Add service Reference to the client application

Make sure Host Application (TransactionLibHost.exe)  is running,

RunningTransactionLibHost

right click on the project, select Add Service Reference

Add Service Reference, a dialog box will be displayed as shown below.

GenerateProxyCuboidService

for the Address, enter http://localhost:9011/CuboidService/mex

for the Namespace, enter CuboidServiceReference.

press OK.

  • Service References folder will be created under Client project. under which a new node CuboidServiceReference will be created.
  • A configuration file (app.config) will be added to the client project.

Little house keeping

  • On the Form_Load event just initialize the controls.,

    private void frmTransactionLibClient_Load(object sender, EventArgs e)

    {

               txtID.Text = "1";

               txtLength.Text = "400";

               txtWidth.Text = "300.0";

               txtHeight.Text = "200" ;

               lvOutput.View = View.Details;

               lvOutput.FullRowSelect = true;

               lvOutput.GridLines = true;

               lvOutput.Columns.Clear();

               lvOutput.Columns.Add( "ID", 43);

               lvOutput.Columns.Add("Length", 52);

               lvOutput.Columns.Add("Width", 52);

               lvOutput.Columns.Add("Height", 52);

               lvOutput.Columns.Add("Area", 70);

               lvOutput.Columns.Add("Volume", 88);

    }

     

  • Create an Helper function to fetch the numeric values from Input Text Boxes.

    //  Common Helper Function.

    private void ParseInputs(ref int nID, ref double dblL, ref double dblW, ref double dblH)

    {

         nID = 0;    dblL = 0;  dblW = 0;   dblH = 0;

         int.TryParse(txtID.Text, out nID);

         double.TryParse(txtLength.Text, out dblL);

         double.TryParse(txtWidth.Text, out dblW);

         double.TryParse(txtHeight.Text, out dblH);

    }

    note that, no input is  validated at this side, i just fetched the values, as we have implemented a FaultContract on the service side, to handle invalid input errors. although i would recommend, if possible,  always validate parameters at the client side, before passing them to service.

Calling Methods on the Service.

Calling Method GetList

enclose every thing in a try block, Creates an object of the Service Proxy, and calls the service method.

try

{

    CuboidServiceClient objClient = new CuboidServiceClient();

    Dictionary<int, CuboidData> list = objClient.GetList();

    if (list != null)  
    {

       and if the return value is not null (there was no error). ListView control is populated.

       int nCount = list.Keys.Count;

       lvOutput.Items.Clear();

       foreach (int nID in list.Keys)

       {

            CuboidData cInfo = list[nID];

            ListViewItem lvItem = lvOutput.Items.Add(cInfo.ID.ToString());

            lvItem.SubItems.Add(cInfo.Length.ToString("F2"));

            lvItem.SubItems.Add(cInfo.Width.ToString("F2"));

            lvItem.SubItems.Add(cInfo.Height.ToString("F2"));

            lvItem.SubItems.Add(cInfo.SurfaceArea.ToString("F2"));

            lvItem.SubItems.Add(cInfo.Volume.ToString("F2"));

       }

    }

Close try block, and see if you got any fault exception, if yes, then display the error message.

}

catch (FaultException<CuboidFaultException> eX)

{

    strMsg = "Source [" + eX.Detail.Source + "]" + "\nReason [" + eX.Detail.Reason + "] \nDeatil [" + eX.Detail.Detail + "]";

}

catch (Exception eX)

{

    strMsg = eX.Message;

}

MessageBox.Show(strMsg);

Calling Method AddTM, Adding a cuboid, inside a client’s transaction

declare and initialize variables.

int nID = 0;

double dblLength = 0, dblWidth = 0, dblHeight = 0;

Parse input values from controls.

ParseInputs(ref nID, ref dblLength, ref dblWidth, ref dblHeight);

enclose every thing in a try block

try

{

execute the service method in client’s transaction

   using (TransactionScope ts = new TransactionScope(TransactionScopeOption.RequiresNew))

    {

create procy object and call the method.

        CuboidServiceClient objClient = new CuboidServiceClient();

        objClient.AddTM ( nID, dblLength, dblWidth, dblHeight);

Client’s transacton is complete.

       ts.Complete();

close client’s Transaction scope block       
    }

close try block

}

in case if there is any problem. store the error message from service.

catch (FaultException<CuboidFaultException> eX)

{

    strMsg = "Source ["+eX.Detail.Source+"]"+"\nReason ["+ eX.Detail.Reason+"] \nDeatil ["+eX.Detail.Detail+ "]";

}

catch (Exception eX)

{

    strMsg = eX.Message;

}

display error message

MessageBox.Show(strMsg);

Calling Method AddTM, Adding a cuboid, without a client’s transaction

declare and initialize variables.

int nID = 0;

double dblLength = 0, dblWidth = 0, dblHeight = 0;

Parse input values from controls.

ParseInputs(ref nID, ref dblLength, ref dblWidth, ref dblHeight);

enclose every thing in a try block

try

{

create proxy object and call the method.

        CuboidServiceClient objClient = new CuboidServiceClient();

        objClient.AddTM (  nID, dblLength, dblWidth, dblHeight);

Note: same method is called for Updating a cuboid, passing first parameter as false.

close try block

}

in case if there is any problem. store the error message from service.

catch (FaultException<CuboidFaultException> eX)

{

    strMsg = "Source ["+eX.Detail.Source+"]"+"\nReason ["+ eX.Detail.Reason+"] \nDeatil ["+eX.Detail.Detail+ "]";

}

catch (Exception eX)

{

    strMsg = eX.Message;

}

display error message

MessageBox.Show(strMsg);

Calling Method UpdateTM, updating a cuboid, inside a client’s transaction

declare and initialize variables.

int nID = 0;

double dblLength = 0, dblWidth = 0, dblHeight = 0;

Parse input values from controls.

ParseInputs(ref nID, ref dblLength, ref dblWidth, ref dblHeight);

enclose every thing in a try block

try

{

execute the service method in client’s transaction

   using (TransactionScope ts = new TransactionScope(TransactionScopeOption.RequiresNew))

    {

create procy object and call the method.

        CuboidServiceClient objClient = new CuboidServiceClient();

        objClient.UpdateTM ( nID, dblLength, dblWidth, dblHeight);

Note: same method is called for Updating a cuboid, passing first parameter as false.

Client’s transacton is complete.

       ts.Complete();

close client’s Transaction scope block       
    }

close try block

}

in case if there is any problem. store the error message from service.

catch (FaultException<CuboidFaultException> eX)

{

    strMsg = "Source ["+eX.Detail.Source+"]"+"\nReason ["+ eX.Detail.Reason+"] \nDeatil ["+eX.Detail.Detail+ "]";

}

catch (Exception eX)

{

    strMsg = eX.Message;

}

display error message

MessageBox.Show(strMsg);

Calling Method UpdateTM, Updating a cuboid, without a client’s transaction

declare and initialize variables.

int nID = 0;

double dblLength = 0, dblWidth = 0, dblHeight = 0;

Parse input values from controls.

ParseInputs(ref nID, ref dblLength, ref dblWidth, ref dblHeight);

enclose every thing in a try block

try

{

create proxy object and call the method.

        CuboidServiceClient objClient = new CuboidServiceClient();

        objClient.UpdatingTM (  nID, dblLength, dblWidth, dblHeight);

close try block

}

in case if there is any problem. store the error message from service.

catch (FaultException<CuboidFaultException> eX)

{

    strMsg = "Source ["+eX.Detail.Source+"]"+"\nReason ["+ eX.Detail.Reason+"] \nDeatil ["+eX.Detail.Detail+ "]";

}

catch (Exception eX)

{

    strMsg = eX.Message;

}

display error message

MessageBox.Show(strMsg);

Calling Method DeleteTM, Adding a cuboid, inside a client’s transaction

declare and initialize variables.

int nID = 0;

Parse input values from controls.

int.TryParse(txtID.Text, out nID);

enclose every thing in a try block

try

{

execute the service method in client’s transaction

   using (TransactionScope ts = new TransactionScope(TransactionScopeOption.RequiresNew))

    {

create procy object and call the method.

        CuboidServiceClient objClient = new CuboidServiceClient();

        objClient.DeleteTM ( nID);

Client’s transacton is complete.

       ts.Complete();

close client’s Transaction scope block       
    }

close try block

}

in case if there is any problem. store the error message from service.

catch (FaultException<CuboidFaultException> eX)

{

    strMsg = "Source ["+eX.Detail.Source+"]"+"\nReason ["+ eX.Detail.Reason+"] \nDeatil ["+eX.Detail.Detail+ "]";

}

catch (Exception eX)

{

    strMsg = eX.Message;

}

display error message

MessageBox.Show(strMsg);

Calling Method DeleteTM, Deleting a cuboid, without a client’s transaction

declare and initialize variables.

int nID = 0;

Parse input values from controls.

int.TryParse(txtID.Text, out nID);

enclose every thing in a try block

try

{

create procy object and call the method.

        CuboidServiceClient objClient = new CuboidServiceClient();

        objClient.DeleteTM ( nID);

close try block

}

in case if there is any problem. store the error message from service.

catch (FaultException<CuboidFaultException> eX)

{

    strMsg = "Source ["+eX.Detail.Source+"]"+"\nReason ["+ eX.Detail.Reason+"] \nDeatil ["+eX.Detail.Detail+ "]";

}

catch (Exception eX)

{

    strMsg = eX.Message;

}

display error message

MessageBox.Show(strMsg);

Calling Method AddTA, Adding a cuboid, inside a client’s transaction

declare and initialize variables.

int nID = 0;

double dblLength = 0, dblWidth = 0, dblHeight = 0;

Parse input values from controls.

ParseInputs(ref nID, ref dblLength, ref dblWidth, ref dblHeight);

enclose every thing in a try block

try

{

execute the service method in client’s transaction

   using (TransactionScope ts = new TransactionScope(TransactionScopeOption.RequiresNew))

    {

create procy object and call the method.

        CuboidServiceClient objClient = new CuboidServiceClient();

        objClient.AddTA ( nID, dblLength, dblWidth, dblHeight);

Client’s transacton is complete.

       ts.Complete();

close client’s Transaction scope block       
    }

close try block

}

in case if there is any problem. store the error message from service.

catch (FaultException<CuboidFaultException> eX)

{

    strMsg = "Source ["+eX.Detail.Source+"]"+"\nReason ["+ eX.Detail.Reason+"] \nDeatil ["+eX.Detail.Detail+ "]";

}

catch (Exception eX)

{

    strMsg = eX.Message;

}

display error message

MessageBox.Show(strMsg);

Calling Method AddTA, Adding a cuboid, without a client’s transaction

declare and initialize variables.

int nID = 0;

double dblLength = 0, dblWidth = 0, dblHeight = 0;

Parse input values from controls.

ParseInputs(ref nID, ref dblLength, ref dblWidth, ref dblHeight);

enclose every thing in a try block

try

{

create proxy object and call the method.

        CuboidServiceClient objClient = new CuboidServiceClient();

        objClient.AddTA ( nID, dblLength, dblWidth, dblHeight);

close try block

}

in case if there is any problem. store the error message from service.

catch (FaultException<CuboidFaultException> eX)

{

    strMsg = "Source ["+eX.Detail.Source+"]"+"\nReason ["+ eX.Detail.Reason+"] \nDeatil ["+eX.Detail.Detail+ "]";

}

catch (Exception eX)

{

    strMsg = eX.Message;

}

display error message

MessageBox.Show(strMsg);

Calling Method UpdateTA, updating a cuboid, inside a client’s transaction

declare and initialize variables.

int nID = 0;

double dblLength = 0, dblWidth = 0, dblHeight = 0;

Parse input values from controls.

ParseInputs(ref nID, ref dblLength, ref dblWidth, ref dblHeight);

enclose every thing in a try block

try

{

execute the service method in client’s transaction

   using (TransactionScope ts = new TransactionScope(TransactionScopeOption.RequiresNew))

    {

create procy object and call the method.

        CuboidServiceClient objClient = new CuboidServiceClient();

        objClient.UpdateTA ( nID, dblLength, dblWidth, dblHeight);

Note: same method is called for Updating a cuboid, passing first parameter as false.

Client’s transacton is complete.

       ts.Complete();

close client’s Transaction scope block       
    }

close try block

}

in case if there is any problem. store the error message from service.

catch (FaultException<CuboidFaultException> eX)

{

    strMsg = "Source ["+eX.Detail.Source+"]"+"\nReason ["+ eX.Detail.Reason+"] \nDeatil ["+eX.Detail.Detail+ "]";

}

catch (Exception eX)

{

    strMsg = eX.Message;

}

display error message

MessageBox.Show(strMsg);

Calling Method UpdateTA, Updating a cuboid, without a client’s transaction

declare and initialize variables.

int nID = 0;

double dblLength = 0, dblWidth = 0, dblHeight = 0;

Parse input values from controls.

ParseInputs(ref nID, ref dblLength, ref dblWidth, ref dblHeight);

enclose every thing in a try block

try

{

create proxy object and call the method.

        CuboidServiceClient objClient = new CuboidServiceClient();

        objClient.UpdatingTA (  nID, dblLength, dblWidth, dblHeight);

close try block

}

in case if there is any problem. store the error message from service.

catch (FaultException<CuboidFaultException> eX)

{

    strMsg = "Source ["+eX.Detail.Source+"]"+"\nReason ["+ eX.Detail.Reason+"] \nDeatil ["+eX.Detail.Detail+ "]";

}

catch (Exception eX)

{

    strMsg = eX.Message;

}

display error message

MessageBox.Show(strMsg);

Calling Method DeleteTA, Adding a cuboid, inside a client’s transaction

declare and initialize variables.

int nID = 0;

Parse input values from controls.

int.TryParse(txtID.Text, out nID);

enclose every thing in a try block

try

{

execute the service method in client’s transaction

   using (TransactionScope ts = new TransactionScope(TransactionScopeOption.RequiresNew))

    {

create procy object and call the method.

        CuboidServiceClient objClient = new CuboidServiceClient();

        objClient.DeleteTA ( nID);

Client’s transacton is complete.

       ts.Complete();

close client’s Transaction scope block       
    }

close try block

}

in case if there is any problem. store the error message from service.

catch (FaultException<CuboidFaultException> eX)

{

    strMsg = "Source ["+eX.Detail.Source+"]"+"\nReason ["+ eX.Detail.Reason+"] \nDeatil ["+eX.Detail.Detail+ "]";

}

catch (Exception eX)

{

    strMsg = eX.Message;

}

display error message

MessageBox.Show(strMsg);

Calling Method DeleteTA, Deleting a cuboid, without a client’s transaction

declare and initialize variables.

int nID = 0;

Parse input values from controls.

int.TryParse(txtID.Text, out nID);

enclose every thing in a try block

try

{

create procy object and call the method.

        CuboidServiceClient objClient = new CuboidServiceClient();

        objClient.DeleteTA ( nID);

close try block

}

in case if there is any problem. store the error message from service.

catch (FaultException<CuboidFaultException> eX)

{

    strMsg = "Source ["+eX.Detail.Source+"]"+"\nReason ["+ eX.Detail.Reason+"] \nDeatil ["+eX.Detail.Detail+ "]";

}

catch (Exception eX)

{

    strMsg = eX.Message;

}

display error message

MessageBox.Show(strMsg);

Output

Calling Method GetList

 

Calling Method Client Transaction
GetList N/A GetListOutput
 

AddTM

Yes

image
     
AddTM No image


AddTA
Yes image
AddTA No image
UpdateTM Yes image
UpdateTM No image
UpdateTA Yes image
UpdateTA No image
DeleteTM Yes image
DeleteTM No image
DeleteTA Yes image
DeleteTA No image
Advertisements

November 12, 2013 Posted by | .NET, SOA, WCF | , , | Leave a comment

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

    Understanding Instance Management in WCF

    instance management is the technique WCF uses to bind client requests to service instances, governing which service instance handles which client request, and when. WCF Supports 3 types of instance activation

    • Per Call : per call services allocate and destroy a new service instance for each  client request.
    • Per Session: session full services allocate a service instance for each client  connection.
    • Singleton: singleton services share the same service instance for all clients, across all connections and activations.

    by and large the service instance mode is strictly a server side implementation detail, hat should not manifest itself on the client side in any way.

    now let’s define a service, that demonstrates the instance management in real life.

    Creating the Class Library

    let’s create a service, create a new project, take a Class Library template, name it InstanceLib, 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 ISingleCallService to the project, a new file ISingleCallService.cs will be added to the project.
    • Add a new Class named SingleCallService, to the project. that will implement the ISingleCallService interface, a new file SingleCallService.cs will be added to the project.
    • Add a new Interface named ISessionService to the project, a new file ISessionService.cs will be added to the project.
    • Add a new Class named SessionService, to the project. that will implement the ISessionService interface, a new file SessionService.cs will be added to the project.
    • Add a new Interface named ISingletonService to the project, a new file ISingletonService.cs will be added to the project.
    • Add a new Class named SingletonService, to the project. that will implement the ISingletonService interface, a new file SingletonService.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 ISingleCallService.cs

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

    namespace InstanceLib
    {
        [ServiceContract]
        interface ISingleCallService
        {
            [OperationContract]
            double AddValue (double dblNum);
        }
    }

    // Listing of ISessionService.cs

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

    namespace InstanceLib
    {
        [ServiceContract]
        interface ISessionService
        {
            [OperationContract]
            double AddValue (double dblNum);
        }
    }

    // Listing of ISingletonService.cs

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

    namespace InstanceLib
    {
        [ServiceContract]
        interface ISingletonService
        {
            [OperationContract]
            double AddValue (double dblNum);
        }
    }

    Implementing Interfaces

    Let’s implement each interface,as shown below.

    // Listing of SingleCallService.cs

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

    namespace InstanceLib
    {
        [ServiceBehavior(InstanceContextMode=InstanceContextMode.PerCall)]  
        public class SingleCallService : ISingleCallService
        {
            private double m_dblTotal = 0 ;

            public double AddValue(double dblVal)
            {
                m_dblTotal += dblVal;
                return m_dblTotal;
            }
        }
    }

    Important

    You can notice ServiceBehavior attribute of the class, which has been defined as InstanceContextMode.PerCall, it specifies that a new InstanceContext object is created prior to and recycled subsequent to each calll. if you omit this attribute or do not specify, then InstanceContextMode.PerCall is assumed by default.

    // Listing of SessionService.cs

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

    namespace InstanceLib
    {
        [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession)]
        public class SessionService : ISessionService
        {
            private double m_dblTotal = 0 ;

            public double AddValue(double dblVal)
            {
                m_dblTotal += dblVal;
                return m_dblTotal;
            }
        }
    }

    Important

    You can notice ServiceBehavior attribute of the class, which has been defined as InstanceContextMode.PerSession, it specifies that InstanceContext object is created for each session.

     // Listing of SingletonService.cs

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

    namespace InstanceLib
    {
        [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
        public class SingletonService : ISingletonService
        {
            private double m_dblTotal = 0 ;

            public double AddValue(double dblVal)
            {
                m_dblTotal += dblVal;
                return m_dblTotal;
            }
        }
    }

    Important

    You can notice ServiceBehavior attribute of the class, which has been defined as InstanceContextMode.Singlel, it specifies that Only one InstanceContext object is used for all incoming calls and is not recycled to the calls. if the service object does not exist, one is created. 

    if you notice the all 3 service implementation, you will notice the difference in ServiceBehavior class attribute of each class, apart from that, all interfaces has been implemented has the same implementation. they simply add a passed value to a member variable of the class.

    Build the project and your Class Library is ready. 

    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>

          <!–************************ Single Call Service ************************ –>
          <service name="InstanceLib.SingleCallService" behaviorConfiguration="SingleCallServiceBehavior">
            <host>
              <baseAddresses>
                <add baseAddress="
    http://localhost:9011/SingleCallService"/>
                <add baseAddress="net.tcp://localhost:9012/SingleCallService"/>
              </baseAddresses>
            </host>

            <endpoint address="http://localhost:9011/SingleCallService" binding="wsHttpBinding" contract="InstanceLib.ISingleCallService"/>
            <endpoint address="net.tcp://localhost:9012/SingleCallService" binding="netTcpBinding" contract="InstanceLib.ISingleCallService"/>
            <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
            <endpoint address="mex" binding="mexTcpBinding" contract="IMetadataExchange"/>
          </service>

          <!—************************ Single Session Service ************************–>
         <service name="InstanceLib.SessionService" behaviorConfiguration="SessionServiceBehavior">
            <host>
              <baseAddresses>
                <add baseAddress="
    http://localhost:9013/SessionService"/>
                <add baseAddress="net.tcp://localhost:9014/SessionService"/>
              </baseAddresses>
            </host>

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

          <!—************************** Singleton Service **************************–>
         <service name="InstanceLib.SingletonService" behaviorConfiguration="SingletonServiceBehavior">
            <host>
              <baseAddresses>
                <add baseAddress="
    http://localhost:9015/SingletonService"/>
                <add baseAddress="net.tcp://localhost:9016/SingletonService"/>
              </baseAddresses>
            </host>

            <endpoint address="http://localhost:9015/SingletonService" binding="wsHttpBinding" contract="InstanceLib.ISingletonService"/>
            <endpoint address="net.tcp://localhost:9016/SingletonService" binding="netTcpBinding" contract="InstanceLib.ISingletonService"/>
            <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="SingleCallServiceBehavior">
              <serviceMetadata httpGetEnabled="true"/>
            </behavior>

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

            <!–Singleton Service Behavior –>
            <behavior name="SingletonServiceBehavior">

              <serviceMetadata httpGetEnabled="true"/>
            </behavior>

          </serviceBehaviors>
        </behaviors>

      </system.serviceModel>
    </configuration>

    Now let’s analyze the Configuration file.

    Single Call service.

    <!–******************* Single Call Service ************************** –>
          <service name="InstanceLib.SingleCallService" behaviorConfiguration="SingleCallServiceBehavior">
            <host>
             <baseAddresses>
                <add baseAddress="
    http://localhost:9011/SingleCallService"/>
                <add baseAddress="net.tcp://localhost:9012/SingleCallService"/>
             </baseAddresses>
            </host>

            <endpoint address="http://localhost:9011/SingleCallService" binding="wsHttpBinding" contract="InstanceLib.ISingleCallService"/>
            <endpoint address="net.tcp://localhost:9012/SingleCallService" binding="netTcpBinding" contract="InstanceLib.ISingleCallService"/>
            <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
            <endpoint address="mex" binding="mexTcpBinding" contract="IMetadataExchange"/>
          </service>

    Session service.

    <!—************************* Single Session Service ************************* –>
          <service name="InstanceLib.SessionService" behaviorConfiguration="SessionServiceBehavior">
            <host>
              <baseAddresses>
                <add baseAddress="
    http://localhost:9013/SessionService"/>
                <add baseAddress="net.tcp://localhost:9014/SessionService"/>
              </baseAddresses>
            </host>

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

    Singleton service.

    <!—************************** Singleton Service **************************–>
         <service name="InstanceLib.SingletonService" behaviorConfiguration="SingletonServiceBehavior">
            <host>
              <baseAddresses>
                <add baseAddress="
    http://localhost:9015/SingletonService"/>
                <add baseAddress="net.tcp://localhost:9016/SingletonService"/>
              </baseAddresses>
            </host>

            <endpoint address="http://localhost:9015/SingletonService" binding="wsHttpBinding" contract="InstanceLib.ISingletonService"/>
            <endpoint address="net.tcp://localhost:9016/SingletonService" binding="netTcpBinding" contract="InstanceLib.ISingletonService"/>
            <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
            <endpoint address="mex" binding="mexTcpBinding" contract="IMetadataExchange"/>
          </service>

     

    Writing code to host the service.

    // Listing of Program.cs

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

    namespace InstanceLibHost
    {
        class Program
        {
            static void Main(string[] args)
            {
                ServiceHost m_SingleCallHost = null;
                ServiceHost m_SingleSessHost = null;
                ServiceHost m_SingletonHost = null;

                Console.WriteLine("\nHosting Single Call Service at >> " );
                Console.WriteLine("   
    http://localhost:9011/SingleCallService");
                Console.WriteLine("    net.tcp://localhost:9012/SingleCallService");
           
                try
                {
                    m_SingleCallHost = new ServiceHost(typeof(InstanceLib.SingleCallService));
                    m_SingleCallHost.Open();
                }
                catch (Exception eX)
                {
                    Console.WriteLine("Failed while starting Single Call Service Service [" + eX.Message + "]");
                    m_SingleCallHost = null;
                }

                if ( m_SingleCallHost!= null ) Console.WriteLine("Single Call Service hosted successfully . . .");

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

                if (m_SingleSessHost != null) Console.WriteLine("Single Session Service hosted successfully . . .");

                Console.WriteLine("\nHosting Singlton Service at >> " );
                Console.WriteLine("   
    http://localhost:9015/SingletonService" );
                Console.WriteLine("    net.tcp://localhost:9016/SingletonService");
           
                try
                {
                    m_SingletonHost = new ServiceHost(new InstanceLib.SingletonService());
                    m_SingletonHost.Open();
                }
                catch (Exception eX)
                {
                    Console.WriteLine("Failed while starting Service [" + eX.Message + "]");
                    m_SingletonHost = null;
                }
                if (m_SingletonHost != null) Console.WriteLine("Singleton Service hosted successfully . . .");

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

                m_SingleCallHost.Close ();
                m_SingleSessHost.Close ();
                m_SingletonHost.Close () ;

                m_SingleCallHost = null;
                m_SingleSessHost = null;
                m_SingletonHost = null;
            }
        }
    }

    Build and Execute the Service.

    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 SingleCallService.

    References –> Add Service Reference

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

    image

    in the namespace type some good name, let’s say SingleCallServiceReference for instance.

    hit OK, this will simply add a new file app.config to the project. and a new item in the Service Reference node of the project.

    Generate Proxy for SessionService.

    References –> Add Service Reference

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

    image

    in the namespace type some good name, let’s say SessionServiceReference for instance.

    Generate Proxy for SingletonService.

    References –> Add Service Reference

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

    image

    in the namespace type some good name, let’s say SingletonServiceReference for instance.

    Now when you have added references of all 3 services, let’s write code for the client to use these services.

    // Listing of Program.cs

    using System;
    using System.Text;

    namespace InstanceClient
    {
    class Program
        {
            static void Main(string[] args)
            {
                SingleCallServiceReference.SingleCallServiceClient objSvc1 = new SingleCallServiceReference.SingleCallServiceClient("WSHttpBinding_ISingleCallService");
                SingleCallServiceReference.SingleCallServiceClient objSvc2 = new SingleCallServiceReference.SingleCallServiceClient("NetTcpBinding_ISingleCallService");

                Console.WriteLine("Client 1 Calling Single Call Service 3 times Using Http Binding");
                for (int nI = 0; nI < 3; nI++)
                {
                    double dblValue1 = 10;
                    double dblResult1 = objSvc1.AddValue(dblValue1);
                    Console.WriteLine("Using HTTP Binding >> Input Value : {0:F2} Return value : {1:F2}", dblValue1, dblResult1);
                }

                Console.WriteLine("Client 2 Calling Single Call Service 3 times using TCP Binding");
                for (int nI = 0; nI < 3; nI++)
                {
                    double dblValue2 = 10;
                    double dblResult2 = objSvc2.AddValue(dblValue2);
                    Console.WriteLine("Using TCP  Binding >> Input Value : {0:F2} Return value : {1:F2}", dblValue2, dblResult2);
                }

                SessionServiceReference.SessionServiceClient objSvc3 = new SessionServiceReference.SessionServiceClient("WSHttpBinding_ISessionService");
                SessionServiceReference.SessionServiceClient objSvc4 = new SessionServiceReference.SessionServiceClient("NetTcpBinding_ISessionService");

                Console.WriteLine("\n\nClient 1 Calling Single Session Service 3 times Using Http Binding");
                for (int nI = 0; nI < 3; nI++)
                {
                    double dblValue1 = 10;
                    double dblResult1 = objSvc3.AddValue(dblValue1);
                    Console.WriteLine("Using HTTP Binding >> Input Value : {0:F2} Return value : {1:F2}", dblValue1, dblResult1);
                }

                Console.WriteLine("Client 2 Calling Single Session Service 3 times using TCP Binding");
                for (int nI = 0; nI < 3; nI++)
                {
                    double dblValue2 = 10;
                    double dblResult2 = objSvc4.AddValue(dblValue2);
                    Console.WriteLine("Using TCP  Binding >> Input Value : {0:F2} Return value : {1:F2}", dblValue2, dblResult2);
                }

                SingletonServiceReference.SingletonServiceClient objSvc5 = new SingletonServiceReference.SingletonServiceClient("WSHttpBinding_ISingletonService");
                SingletonServiceReference.SingletonServiceClient objSvc6 = new SingletonServiceReference.SingletonServiceClient("NetTcpBinding_ISingletonService");

                Console.WriteLine("\n\nClient 1 Calling Singleton Service 3 times Using Http Binding");
                for (int nI = 0; nI < 3; nI++)
                {
                    double dblValue1 = 10;
                    double dblResult1 = objSvc5.AddValue(dblValue1);
                    Console.WriteLine("Using HTTP Binding >> Input Value : {0:F2} Return value : {1:F2}", dblValue1, dblResult1);
                }

                Console.WriteLine("Client 2 Calling Singleton Service 3 times using TCP Binding");
                for (int nI = 0; nI < 3; nI++)
                {
                    double dblValue2 = 10;
                    double dblResult2 = objSvc6.AddValue(dblValue2);
                    Console.WriteLine("Using TCP  Binding >> Input Value : {0:F2} Return value : {1:F2}", dblValue2, dblResult2);
                }

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

     

    Open command prompt and execute the client to see the output.

    image

    Open one more command prompt, and execute another instance of client.

    image

    as you can see from the output, all the instance mode in action.

    September 29, 2013 Posted by | .NET, CodeProject, SOA, WCF | , , , | Leave a comment

    Creating and Consuming a WCF Service without configuration files.

    Introduction

    This article explains how to create a simple WCF Service Host it on a specified port according to specified binding. all this you can do on the fly.

    Background

    Normally when we develop WCF Services, we are in habit of using Configuration files. (web.config/app.config depending on the environment). it is indeed very good idea to use configuration files. all this seems very automatic. And using configuration files is default way of doing things.

    How this project is organized

    In this article i have used 3 projects.

    • WCF Service (WCFMathLib.dll):  Actual Service logic, which defines a Service Contract Interface, OperationContract, and implements them, and exposes few functions to the world to use them.
    • Host Application (ConWCFMathHost.exe): Defines the logic to host the said service, according to the parameters supplied at the command line.
    • Client Application (ConWCFMathClient.exe): Client Application which will use this service.

    First Part : WCF Service (WCFMathLib.dll):

    The actual Service which implements the business logic. it defines an ServiceContract and few Operations available through Service. as shown below.  let;s call it IMathInterface.cs

    to create this project you can simply take a Class Library Project, from while choosing from project wizard option. let’s Name it "WCFMathLib", it already contains a File Class1.cs, rename that file as MathService.cs, add one more file (IMathService.cs) to define the interface, although you can define interface this file also. below is the listing of both files.

     

    Defining a Service Contract

    // Listing of IMathInterface.cs
    [ServiceContract]
    public interface IMathService
    {
        [OperationContract]
        double AddNumber(double dblX, double dblY);
       
        [OperationContract]
        double SubtractNumber(double dblX, double dblY);
       
        [OperationContract]
        double MultiplyNumber(double dblX, double dblY);
       
        [OperationContract]
        double DivideNumber(double dblX, double dblY);
    }

    Implementation  of Service Contract

    //    Listing MathInterface.cs
    public class MathService : IMathService
    {
        public double AddNumber(double dblX, double dblY)
        {
            return (dblX + dblY);
        }

        public double SubtractNumber(double dblX, double dblY)
        {
            return (dblX – dblY);
        }

        public double MultiplyNumber(double dblX, double dblY)
        {
            return (dblX * dblY);
        }

        public double DivideNumber(double dblX, double dblY)
        {
            return ((dblY == 0 ) ? 0 : dblX / dblY);
        }
    }

    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 specifyies 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 an Service Contract definition and its implementation.

    Second Part : WCF Service (ConWCFMathHost.exe):

    Host application has been developed as console based application, which host our Service (WCFMathLib.MathService), according to the supplied parameters, parameters are supplied in the following form.  it accepts two parameters.

    ConWCFMathHost.exe TCP/HTTP portAdr <return>

    Parameter 1 : Binding accepted values are HTTP and TCP

    Parameter 2 : Port Number, and integer value specifyin the port value.

    Hosting Service using HTTP Bidnding

    private static void StartHTTPService( int nPort)
    {
        string strAdr = "
    http://localhost:" + nPort.ToString() + "/MathService";
        try
        {
            Uri adrbase = new Uri(strAdr);
            m_svcHost = new ServiceHost(typeof(MathService), adrbase);

            ServiceMetadataBehavior mBehave = new ServiceMetadataBehavior();
            m_svcHost.Description.Behaviors.Add(mBehave);
            m_svcHost.AddServiceEndpoint(typeof(IMetadataExchange), MetadataExchangeBindings.CreateMexHttpBinding(), "mex");

            BasicHttpBinding httpb = new BasicHttpBinding();
            m_svcHost.AddServiceEndpoint(typeof(IMathService), httpb, strAdr);
            m_svcHost.Open();
            Console.WriteLine("\n\nService is Running as >> " + strAdr );
        }
        catch (Exception eX)
        {
            m_svcHost = null;
            Console.WriteLine("Service can not be started as >> [" + strAdr + "] \n\nError Message [" + eX.Message + "]");
        }
    }

    Hosting Service using TCP Bidnding

    private static void StartTCPService(int nPort)
    {
        string strAdr = "net.tcp://localhost:" + nPort.ToString() + "/MathService";
        try
        {
            Uri adrbase = new Uri(strAdr);
            m_svcHost = new ServiceHost(typeof(MathService), adrbase);
            NetTcpBinding tcpb = new NetTcpBinding();

                    ServiceMetadataBehavior mBehave = new ServiceMetadataBehavior();
            m_svcHost.Description.Behaviors.Add(mBehave);
            m_svcHost.AddServiceEndpoint(typeof(IMetadataExchange),   MetadataExchangeBindings.CreateMexTcpBinding(), "mex");

            m_svcHost.AddServiceEndpoint(typeof(IMathService), tcpb, strAdr);
            m_svcHost.Open();
            Console.WriteLine("\n\nService is Running as >> " + strAdr );
        }
        catch (Exception eX)
        {
            m_svcHost = null;
            Console.WriteLine("Service can not be started as >> [" + strAdr + "] \n\nError Message [" + eX.Message + "]");
        }
    }

    Code Description

    Create Desired binding (TCP/HTTP)

    ServiceMetadataBehavior, Controls the publication of service metadata and associated information. although we can omit the part shown in bold, as far as this project is concerned, as we are going to create everything by hand, but it is a good Idea, to always add a IMetadataExchange endpoint to the service. as it  exposes methods used to return the metadata about  the service. it is usefull when we are generating the proxy class and config files from the service using SVCUtil.exe utility.

    Host Application Complete Code

    // Program.cs

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

    namespace ConWCFMathHost
    {
        class Program
        {
            static ServiceHost m_svcHost = null;
            static void Main(string[] args)
            {
                if (args.Count() != 2)
                {
                    Console.WriteLine("Insufficient arguments supplied");
                    Console.WriteLine("Service Host must be started as <Executable Name> <TCP/HTTP> <Port#>");
                    Console.WriteLine("Argument 1 >> Specifying the Binding, which binding will be used to Host (Supported values are either HTTP or TCP) without quotes");
                    Console.WriteLine("Argument 2 >> Port Number a numeric value, the port you want to use");
                    Console.WriteLine("\nExamples");
                    Console.WriteLine("<Executable Name> TCP 9001");
                    Console.WriteLine("<Executable Name> TCP 8001");
                    Console.WriteLine("<Executable Name> HTTP 8001");
                    Console.WriteLine("<Executable Name> HTTP 9001");
                    return;
                }

                string strBinding = args[0].ToUpper();
                bool bSuccess = ((strBinding == "TCP") || (strBinding == "HTTP"));
                if (bSuccess == false)
                {
                    Console.WriteLine("\nBinding argument is invalid, should be either TCP or HTTP)");
                    return;
                }
               
                int nPort = 0;
                bSuccess = int.TryParse(args[1], out nPort);
                if (bSuccess == false)
                {
                    Console.WriteLine("\nPort number must be a numeric value");
                    return;
                }

                bool bindingTCP = (strBinding == "TCP");
                if (bindingTCP) StartTCPService(nPort);  else StartHTTPService(nPort);
                if (m_svcHost != null)
                {
                    Console.WriteLine("\nPress any key to close the Service");
                    Console.ReadKey();
                    StopService();
                }
            }

            private static void StartTCPService(int nPort)
            {
                string strAdr = "net.tcp://localhost:" + nPort.ToString() + "/MathService";
                try
                {
                    Uri adrbase = new Uri(strAdr);
                    m_svcHost = new ServiceHost(typeof(MathService), adrbase);
                   
                    ServiceMetadataBehavior mBehave = new ServiceMetadataBehavior();
                    m_svcHost.Description.Behaviors.Add(mBehave);
                    m_svcHost.AddServiceEndpoint(typeof(IMetadataExchange), MetadataExchangeBindings.CreateMexTcpBinding(), "mex");

                    NetTcpBinding tcpb = new NetTcpBinding();
                    m_svcHost.AddServiceEndpoint(typeof(IMathService), tcpb, strAdr);
                    m_svcHost.Open();
                    Console.WriteLine("\n\nService is Running as >> " + strAdr );
                }
                catch (Exception eX)
                {
                    m_svcHost = null;
                    Console.WriteLine("Service can not be started as >> [" + strAdr + "] \n\nError Message [" + eX.Message + "]");
                }
            }

            private static void StartHTTPService( int nPort)
            {
                string strAdr = "
    http://localhost:" + nPort.ToString() + "/MathService";
                try
                {
                    Uri adrbase = new Uri(strAdr);
                    m_svcHost = new ServiceHost(typeof(MathService), adrbase);
                   
                    ServiceMetadataBehavior mBehave = new ServiceMetadataBehavior();
                    m_svcHost.Description.Behaviors.Add(mBehave);
                    m_svcHost.AddServiceEndpoint(typeof(IMetadataExchange), MetadataExchangeBindings.CreateMexHttpBinding(), "mex");

                    BasicHttpBinding httpb = new BasicHttpBinding();
                    m_svcHost.AddServiceEndpoint(typeof(IMathService), httpb, strAdr);
                    m_svcHost.Open();
                    Console.WriteLine("\n\nService is Running as >> " + strAdr );
                }
                catch (Exception eX)
                {
                    m_svcHost = null;
                    Console.WriteLine("Service can not be started as >> [" + strAdr + "] \n\nError Message [" + eX.Message + "]");
                }
            }

            private static void StopService()
            {
                if (m_svcHost != null)
                {
                    m_svcHost.Close();
                    m_svcHost = null;
                }
            }
        }
    }

    Third Part : Client (ConWCFMathClient.exe):

    Client application has been also developed as console based application, parameters through command line in the following form.  you need to supply 6 parameters at the command line.

    ConWCFMathClient <Host Machine> <TCP/HTTP> <Port#> <ADD/SUB/MUL/DIV> Num1 Num2

    Parameter 1 : name/IP address of the machine, where service has been hosted.

    Parameter 2 : Binding of the hosted service, accepted values are HTTP and TCP

    Parameter 3 : Port Number, and integer value specifyin the port value.

    Parameter 4 : Operation Code (accepted values are ADD, SUB, MUL, DIV)

    Parameter 5 : Operand 1, operand value 1 for the operation.

    Parameter 6 : Operand 2, operand value 2 for the operation.

    Importing the Interface  definition

    You can define the interface or just import the IMathService.cs file from the Service. this simply defines the interface, so that there is no error while compiling the client project

    // Listing of IMathInterface.cs
    namespace ConWCFMathClient
    {
        [ServiceContract]
        public interface IMathService
        {
            [OperationContract]
            double AddNumber(double dblX, double dblY);
           
            [OperationContract]
            double SubtractNumber(double dblX, double dblY);
           
            [OperationContract]
            double MultiplyNumber(double dblX, double dblY);
           
            [OperationContract]
            double DivideNumber(double dblX, double dblY);
        }
    }

      

    Calling Methods of the Service

    a function has been created to call  methods of the Service, passing all the parameter to the method, after validating them. here is the mehod.

    private static void Evaluate(string strServer, string strBinding, int nPort, string strOper, double dblVal1, double dblVal2)
    {
        ChannelFactory<IMathService> channelFactory = null;
        EndpointAddress ep = null;

        string strEPAdr = "http://" + strServer + ":" + nPort.ToString() + "/MathService";
        try
        {
            switch (strBinding)
            {
                case "TCP":
                    NetTcpBinding tcpb = new NetTcpBinding();
                    channelFactory = new ChannelFactory<IMathService>(tcpb);

                    // End Point Address
                    strEPAdr = "net.tcp://" + strServer + ":" + nPort.ToString() + "/MathService";
                    break;

                case "HTTP":
                    BasicHttpBinding httpb = new BasicHttpBinding();
                    channelFactory = new ChannelFactory<IMathService>(httpb);
                   
                    // End Point Address
                    strEPAdr = "
    http://" + strServer + ":" + nPort.ToString() + "/MathService";
                    break;
            }

            // Create End Point
            ep = new EndpointAddress(strEPAdr);

            // Create Channel
            IMathService mathSvcObj = channelFactory.CreateChannel(ep);
            double dblResult = 0;

                // Call Methods
            switch (strOper)
            {
                case "ADD": dblResult = mathSvcObj.AddNumber(dblVal1, dblVal2); break;
                case "SUB": dblResult = mathSvcObj.SubtractNumber(dblVal1, dblVal2); break;
                case "MUL": dblResult = mathSvcObj.MultiplyNumber(dblVal1, dblVal2); break;
                case "DIV": dblResult = mathSvcObj.DivideNumber(dblVal1, dblVal2); break;
            }

            //  Display Results.
            Console.WriteLine("Operation {0} ", strOper );
            Console.WriteLine("Operand 1 {0} ", dblVal1.ToString ( "F2"));
            Console.WriteLine("Operand 2 {0} ", dblVal2.ToString("F2"));
            Console.WriteLine("Result {0} ", dblResult.ToString("F2"));
            channelFactory.Close();
        }
        catch (Exception eX)
        {
            // Something unexpected happended ..
            Console.WriteLine("Error while performing operation [" + eX.Message + "] \n\n Inner Exception [" + eX.InnerException + "]");
        }
    }

    Code Description

    Before you can call any method, you need to do two things.

    Create a channel factory object, using the specified binding using .

    for HTTP Bindng

    BasicHttpBinding httpb = new BasicHttpBinding();
    channelFactory = new ChannelFactory<IMathService>(httpb);

    for TCP Bindng

    NetTcpBinding tcpb = new NetTcpBinding();
    channelFactory = new ChannelFactory<IMathService>(tcpb);
     

    EndPoint address for HTTP binding

    strEPAdr = "http://" + strServer + ":" + nPort.ToString() + "/MathService";

    EndPoint address   TCP binding

    strEPadr = strEPAdr = "net.tcp://" + strServer + ":" + nPort.ToString() + "/MathService";

    Create a chaneel through specified end point aadress

    // Create End Point ep = new EndpointAddress(strEPAdr); // Create ChannelIMathService mathSvcObj = channelFactory.CreateChannel(ep);                

    Invoke methods through channel and display output.

    double dblResult = 0;

    // Call Methods
    switch (strOper)
    {
        case "ADD": dblResult = mathSvcObj.AddNumber(dblVal1, dblVal2); break;
        case "SUB": dblResult = mathSvcObj.SubtractNumber(dblVal1, dblVal2); break;
        case "MUL": dblResult = mathSvcObj.MultiplyNumber(dblVal1, dblVal2); break;
        case "DIV": dblResult = mathSvcObj.DivideNumber(dblVal1, dblVal2); break;
    }

    //  Display Results.
    Console.WriteLine("Operation {0} ", strOper );
    Console.WriteLine("Operand 1 {0} ", dblVal1.ToString ( "F2"));
    Console.WriteLine("Operand 2 {0} ", dblVal2.ToString("F2"));
    Console.WriteLine("Result {0} ", dblResult.ToString("F2"));

    // Close channel
    channelFactory.Close();
     

    Point of Interest

    • No configuration file,
    • there is no proxy generation using SvcUtil

    Client Application Complete Code

    // Program.cs
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.ServiceModel;
    using System.ServiceModel.Description;

    namespace ConWCFMathClient
    {
        class Program
        {
            static void Main(string[] args)
            {
                if (args.Count() !=6 )
                {
                    Console.WriteLine("Insufficient arguments supplied");
                    Console.WriteLine("Service Host must be started as ConWCFMathClient <Host Machine> <TCP/HTTP> <Port#> <ADD/SUB/MUL/DIV> Num1 Num2");
                    Console.WriteLine("Argument 1 >> IP address or the Machine name, where Service is hosted/running (localhost if running onm the same machine)");
                    Console.WriteLine("Argument 2 >> Specifying the Binding type, which binding is used to Host the service. (Supported values are either HTTP or TCP),  without quotes");
                    Console.WriteLine("Argument 3 >> Port Number a numeric value");
                    Console.WriteLine("Argument 4 >> Operation permissible values are ADD/SUB/MUL/DIV without quotes");
                    Console.WriteLine("Argument 5 >> Operand 1 for the operation ");
                    Console.WriteLine("Argument 6 >> Operand 2 for the operation ");
                    Console.WriteLine("\nExamples");
                    Console.WriteLine("<Executable Name> 192.168.1.1 TCP 9001 ADD 1000 2000");
                    Console.WriteLine("<Executable Name> 192.168.1.1 TCP 8001 SUB 500 1000");
                    Console.WriteLine("<Executable Name> 192.168.1.1 HTTP 8001 MUL 500 1000");
                    Console.WriteLine("<Executable Name> 192.168.1.1 HTTP 9001 DIV 3000 1000");
                    Console.WriteLine("<Executable Name> localhost TCP 9001 ADD 4000.0  500.0");
                    Console.WriteLine("<Executable Name> localhost TCP 8001 SUB 600.0 700.0");
                    Console.WriteLine("<Executable Name> localhost HTTP 8001 MUL 1200.0 300.0");
                    Console.WriteLine("<Executable Name> localhost HTTP 9001 DIV 2000.0 90.0");
                    return;
                }

                string strAdr = args[0];
                string strBinding = args[1].ToUpper();
                bool bSuccess = ((strBinding == "TCP") || (strBinding == "HTTP"));
                if (bSuccess == false)
                {
                    Console.WriteLine("\nBinding argument is invalid, should be either TCP or HTTP)");
                    return;
                }

                int nPort = 0;
                bSuccess = int.TryParse(args[2], out nPort);
                if (bSuccess == false)
                {
                    Console.WriteLine("\nPort number must be a numeric value");
                    return;
                }

                string strOper = args[3].ToUpper();
                bSuccess = ((strOper == "ADD") || (strOper == "SUB") || (strOper == "MUL") || (strOper == "DIV"));
                if (bSuccess == false)
                {
                    Console.WriteLine("\nOperation argument is invalid, should be ADD/SUB/MUL/DIV)");
                    return;
                }

                //  Determine operand 1
                double dblNum1 = 0;
                bSuccess = double.TryParse(args[4], out dblNum1);
                if (bSuccess == false)
                {
                    Console.WriteLine("\nOperand 1 must be a numeric value");
                    return;
                }

                //  Determine operand 2
                double dblNum2 = 0;
                bSuccess = double.TryParse(args[5], out dblNum2);
                if (bSuccess == false)
                {
                    Console.WriteLine("\nnOperand 2 must be a numeric value");
                    return;
                }

                Evaluate(strAdr, strBinding, nPort, strOper, dblNum1, dblNum2);
            }

            private static void Evaluate(string strServer, string strBinding, int nPort, string strOper, double dblVal1, double dblVal2)
            {
                ChannelFactory<IMathService> channelFactory = null;
                EndpointAddress ep = null;

                string strEPAdr = "http://" + strServer + ":" + nPort.ToString() + "/MathService";
                try
                {
                    switch (strBinding)
                    {
                        case "TCP":
                        NetTcpBinding tcpb = new NetTcpBinding();
                        channelFactory = new ChannelFactory<IMathService>(tcpb);

                        // End Point Address
                        strEPAdr = "net.tcp://" + strServer + ":" + nPort.ToString() + "/MathService";
                        break;

                                            case "HTTP":
                        BasicHttpBinding httpb = new BasicHttpBinding();
                        channelFactory = new ChannelFactory<IMathService>(httpb);

                        // End Point Address
                        strEPAdr = "
    http://" + strServer + ":" + nPort.ToString() + "/MathService";
                        break;
                    }

                    // Create End Point
                    ep = new EndpointAddress(strEPAdr);

                    // Create Channel
                    IMathService mathSvcObj = channelFactory.CreateChannel(ep);
                    double dblResult = 0;

                    // Call Methods
                    switch (strOper)
                    {
                        case "ADD": dblResult = mathSvcObj.AddNumber(dblVal1, dblVal2); break;
                        case "SUB": dblResult = mathSvcObj.SubtractNumber(dblVal1, dblVal2); break;
                        case "MUL": dblResult = mathSvcObj.MultiplyNumber(dblVal1, dblVal2); break;
                        case "DIV": dblResult = mathSvcObj.DivideNumber(dblVal1, dblVal2); break;
                    }
               
                    //  Display Results.
                    Console.WriteLine("Operation {0} ", strOper );
                    Console.WriteLine("Operand 1 {0} ", dblVal1.ToString ( "F2"));
                    Console.WriteLine("Operand 2 {0} ", dblVal2.ToString("F2"));
                    Console.WriteLine("Result {0} ", dblResult.ToString("F2"));

                    // Close channel
                    channelFactory.Close();
                }
                catch (Exception eX)
                {
                    // Something happended ..
                    Console.WriteLine("Error while performing operation [" + eX.Message + "] \n\n Inner Exception [" + eX.InnerException + "]");
                }
            }
        }
    }

    //    Listing of IMathInterface.cs

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

    namespace ConWCFMathClient
    {
        [ServiceContract]
        public interface IMathService
        {
            [OperationContract]
            double AddNumber(double dblX, double dblY);
            [OperationContract]
            double SubtractNumber(double dblX, double dblY);
            [OperationContract]
            double MultiplyNumber(double dblX, double dblY);
            [OperationContract]
            double DivideNumber(double dblX, double dblY);
        }
    }

    Output

    Running the Host Application as a TCP Service

    image

    Running the Client Application

    image

    Running the Host Application as a HTTP Service

    Notewhile running the Service in HTTP Mode, you need to open command prompt in administrator mode.

    image

    Running the Client Application

    image

    September 12, 2013 Posted by | .NET, SOA, WCF | , , | Leave a comment

    Creating a Self Hosted WCF Service

    In this post I am going to explain how to create a simple WCF Service, with Self Hosting and consume it, there will be 4 parts of the project.

    1. A Class Library (MyMathServiceLib.dll): which will implement our business logic.
    2. Service Host Application (MathServiceHost.exe): Application which will host this class library as a WCF Service.
    3. A Windows Client Application (MyMathServiceClient.exe): Client Application which will use this service.
    4. A Web Client, that will use this Service.

    Note:

    part 1 and 2 can be merged into one, if one like so, but, for the sake of portability, keep them as separate modules. let’s start with creating the Class Library.

    Part 1: Creating the Class Library

    Start the Visual Studio, and click File->New->Project . Select the project template ‘Class Library" click OK.

    image

    Now let’s analyse the project created by wizard for us, and do little house keeping, before we write any code.

    • Class1.cs (The default class, created by wizard).

    Assumption

    We are going to create a Service as per following assumptions.

    • Interface will named as IMyMathService (IMyMathService.cs)
    • Service will be implemented as MyMathService (MyMathService.cs)

    Little house keeping. (just to keep things organized)

    • Delete Class1.cs the project workspace.
    • Add an Interface (IMyMathService) IMyMathService.cs to the project.
    • Add an Class (MyMathService) MyMathService.cs to the project.
    • Add reference of System.ServiceModel to the  project.

    Now, let’s define the Interface and its implementation of our old familiar class Library.

    //  Listing of IMyMathService.cs,

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

    namespace MyMathServiceLib
    {
        [ServiceContract]
        public interface IMyMathService
        {
            [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);
        }
    }

    //  Listing of MyMathService.cs,

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

    namespace MyMathServiceLib
    {
        public class MyMathService : IMyMathService
        {
            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));
            }
        }
    }

    Part 2: Creating the Host Application

    Our class library is ready, let’s write an Host application, which will expose this facility to the world as a WCF Service. lets’ Create a Console based application, for the sake of simplicity, name it MyMathServiceHost, as shown below.

    image

    a new Project MyMathServiceHost will be added to the workspace.

    Assumption

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

    • Service will implement HTTP endpoint at Port 9001
    • Corresponding mex End Point  (IMetadatExchange) for the HTTP end point.  
    • Service will implement TCP endpoint at Port 9002
    • Corresponding mex End Point  (IMetadatExchange) for the TCP end point.  

    Adding reference of the class library to the Host project

    • Add reference of the class library to the console project,as shown below.

    image

    • Add reference of System.ServiceModel to the  project.

    Creating the Service Configuration

    Add a Application configuration file (App.config) to the Host Project. define the End Points and bindings according to the assumptions made at the start of the project.

    • will expose an end point with HTTP Binding at 9001
    • will expose an end point with TCP Binding at 9002
    • will expose, required mex (one for each binding) end points

    below is he listing of the app.config file.

    <?xml versio="1.0"?>
    <configuration>

        <system.serviceModel>
          <services>
            <service name="MyMathServiceLib.MyMathService" behaviorConfiguration="myMathServiceBehave">
              <host>
                <baseAddresses>
                  <add baseAddress="http://localhost:9001/MyMathService"/>
                  <add baseAddress="net.tcp://localhost:9002/MyMathService"/>
                </baseAddresses>
              </host>
              <endpoint address="http://localhost:9001/MyMathService" binding="basicHttpBinding" contract="MyMathServiceLib.IMyMathService"/>
              <endpoint address="net.tcp://localhost:9002/MyMathService" binding="netTcpBinding" contract="MyMathServiceLib.IMyMathService"/>
              <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
              <endpoint address="mex" binding="mexTcpBinding" contract="IMetadataExchange"/>
            </service>
          </services>
          <behaviors>
            <serviceBehaviors>
              <behavior name="myMathServiceBehave">
                <serviceMetadata httpGetEnabled="true"/>
              </behavior>
            </serviceBehaviors>
          </behaviors>
        </system.serviceModel>
    </configuration>

    Description
    • Application configuration files defines 2 endpoints, one for TCP and one for HTTP. and 1 mex end point for each.
    • Service behavior define, <serviceMetadata httpGetEnabled="true"/> that meta data should be available through a HTTP get request.

    Write code to Host the Service.

    Write the code to Host the Service in the Main function of the Open Program.cs, and let’s write the code.

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

    namespace CalcServiceHost
    {
        class Program
        {
            static void Main(string[] args)
            {
                ServiceHost svcHost = null;
                try
                {
                    svcHost = new ServiceHost(typeof(MyMathServiceLib.MyMathService));
                    svcHost.Open();
                    Console.WriteLine("\n\nService is Running  at following address" );
                    Console.WriteLine("\nhttp://localhost:9001/MyMathService");
                    Console.WriteLine("\nnet.tcp://localhost:9002/MyMathService");
                }
                catch (Exception eX)
                {
                    svcHost = null;
                    Console.WriteLine("Service can not be started \n\nError Message [" + eX.Message + "]");
                }

                if (svcHost != null)
                {
                    Console.WriteLine("\nPress any key to close the Service");
                    Console.ReadKey();
                    svcHost.Close();
                    svcHost = null;
                }
            }
        }
    }

    explanation

    as you can see from the above code,

    • Class Library is simply hosted inside a ServiceHost object, as all the End Points are defined in App.config file.

    Part 3: Creating the Windows Client

    Now when our service is self hosted and running, let us write a client Application, which can use this Service. lets’ Create a Console based application, for the sake of simplicity, name it MyMathServiceClient, as shown below.

    image

    newly created project will be added to the work space.

    Generating proxy and config file.

    Before we write the Client application, we need to generate the proxy for our Service, open a command prompt in Administrator mode. and execute the Host Application.

    image

    Open another command prompt, switch to the folder, where client project has been created, and generate the proxy and configuration file using the SVCUtil.exe utility. as shown below.

    image

    add both these files to the client project.

    Calling the Service

    Now write the code to use the proxy and call the service, here is the listing of the Program.cs.

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

    namespace MyMathClient
    {
        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);

                    MyMathServiceClient mathClient1 = new MyMathServiceClient("NetTcpBinding_IMyMathService");

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

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

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

                    dblResult = mathClient1.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);

                    MyMathServiceClient mathClient2 = new MyMathServiceClient("BasicHttpBinding_IMyMathService");

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

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

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

                    dblResult = mathClient2.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 + "]");
                }
            }
        }
    }

    and here is the output.

    image

    Part 4: Creating a Web Client

    Create a New Web Site, using the File –> New Web Site as shown below. name it MyMathWebClient.

    image

     

    Click OK,

    Now we need to add a reference to our service to this web site, right click on the newly created web site project, and click on Add Service Reference. a dialog box will pop up.

    • Enter any (TCP/HTTP) endpoint address in the address box.
    • Name the namespace, where Service Proxy will be added to the project.

    image

    click OK, some changes will be made to your web.config file, and App_WebReferences folder will be added, under which a Service reference "MathServiceReference" will be added.

    • a Service reference named MyMathServiceReference will be added in Service References node of the client project.
    • some changes will be made to the web.config file,

    let’s analyse the changes made to web.config. open the web.config, you will find the changes at the end of the file, see the <system.ServiceModel> section, as shown below.

    <system.serviceModel>
        <bindings>
          <basicHttpBinding>
            <binding name="BasicHttpBinding_IMyMathService"/>
          </basicHttpBinding>
          <netTcpBinding>
            <binding name="NetTcpBinding_IMyMathService"/>
          </netTcpBinding>
        </bindings>
        <client>
          <endpoint address="
    http://localhost:9001/MyMathService&quot; binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_IMyMathService" contract="MathServiceReference.IMyMathService" name="BasicHttpBinding_IMyMathService"/>
          <endpoint address="net.tcp://localhost:9002/MyMathService" binding="netTcpBinding" bindingConfiguration="NetTcpBinding_IMyMathService" contract="MathServiceReference.IMyMathService" name="NetTcpBinding_IMyMathService">
            <identity>

              <!—this value will be different on your machine –!>
              <userPrincipalName value="PRAVEEN-WIN7\BriskTech"/>
            </identity>
          </endpoint>
        </client>
    </system.serviceModel>

    Open the Main page of the site (Default.aspx page), double click, that will create the Page_Load  handler for the Page, lets call our service in the page load handler.

    protected void Page_Load(object sender, EventArgs e)
        {
            double dblX = 10000.0 ;
            double dblY = 2000.0 ;
            Response.Write ( "<p>Value 1 : " + dblX.ToString ( "F2"));
            Response.Write ( "<br>Value 2 : " + dblY.ToString ( "F2"));

            try
            {
                Response.Write("<p>Using TCP Binding");

                MathServiceReference.MyMathServiceClient mathProxy1 = new MathServiceReference.MyMathServiceClient("NetTcpBinding_IMyMathService");

                double dblResult = mathProxy1.Add(dblX, dblY);
                Response.Write("<br>Calling Add >>  X : " + dblX.ToString("F2") + "  Y : " + dblY.ToString("F2") + " Result : " + dblResult.ToString("F2"));

                dblResult = mathProxy1.Subtract(dblX, dblY);
                Response.Write("<br>Calling Add >>  X : " + dblX.ToString("F2") + "  Y : " + dblY.ToString("F2") + " Result : " + dblResult.ToString("F2"));

                dblResult = mathProxy1.Multiply(dblX, dblY);
                Response.Write("<br>Calling Add >>  X : " + dblX.ToString("F2") + "  Y : " + dblY.ToString("F2") + " Result : " + dblResult.ToString("F2"));

                dblResult = mathProxy1.Divide(dblX, dblY);
                Response.Write("<br>Calling Add >>  X : " + dblX.ToString("F2") + "  Y : " + dblY.ToString("F2") + " Result : " + dblResult.ToString("F2"));

                Response.Write("<p>Using Basic HTTP Binding");

                MathServiceReference.MyMathServiceClient mathProxy2 = new MathServiceReference.MyMathServiceClient("BasicHttpBinding_IMyMathService");

                dblResult = mathProxy2.Add(dblX, dblY);
                Response.Write("<br>Calling Add >>  X : " + dblX.ToString("F2") + "  Y : " + dblY.ToString("F2") + " Result : " + dblResult.ToString("F2"));

                dblResult = mathProxy2.Subtract(dblX, dblY);
                Response.Write("<br>Calling Add >>  X : " + dblX.ToString("F2") + "  Y : " + dblY.ToString("F2") + " Result : " + dblResult.ToString("F2"));

                dblResult = mathProxy2.Multiply(dblX, dblY);
                Response.Write("<br>Calling Add >>  X : " + dblX.ToString("F2") + "  Y : " + dblY.ToString("F2") + " Result : " + dblResult.ToString("F2"));

                dblResult = mathProxy2.Divide(dblX, dblY);
                Response.Write("<br>Calling Add >>  X : " + dblX.ToString("F2") + "  Y : " + dblY.ToString("F2") + " Result : " + dblResult.ToString("F2"));
            }
            catch (Exception eX)
            {
                Response.Write("There was an error while calling Service <p> <b>[" + eX.Message + "] </b>");
            }
        }

    Build and run the web site, following output will be displayed.

    image

    that’s all folks.

    September 9, 2013 Posted by | CodeProject, SOA, WCF | , , | Leave a comment

    Creating Simple WCF Service with IIS Hosting

    In this post I am going to explain how to create a simple WCF Service, with IIS Hosting and consume it, there will be two parts of the project.

    • A WCF Service (MyMathService.svc): will be hosted by IIS.
    • Client Application (MyMathClient.exe): Client Application which will use this service.

    let’s start with creating the WCF Service.

    Creating the WCF Service

    Start the Visual Studio 2010 in administrator Mode (Run as Administrator), and click File->New->Web Site. Select the project template ‘WCF Service’ and Location as http. This will directly host the service in IIS and click OK. select the template “WCF Service”. 

    image

    Now let’s analyse the project created by wizard for us, and do little house keeping, before we write any code.

    • IService.cs (the interface definition of the service)
    • Service.cs (the implementation of the service).
    • web.config (Service configuration file).
    • Service.svc

    Assumption

    We are going to create a Service as per following assumptions.

    • Service will be named as "MyMathService" (MyMathService.svc)
    • Interface will named as IMyMathService (IMyMathService.cs)
    • Service will be implemented as MyMathService (MyMathService.cs)
    • Service will implement Http endpoint at Port 8001.
    • Service will also expose IMetadataExchange endpoint.  IMetadataExchange interface Exposes methods used to return metadata about a service.

    Little house keeping. (just to keep things organized)

    • Rename IService.cs as IMyMathService.cs.
    • Rename Service.cs as MyMathService.cs.
    • Rename Service.svc as MyMathService.svc, and change its content as

    Previous Content

    <%@ ServiceHost Language="C#" Debug="true" Service="Service" CodeBehind="~/App_Code/Service.cs" %>

    New Content

    <%@ ServiceHost Language="C#" Debug="true" Service="MyMathService" CodeBehind="~/App_Code/MyMathService.cs" %>

    Defining Configuration File

    Changing web.config file.

    Below is the listing of the configuration file, generated by visual studio wizard, as we know, that Services are defined in <System.servicemodel> section. Below is the listing of the configuration file, generated by visual studio wizard.  let’s define a service and endpoint, see the highlighted part.

    <?xml version="1.0"?>
    <configuration>

                     <configSections>
            . . . . . . . . .
             . . . . . . . . .
          </configSections>

        <system.web>
         . . . . . . . . .
         . . . . . . . . .
        </system.web>

      <system.serviceModel> 
    <services>
         <service name="MyMathService" behaviorConfiguration="svcBehavior">  
            
    <endpoint address=http://localhost:8001/MyMathService/MyMathService.svc" binding="wsHttpBinding" contract="IMyMathService">

              <identity> <dns value="localhost"/> </identity>
    </endpoint>

          <endpoint address ="MEX" binding ="mexHttpBinding" contract="IMetadataExchange" /> 
       </service> 
    </services>

       <behaviors>
          <serviceBehaviors>
            <behavior  name="svcBehavior">
              <!– To avoid disclosing metadata information, set the value below to false and remove the metadata endpoint above before deployment –>
             <serviceMetadata httpGetEnabled="true"/>
              <!– To receive exception details in faults for debugging purposes, set the value below to true.  Set to false before deployment to avoid disclosing exception information –>
              <serviceDebug includeExceptionDetailInFaults="true"/>
            </behavior>
          </serviceBehaviors>
        </behaviors>
       
        <serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
      </system.serviceModel> 

    <system.codedom>
             . . . . . . . . .
             . . . . . . . . .
          </system.codedom>

       <system.webServer>
             . . . . . . . . .
             . . . . . . . . .
       </system.webServer>    

       <runtime>
             . . . . . . . . .
             . . . . . . . . .
       </runtime>

     
    </configuration>

    Explanation

    See the highlighted text, as you can see service defines two endpoints, one is first end point defines an address(http://localhost:8001/MyMathService), binding(HTTP) and contract (IMyMathService), the other is MEX endpoint, MEX endpoint are defined to import Metadata (to be used by client applications) from a Service using utility SVCUtil.exe

    <endpoint address ="http://localhost:8001/MyMathService&quot; binding ="basicHttpBinding" contract="IMyMathService" />

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

    In the serviceBehaviors, a behavior named "svcBehavior" defines following attributes about the service.

    <serviceMetadata httpGetEnabled="true"/>

    metadata information about the service can disclosed through a get request (if this is false, SVCUtil.exe would not be able to obtain metadata information about the service).

    <serviceDebug includeExceptionDetailsInFaults="true"/>

    can be set true, to receive exception details in faults for debugging purposes, should be set to false, before deployment, to avoid disclosing exception information.

    Defining Service Interface (IMyMathService.cs)

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

    [ServiceContract]
    public interface IMyMathService
    {
        [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

    as one can see, there is one service contract (IMyMathService), and 4 operation contract to perform 4 basic math operations.

    Implementing Service (MyMathService.cs)

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

    public class MyMathService : IMyMathService
    {
        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));
        }
    }

    explanation

    here simply those 4 methods has been implemented.

    Creating Client for the Service

    Add a new project to the workspace, say a Console based application, name it MyMathClient, for instance.

    image

    newly created project will be added to the work space.

    Generating proxy and config file.

    right click on the newly created (client) project, and click on "Add Service Reference", as shown below.

    image

    a dialog box will be displayed as shown below. in the address bar type the address of the service,

    image

    Click OK,

    • A app.config (client side configuration) will be added to the project.
    • a Service reference named MyMathServiceProxy will be added in Service References node of the client project.

    Calling the Service

    Now write the code to use the proxy and call the service, here is the listing of the Program.cs.

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

    namespace MyMathClient
    {
        class Program
        {
            static void Main(string[] args)
            {
                double dblX = 2000.0 ;
                double dblY = 100.0 ;
                double dblResult = 0 ;
                MyMathServiceProxy.MyMathServiceClient mathClient = new MyMathServiceProxy.MyMathServiceClient();

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

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

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

    and here is the output.

    image

    Creating a web Client for the Service

    Add a new web site to the workspace, name it MyWebMathClient, for instance as shown below.

    image

    Add Service reference of the Math Service to the newly created website, as shown below

    image

    Adding this Service Reference, made a change in the web.config file of the Client Web site, if you open the web.config, you will find the following changes, at the bottom of the file.

    <system.serviceModel>
            <bindings>
                <wsHttpBinding>
                    <binding name="WSHttpBinding_IMyMathService" />
                </wsHttpBinding>
            </bindings>
            <client>
                <endpoint address="
    http://localhost/MyMathService/MyMathService.svc"
                    binding="wsHttpBinding" bindingConfiguration="WSHttpBinding_IMyMathService"
                    contract="MyMathServiceReference.IMyMathService" name="WSHttpBinding_IMyMathService">
                    <identity>

                       <!—this name can be different on your machine. –!>
                        <servicePrincipalName value="host/PRAVEEN-WIN7" />
                    </identity>
                </endpoint>
            </client>
        </system.serviceModel>

     

    Open the Page_load Handler of the Default.aspx in the newly created (Client Web Site) and put the code as shown below.

    protected void Page_Load(object sender, EventArgs e)
       {
           double dblX = 10000.0;
           double dblY = 2000.0;
           double dblResult = 0;
           Response.Write("<p>Value 1 : " + dblX.ToString("F2"));
           Response.Write("<br>Value 2 : " + dblY.ToString("F2"));

           try
           {
               Response.Write("<p>Using WS HTTP Binding");

               MyMathServiceReference.MyMathServiceClient mathProxy2 = new MyMathServiceReference.MyMathServiceClient("WSHttpBinding_IMyMathService");

               dblResult = mathProxy2.Add(dblX, dblY);
               Response.Write("<br>Calling Add >>  X : " + dblX.ToString("F2") + "  Y : " + dblY.ToString("F2") + " Result : " + dblResult.ToString("F2"));

               dblResult = mathProxy2.Subtract(dblX, dblY);
               Response.Write("<br>Calling Add >>  X : " + dblX.ToString("F2") + "  Y : " + dblY.ToString("F2") + " Result : " + dblResult.ToString("F2"));

               dblResult = mathProxy2.Multiply(dblX, dblY);
               Response.Write("<br>Calling Add >>  X : " + dblX.ToString("F2") + "  Y : " + dblY.ToString("F2") + " Result : " + dblResult.ToString("F2"));

               dblResult = mathProxy2.Divide(dblX, dblY);
               Response.Write("<br>Calling Add >>  X : " + dblX.ToString("F2") + "  Y : " + dblY.ToString("F2") + " Result : " + dblResult.ToString("F2"));
           }
           catch (Exception eX)
           {
               Response.Write("There was an error while calling Service <p> <b>[" + eX.Message + "] </b>");
           }
       }

    and when you run this web site, you will get following output.

    image

    September 6, 2013 Posted by | .NET, CodeProject, SOA, WCF | , , , | 1 Comment

    Introduction to WCF

    What is WCF

    In a nutshell, WCF is a SDK for developing and deploying services on Windows, although services can be build even without WCF.  Developing services with WCF is easier. WCF is Microsoft’s implementation of a set of industry standards defining service interactions, type conversions, marshaling, and management of various protocols. consequently, WCF provides interoperability between services.    

    What is SOA

    SOA is the next evolutionary step in the long journey from functions to object to components to service orientated applications. Service orientation (SO) is an abstract set of methodologies, principles and best practices for building service oriented applications(SOA). Service oriented application aggregates services into a single logical application.

    What is a Service

    A service is a unit of functionality exposed to the world. the services can be local or remote, can be deployed by multiple parties using any technology.

    What is a WCF Service

    WCF Service typically exposes metadata describing the available functionality and possible ways of communicating with service. Metadata is published in a predefined, technology-neutral way such as using WSDL (Web Services Description Language) over HTTP-GET or an industry standard for metadata exchange over any protocol. WCF services may communicate over a variety of transports (not just HTTP). A non WCF client can import the metadata to its native environment as native types. similarly, a WCF client can import the metadata of a non WCF service and  consume it as a native CLR class/interface. 

    Anatomy of WCF Service/ A B C of WCF Service

    (A) Address

    in WCF every service is associated with a unique address. the address provides two important elements:

    • location of the service: the location portion of the address indicates the name of the target machine, site, or network; a communication port, pipe or queue; and an optional specific path, or URI. URI can be any unique string, such as service name or a globally unique identifier (GUID).

    • transport protocol/transport scheme: WCF supports following transport protocols/schemes.

      • HTTP/HTTPS

      • TCP

      • IPC

      • Peer network

      • MSMQ

      • Service Bus

    the address always have the following format

    [base address] / [optional URI]

    the base address is always in the format

    [transport]://[machine or domain][optional port]

    here are few examples of  sample addresses.

    http://localhost:8001                              

    http://localhost:8001/MyService           

    net.tcp://localhost:8002/MyService      

    net.pipe://localhost/MyPipe                   

    net.msmq://localhost/MyQueue             

    here are few examples of  sample addresses.

    (B) Binding

    A binding is merely, a consistent, canned set of choices regarding the transport protocol, message encoding, communication pattern, reliability, security, transaction propagation and interoperability. A single service can support multiple bindings on separate addresses. Binding allow you to use the same service logic over drastically different plumbing. all you need to do is determine the target scenario for your service, and WCF makes a correct multidimensional decision for you regarding all the aspects of the communication. One can use the WCF-provided bindings out of the box, can tweak their properties, or can write their own custom bindings from scratch. WCF defines 5 frequently used bindings.

    • Basic Binding: offered by the BasicHttpBinding class, basic binding is designed to expose a WCF services as a legacy ASMX web service, so that old clients can work with the new services. this binding makes your service look like a legacy web service that communicates over basic web service profile. when used by clients, this enables new WCF clients to work with old ASMX services.
    • TCP Binding:  offered by NetTcpBinding class, TCP binding uses TCP for cross machine communication on the intranet. it supports variety of features, including reliability, transactions and security, and is optimized for WCF to WCF communication, hence it requires both the client and the service to use WCF.
    • IPC Binding: offered by NetNamedPipeBinding class, IPC binding uses named pipes as a transport for same-machine communication. it is the most secure binding, since it can not accept calls outside of the machine boundary. It is also the most performant binding, since IPC is a lighter protocol than TCP.
    • Web Service (WS) Binding: offered by WSHttpBinding class, the WS binding uses HTTP or HTTPS for transport and offers a variety of features(such as reliability, transactions, and security) over the Internet, all using the WS-* standards.
    • MSMQ Binding: offered by the NetMsmqBinding class, the MSMQ binding uses MSMQ for transport and offers support for disconnected queued calls. 

    each frequently used binding  uses different transport scheme and encoding as shown below.

    Name Transport Encoding Interoperable
    BasicHttpBdinding HTPP/HTTPS TEXT, MTOM Yes
    NetTcpBinding TCP Binary No
    NetNamedPipeBinding IPC Binary No
    WsHttpBinding HTTP/HTTPS TEXT, MTOM Yes
    NetMsmqBidning MSMQ Binary No

    having text based encoding typically enables a WCF service (or client) to communicate over HTTP with any other service (or client), regardless of its technology and across firewalls. Binary encoding over TCP, IPC or MSMQ yields the best performance, but it does so at the cost of interoperability because it mandates the WCF-WCF communication.

    Choosing a Binding

    When choosing a binding the following diagram can help.

    image

    in addition to the five frequently used bindings described above, WCF defines 6 infrequently used bindings. these bindings are each designed for a specific target scenario, which normally can not be used out of that scenario. 

    • WS dual binding:  offered by WSDualHttpBinding class, similar to the WS binding, except it also supports bidirectional duplex communication from the service to the client. This binding nothing more than just two WsHttpBinding bindings wired up against each other to support callbacks, as there is no industry standard for setting up callback, thus WSDualHttpBinding is not interoperable.
    • Peer Network binding: offered by NetPeerTcpBinding class, this uses peer networking as a transport: the peer network enabled client and services all subscribe to the same grid and broadcast messages to it.
    • Fedrated WS binding: offered by WSFedrationHttpBinding class, a specialization of WS binding that offers support for federated security, not a main stream binding. 
    • Fedrated WS 2007 binding: offered by WS2007FedrationHttpBinding class, this is an update of a WSFedrationHttpBinding.
    • MSMQ integration binding: offered by MSMQIntegrationBinding class, this is the analogous queued –world binding to the basic binding. The integration binding converts WCF messages to and from MSMQ messages and is designed to interoperate with legacy MSMQ clients.
    • WS 2007 binding: offered by WS2007HttpBinding class, this binding is derived from the WSHttpBinding class; it adds support for the emerging co-ordination standard and updates for the transaction, security, and reliability standards.

    (C) Contracts

    A WCF contract is a platform neutral and standard way of describing what a service does. in WCF all services expose contracts. WCF defines 4 types of contracts.

    • Service Contracts: Describes which related operations can be tied together as a single functional unit that the client can perform on the service. Service contracts can be defined as shown below.

      [ServiceContract]
      public interface IMyService
      {

      }

    • Operation Contract: An operation contract defines the parameters and return type of an operation.

      [ServiceContract]
      public interface IMyService
      {
         [OperationContract]
          double MultiplyNumber(double dblX, double dblY );   
         [OperationContract]
          double DivideNumber(double dblX, double dblY );  
      }

    • Data Contracts: Defines which data types are passed to and from the service. WCF defines implicit contracts for built in types such as int and string, but you can easily define explicit opt in data contracts for custom types.

      [DataContract]
      public class ErrorInfo
      {
          private string m_strError;
          private int m_nErrorCode;
         
         [DataMember]
          public string ErrorMessage
          {
              get { return m_strError; }
              set { m_strError = value; }
          }

         [DataMember]
          public int ErrorCode
          {
              get { return m_nErrorCode }
              set { m_nErrorCode = value; }
          }
      }

    • Fault Contracts: Defines which errors are raised by the service and how the service handles and propagates errors to the clients. Fault Contract provides documented view for error occurred in the service to client. This helps to easily identity the error.

      [ServiceContract]
      public interface IMyService
      {
          [OperationContract]
          double MultiplyNumber(double dblX, double dblY );   
          [OperationContract]
         [FaultContract(typeof(DevideByZeroException))]
          double DivideNumber(double dblX, double dblY );  
      }

    • Message Contracts: Allow the service to interact directly with messages. Message contracts can be types or un typed and are useful in interoperability cases when another party has already dictated some explicit (mostly proprietary) message format. 

      [MessageContract]
      public class ErrorDetails
      {
          [MessageHeader]
          public int  ErrorCode;
         
         [MessageBodyMember]
          public string ErrorMessage;
         [MessageBodyMember]
          public DateTime ErrorTime;
      }

    Hosting

    The WCF service class can not exist in void. every WCF service must be hosted in a Windows process called host process. a single host process can host multiple services, and the same service type can be hosted in multiple services.

    Hosting Environment and Supported Bindings

    Hosting Environment

    Supported protocol

    Windows console and form application

    http, net.tcp, net.pipe, net.msmq

    Windows service application (formerly known as NT services)

    http, net.tcp, net.pipe, net.msmq

    Web server IIS6

    http, wshttp

    Web server IIS7 – Windows Process Activation Service (WAS)

    http, net.tcp, net.pipe, net.msmq

     

    A summary of hosting options and supported features.

    Feature

    Self-Hosting

    IIS Hosting

    WAS Hosting

    Executable Process/ App Domain

    Yes

    Yes

    Yes

    Configuration

    App.config

    Web.config

    Web.config

    Activation

    Manual at startup

    Message-based

    Message-based

    Idle-Time Management

    No

    Yes

    Yes

    Health Monitoring

    No

    Yes

    Yes

    Process Recycling

    No

    Yes

    Yes

    Management Tools

    No

    Yes

    Yes

    Endpoints

    Every service is associated with an address that defines where the service is, a binding defines how to communicate with the service, and the contract defines what the service does. WCF formalizes this relationship in the form of endpoint. The endpoint is the coalition of address, binding and contract. So every endpoint must have all 3 elements, and the host exposes the endpoint. logically, endpoint is the interface of the service to the outside world. Every service must expose at least one business Endpoint, and each endpoint has exactly one contract. All endpoints on a service has unique addresses, and a single service can expose multiple endpoints.

    Configuration of Endpoint

    It is important to note nothing in the service code has to do with its service points. and they are always external to the service code. you can configure endpoints either administratively or programmatically.

    Administrative Endpoint Configuration

    <system.ServiceModel>
        <services>
            <service name="MyService">
                <endpoint address="http://localhost:8000/MyService&quot; binding ="wsHttpBinding" contract="IMyContract">
                <endpoint address="http://localhost:8001/MyService&quot; binding ="netTcpBinding" contract="IMyContract">
                <endpoint address="http://localhost:8002/MyService&quot; binding ="netTcpBinding" contract="IOtherContract">
            </service>
        </services>
    </system.ServiceModel>

    Programmatic Endpoint Configuration

    ServiceHost host = new ServiceHost(typeof(MyService));
    Binding httpBinding = new WSHttpBinding();
    Binding tcpBinding = new NetTcpBinding();
    host.AddServiceEndPoint( typeof (IMyContract), httpBinding, "
    http://localhost:8000/MyService");
    host.AddServiceEndPoint( typeof (IMyContract), tcpBinding, "net.tcp://localhost:8001/MyService");
    host.AddServiceEndPoint( typeof (IMyOtherContract), tcpBinding, "net.tcp://localhost:8002/MyService");

    Default Endpoints

    If the service host does not define any end points (neither in configuration nor programmatically) but does provide at least one base address. WCF will by default add endpoints to the service. These are called default endpoints. WCF will add an endpoint per base address per contract, using the base address as the endpoint’s address. for HTTP WCF will use the basic binding. Note that default binding will affect the default endpoints. WCF will also name the endpoint by concatenating the binding name and the contract name.

    consider the following example.

    [ServiceContract]
    interface IMyContract {
        //Interface Body
        … 
    }

    [ServiceContract]
    interface IMyOtherContract {
        //Interface Body
        … 
    }

    class MyService : IMyContract, IMyOtherContract {
        //Class Body
        … 
    }

    //    Host Application
    Uri httpbaseAdr = new Uri ( "http://localhost:8000/");
    Uri tcpbaseAdr = new Uri ( "net.tcp://localhost:9000/");
    Uri pipebaseAdr = new Uri ( "net.pipe://localhost/");
    ServiceHost host = new ServiceHost ( typeof (MyService), httpbaseAdr, tcpbaseAdr, pipebaseAdr);
    host.Open ();

    Assuming that no config file is used to define any additional endpoints, WCF will add these default endpoints, as they were defined in config file.

    <service name="MyService">


        <endpoint name="BasicHttpBinding_IMyContract" address="http://localhost:8000/" binding="basicHttpBinding" contract="IMyContract" />
        <endpoint name="NetTcpBinding_IMyContract" address="net.tcp://localhost:9000/" binding="netTcpBinding" contract="IMyContract" />
        <endpoint name="NetNamedPipeBinding_IMyContract" address="net.pipe://localhost/" binding="netNamedPipeBinding" contract="IMyContract" />
       
        <endpoint name="BasicHttpBinding_IMyOtherContract" address="http://localhost:8000/" binding="basicHttpBinding" contract="IMyOtherContract" />
        <endpoint name="NetTcpBinding_IMyOtherContract" address="net.tcp://localhost:9000/" binding="netTcpBinding" contract="IMyOtherContract" />
        <endpoint name="NetNamedPipeBinding_IMyOtherContract" address="net.pipe://localhost/" binding="netNamedPipeBinding" contract="IMyOtherContract" />

    </service>   

    Metadata Exchange

    By default, the service will not publish its metadata. publishing you service meta data will need some efforts. , luckily, the host can do it for you, if you instruct it to do so, as host knows everything, it needs to know about your service and its endpoints.

    Enabling Metadata exchange administratively

    <system.ServiceModel>
    <services>
        <service name="MyService" behaviorConfiguration="MEXGET">
            <host>
            <baseAddresses>
                <add baseAddress = "net.pipe://localhost/" /> 
            </baseAddresses>
            
             <endpoint address="MEX" binding="mexNamedPipeBinding" contract="IMetadataExchange"/>

            <!– Here we have defined the complete address, we do not use the base address. -–>
            <endpoint address="http://localhost:8000/MEX" binding="mexHttpBinding" contract="IMetadataExchange"/>

            <endpoint address="net.tcp://localhost:8001/MEX" binding="mexTcpBinding" contract="IMetadataExchange"/>

        </service>   
    </services>
    </system.ServiceModel>

    in the vast majority of cases, a MEX endpoint always has the same 3 elements: the contract is always IMetadataExchange, the binding is always the reserved binding element, and the only variable is the address (and that too, just the base address)

    Adding MEX endpoints programmatically

    like any other endpoint, you can also add metadata exchange endpoint programmatically before opening the host. WCF does not offer a dedicated binding type for the metadata exchange endpoint. you can use the following methods of MetadataExchangeBindings  class defined in System.ServiceModal.Description

    Uri tcpbaseAdr = new Uri ( “net.tcp://localhost:9000/”);
    ServiceHost host = new ServiceHost ( typeof (MyService), tcpbaseAdr );
    ServiceMetadataBehavior metaBehave ;
    metaBehave = host.Description.Behaviors.Find<ServiceMetadataBehavior>();
    if ( metaBehave == null )
    {
        metaBehave = new ServiceMetadataBehavior();
        host.Description.Behaviors.Add (metaBehave);
    }
    Binding binding = MetadataExchangeBindings.CreateMexTcpBinding ();
    host.AddServiceEndpoint (typeof (IMetadataExchange), binding, "MEX");
    host.Open ();

    you can also add a MEX endpoint using the standard MEX endpoint.

    ServiceHost host = new ServiceHost (typeof(MyService));
    host.Description.Behaviors.Add(new ServiceMetadataBehavior());
    EndpointAddress address = new EndpointAddress ( "
    http://localhost:8000/MEX");
    ServiceEndPoint endpoint = new ServiceMetadataEndPoint(address) ;
    host.AddServiceEndpoint (endpont);
    host.Open ();

    Client side Programming

    To invoke operations on a service, a client first need to import the service contract to client’s native (environ.) .

    Client side Calling Service using Code

    Just define the interface definition in the client application in a file, say IMyService.cs.

    [ServiceContract]
    public interface IMyService
    {
       [OperationContract]
        double AddNumber(double dblX, double dblY );   
       [OperationContract]
        double MultiplyNumber(double dblX, double dblY );   
    }

    assuming that Service is hosted at address “http://localhost:8001/MyService/”

    To invoke operations on a service, if you do not want to use a configuration file. a client need to create everything by hand.

    private void CallServiceFunctionOnHTTP ()
    {
        string strAdr =
    http://localhost:8001/MyService/”;
        Uri adrbase = new Uri(strAdr);
        BasicHttpBinding httpb = new BasicHttpBinding();
        ChannelFactory<IMyService> channelFactory = new ChannelFactory<IMyService>(httpb);
       
        strEPAdr = "
    http://localhost:8001/MyService" ;
        EndpointAddress ep = new EndpointAddress(strEPAdr );
        IMyService myObj = m_ChannelFactory.CreateChannel(ep);
        double dblResult = 0;
        dblResult = chatObj.AddNumber(dblX, dblY);
    }

    If the same Service also exposes another EndPoint (let say at "net.tcp://localhost:9000/MyService” ) then you can call the service, through this end point as shown below.

    private void CallServiceFunctionOnTCP ()
    {
        string strAdr =
    net.tcp://localhost:9000/MyService/”;
        Uri adrbase = new Uri(strAdr);
        NetTcpBinding tcpb = new NetTcpBinding();
        ChannelFactory<IMyService> channelFactory = new ChannelFactory<IMyService>(tcpb);
       
        strEPAdr = "
    net.tcp://localhost:9000/MyService
    " ;
        EndpointAddress ep = new EndpointAddress(strEPAdr );
        IMyService myObj = m_ChannelFactory.CreateChannel(ep);
        double dblResult = 0;
        dblResult = chatObj.AddNumber(dblX, dblY);
    }

    August 20, 2013 Posted by | .NET, CodeProject, SOA, WCF | , , , , | Leave a comment