Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C#4.0

Creating and consuming a simple WCF Service without using configuration files

4.94/5 (28 votes)
14 Oct 2013CPOL5 min read 89.3K   4.8K  
This article explains how to create a simple WCF Service, and host it according to supplied parameters, and a client to use this service.

Introduction

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

Background

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

Using the code 

In this article I have used three projects:

  • WCF Service (WCFMathLib.dll): Actual Service logic, which defines a Service Contract interface, OperationContract, and implements them, and exposes a 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 a ServiceContract and a few operations available through the Service, as shown below. Let's call it IMathInterface.cs.

To create this project you can simply take a Class Library Project, choosing from the 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 the interface in this file also. Below is the listing of both files.

Definition of Service Contract

C#
// 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

C#
// 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 specifies that the said operation is exposed by the service; the service defines the parameters and return type of an operation. As we can see, there is nothing special here, just a Service Contract definition and its implementation.

Second Part: WCF Service (ConWCFMathHost.exe)

A host application has been developed as console based application, which hosts 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 specifying the port value.

Hosting Service using HTTP Binding

C#
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 Binding    

C#
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 + "]");
    }
} 

As there is no config file, all we have to do by hand (see the bold part) basically is do the following things:

Code Description

Create Desired binding (TCP/HTTP)  

ServiceMetadataBehavior controls the publication of the service metadata and associated information. Although we can omit the part shown in bold, as far as this project is concerned, 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 useful when we are generating the proxy class and config files from the service using the SVCUtil.exe utility. 

Third Part: Client (ConWCFMathClient.exe)

The client application has been developed as a console based application, passing parameters through the command line in the following form. You need to supply six 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 specifying 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.

C#
// Listing of IMathService.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);
    }
}    

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 method:

C#
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
C#
BasicHttpBinding httpb = new BasicHttpBinding();
channelFactory = new ChannelFactory<IMathService>(httpb); 
for TCP Bindng 
C#
NetTcpBinding tcpb = new NetTcpBinding();
channelFactory = new ChannelFactory<IMathService>(tcpb);                       
EndPoint address for HTTP binding
C#
strEPAdr = "http://" + strServer + ":" + nPort.ToString() + "/MathService";
EndPoint address   TCP binding
C#
strEPadr = strEPAdr = "net.tcp://" + strServer + ":" + nPort.ToString() + "/MathService";   

Create a channel through a specified end point address:

C#
// Create End Point
ep = new EndpointAddress(strEPAdr);
                
// Create Channel
IMathService mathSvcObj = channelFactory.CreateChannel(ep);                    

Invoke methods through the channel and display output:

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

Points of interest

  • No configuration file. 
  • There is no proxy generation using SvcUtil. 

Output

Running Service with TCP endpoint

Image 14

Client

Image 15

Running Service with HTTP endpoint

Note: While running the Service in HTTP mode, you need to open the command prompt in Administrator mode.

Image 16

Client  

Image 17

Points of Interest   

In this article i have described, all three contract types in a very simple manner, i have also explained, the suitability of each contract type. when you should use I hope you will enjoy reading it as much as I enjoyed writing it. 

thanks alot for reading. 

History 

Initial version.

License

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