Praveen Katiyar' Blog

Learning along the way . . . .

Understanding Late binding

in simplest words, late binding is a technique, in which you can create an instance of a given type, and can invoke its methods, at runtime, without having knowledge of its existence at the compile time.

System.Activator class plays a pivotal role in late binding process.

let us understand, it by creating

  • simple a Class library “MyMathLib” which exposes few methods let say Add, Subtract, Multiply.
  • Console bases client program , that will use this library using the conventional (early binding) method.
  • Console bases client program , that will use this library using the late binding method.

Creating the class library

Create a Class library, let’s name it “MyMathLib” choosing the “Class Library” template from the project wizard. Default project is created with a public class Class1 defined in Class1.cs.

Little house keeping.

rename the Class1 as “MathClass” using the refactor menu.

rename the class1.cs as MathClass.cs

Now define methods Add, Subtract and Multiply for MathClass as shown below.  as shown below.

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

namespace MyMathLib
{
    public class MathClass
    {
        public double Add(double dblX, double dblY)
        {
            return (dblX + dblY);
        }

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

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

 

Compile and Build library.

Creating the Application, that will use this library

Create a Console application, let’s name it “TestEarlyBinding” choosing the “Console Application” template from the project wizard. Default project is created.

Little house keeping.

Add Reference of MathLib class library project, created earlier.

Write code in the main function to use this library as shown below.

using System;
using System.Text;

using MyMathLib;

namespace TestEarlyBinding
{
    class Program
    {
        static void Main(string[] args)
        {
            double dblResult = 0;
            double dblX = 200; double dblY = 200;
           
            Console.WriteLine("Tesing early binding");  
           MathClass mathObj = new MyMathLib.MathClass ();
           
            dblResult = mathObj.Add(dblX, dblY);
            Console.WriteLine("Adding {0} and {1} results to {2}", dblX, dblY, dblResult);

            dblResult = mathObj.Subtract (dblX, dblY);
            Console.WriteLine("Adding {0} and {1} results to {2}", dblX, dblY, dblResult);
           
            dblResult = mathObj.Multiply(dblX, dblY);
            Console.WriteLine("Adding {0} and {1} results to {2}", dblX, dblY, dblResult);
        }
    }
}

Compile and build and here is the output.

image

till now, it was default way, we used to do things, but this post is about late binding.

so using the same class library  with late binding, let’s create one more console based project let’s  call it TestLateBidning.

using System;
using System.Text;
using System.Reflection;

namespace TestLateBidning
{
    class Program
    {
        static void Main(string[] args)
        {
            double dblResult = 0;
            double dblX = 200; double dblY = 100;

            Console.WriteLine("Tesing late binding");
           
Assembly assem = null;
           
Console.WriteLine("Loading MyMathLib assembly . . .");
            try
            {
                assem = Assembly.Load("MyMathLib");
            }
            catch (Exception eX)
            {
                Console.WriteLine("Error while loading MyMathLib Error [{0}] \n\nMyMathLib.dll not found in application Folder", eX.Message);
                return;
            }

            Console.WriteLine("Creating Instance of MathClass object . . .");
            Type mathType = assem.GetType("MyMathLib.MathClass");
            object mathObjLB = Activator.CreateInstance(mathType);

            Console.WriteLine("Invoke method [Add] . . .");
            MethodInfo miAdd = mathType.GetMethod("Add");
            dblResult = (double)miAdd.Invoke(mathObjLB, new object[] { 100, 200 });
            Console.WriteLine("Adding {0} and {1} results to {2}", dblX, dblY, dblResult);

            Console.WriteLine("Invoke method [Subtract] . . .");
            MethodInfo miSub = mathType.GetMethod("Subtract");
            dblResult = (double)miSub.Invoke(mathObjLB, new object[] { 100, 200 });
            Console.WriteLine("Subtracting {0} and {1} results to {2}", dblX, dblY, dblResult);

            Console.WriteLine("Invoke method [Multiply] . . .");
            MethodInfo miMul = mathType.GetMethod("Multiply");
            dblResult = (double)miMul.Invoke(mathObjLB, new object[] { 100, 200 });
            Console.WriteLine("Multiply {0} and {1} results to {2}", dblX, dblY, dblResult);
        }
    }
}

Explanation:

Assembly assem = null;
Console.WriteLine("Loading MyMathLib assembly . . .");
try
{
    assem = Assembly.Load("MyMathLib");
}
catch (Exception eX)
{

    . . . . .    
    return

}

Assembly class represents an assembly, which is a valid building block of a common language runtime.

Load method simply loads an assembly, whose name has been provided.

Type mathType = assem.GetType("MyMathLib.MathClass");
object mathObjLB = Activator.CreateInstance(mathType);

Activator class contains methods to create types of object of the specified type, locally or remotely. 

MethodInfo miAdd = mathType.GetMethod("Add");

GetMethod is member function of Type class, that searches the given method name, and returns the MethodInfo, MethodInfo class discovers the attributes of a method access its metadata.

dblResult = (double)miAdd.Invoke(mathObjLB, new object[] { 100, 200 });(mathObjLB, new object[] { 100, 200 });

Invoke method of MethodInfo class simply calls the method, using the required parameters.

similarly other methods (Subtract, Multiply) are discovered and invoked.

build and execute.

image

oops, there was an error, as it could not found the MyMathLib.dll in the test application executable (TestLateBinding.exe) folder.

just copy the MyMathLib.dll to the test application’s executable folder and execute the client again. and you will see what was expected.

image

 

Hope it useful.

April 27, 2014 Posted by | .NET, CodeProject, General | , , , | Leave a comment

Introduction to Device Driver Development

Merely connecting the hardware device to the computer and providing it power is not sufficient for a device to function in a desired manner. In a nutshell, the device driver is a software interface to hardware connected to a computer. It is a trusted part of the operating system. From the user application point of view, the device driver is the abstract connection to the device, and the user application should access the device without having to worry about how the hardware must be controlled,  so a device driver is a piece of software that enables a particular hardware device to function properly. Device driver is so named because it drives the operations of the device, in other words a device driver instructs the hardware device to perform different operations. Almost all externally or internally connected hardware device requires a device driver for its functioning.

  • Device driver program although is a separate piece of program is considered to be a part/extension of  the Operating System.
  • The Operating System becomes dynamically extensible by the use of device drivers.
  • Newer hardware can be added to the system, and installing their respective drivers, without recompiling the operating system, on the other way around, drivers can be updated to run on a newer version of OS.
  • Since device drivers becomes an extension to the OS, it is highly trusted, can perform operations, which are normally not allowed through application programs.
  • In a multitasking environment like windows, hardware devices are a shared resources between all the running programs. allowing hardware access from application program can result in conflict if multiple applications try to access the same device at the same timey.
  • Device driver can not to be integral part of the Operating System. because
      • Each hardware device internally operates in a different way than another.
      • New hardware devices continue to evolve at a rapid rate, so it will not be practical to modify, recompile, and redistribute the OS again and again to accommodate the functionality of a new hardware.
      • By keeping the code for controlling hardware devices in a separate module the operating system remains independent of any particular hardware devices.

Types of device driver

From Functional point of view

Every device driver program does not controls a hardware device. From the functional point of view device driver can be classified in to following categories.

  • A File System driver never interacts with with any physical hardware, The FSD (File System Driver is responsible for handling I/O requests from application, converting these requests to a low level form(Cylinder, Head, Sector request) and passing it to another driver. this another driver actually interacts with the physical storage device.  
  • Another type of driver programs is to write very low level system utilities, because they are trusted part of the system, so they can do things, which are normally not possible in application level code. these activities may include reading/writing directly with I/O Ports, device registers etc.
  • System utilities in particular, sometimes have an additional module in the form of a driver that performs much of the low level activities on the behalf of the utility. System Monitoring utilities are the best example of this category, for example.
      • File Monitor, Disk Monitor, and other similar utilities, normally have two modules, one is a driver at its core, and the other is a GUI application which polls this driver and displays the desired activities.
  • An antivirus program may take help of a driver program to accomplish many of its activities like.
        • Scanning memory areas of all the running processes including the Operating System.
        • providing auto protect feature, where by any program that is being scanned for a virus, if a virus is found, the antivirus program stops the execution of the said program.
  • Driver code can also be used to emulate physical hardware devices. for example there are lot of commercial/freeware/shareware utilities available that can emulate a CDROM device.

From the lens of operating System

with the advent of Windows XP (NT based) OS as a main consumer windows, driver for 16 bit windows and MS-DOS does not make any sense. So when we talk about driver we simply means a Ring–0 driver unless and until explained otherwise. which can be different from functional point of view as explained above

image

Kernel mode (Ring-0) drivers further can be divided into several categories as shown below.

image

Kernel-mode drivers further can be divided into following subcategories.

  • A PnP driver is a kernel-mode driver that understands the Plug and Play protocols of Windows XP. 
  • WDM driver is a PnP driver that understands power-management protocols and is source compatible with both Windows 98/Me and Windows 2000/XP. These type of drivers can be further subcategorized to.
      • Class drivers are those who manage a device belonging to some well-defined class of device.
      • Mini drivers are those, who supply vendor-specific help to a class driver.
      • Monolithic Function drivers are those which embody all the functionality needed to support a hardware device.
      • Filter drivers are those who “filter” the I/O operations for a particular device in order to add or modify behavior.
  • File system drivers implement the standard PC file system model (which includes the concepts of a hierarchical directory structure containing named files) on local hard disks or over network connections. These, too, are kernel-mode drivers.
  • Legacy device drivers are kernel-mode drivers that directly control a hardware device without help from other drivers. This category essentially includes drivers for earlier versions of Windows NT.

What is required for writing a device driver

Drivers for windows use a variety of architectures, and involve understanding of lot of complicated concepts including.

      • Learn hardware details, one must has to to know the details of his hardware environment, memory mapping/addressing, device registers and mapping, accessing them in an architecture independent manner.
      • Learning driver architecture– drivers for windows follow a very specific architecture, that is a very well defined interface for an application to communicate with a driver, again when the Operating System needs to communicate with the driver it follows a different packet driven model.
      • Learn Key Windows Concepts, basically device driver is an extension to the Operating System, writing device drivers needs a lot of details of windows internals.

DDK/WDK

there are several DDK/WDK are available form Microsoft’s website depending version of windows.  the latest version was Windows Driver Kit 7600 (WDK 7600). These kits are available free of cost.

S.N. DDK Version Supported Operating Systems.
1 3790 Windows 2000, Windows XP
2 7600 XP, Vista, Windows Server 2003, Windows 7, Windows Server 2008

Books & online reference.

  • First and foremost, Programming the Windows Driver Model by Walter Oney, can be said to be the Bible for driver development.
  • Programming the Windows Driver for Windows 2000, by Art Baker and Guy Lazano.
  • Windows NT Device Driver by Peter G. Viscarola.
  • Windows Internals by Mark Russinovich

there are several DDK/WDK are available form Microsoft’s website depending version of windows.  the latest version was Windows Driver Kit 7600  (WDK 7600). These kits are available free of cost.

April 11, 2014 Posted by | DDK, Driver Development | , , | Leave a comment

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

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