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
Collapse | Copy Code
[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
Collapse | Copy Code
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
Collapse | Copy Code
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
Collapse | Copy Code
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.
Collapse | Copy Code
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.
Collapse | Copy Code
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:
Collapse | Copy Code
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);
strEPAdr = "net.tcp://" + strServer + ":" +
nPort.ToString() + "/MathService";
break;
case "HTTP":
BasicHttpBinding httpb = new BasicHttpBinding();
channelFactory = new ChannelFactory<IMathService>(httpb);
strEPAdr = "http://" + strServer + ":" +
nPort.ToString() + "/MathService";
break;
}
ep = new EndpointAddress(strEPAdr);
IMathService mathSvcObj = channelFactory.CreateChannel(ep);
double dblResult = 0;
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;
}
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)
{
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
Collapse | Copy Code
BasicHttpBinding httpb = new BasicHttpBinding();
channelFactory = new ChannelFactory<IMathService>(httpb);
for TCP Bindng
Collapse | Copy Code
NetTcpBinding tcpb = new NetTcpBinding();
channelFactory = new ChannelFactory<IMathService>(tcpb);
EndPoint address for HTTP binding
Collapse | Copy Code
strEPAdr = "http://" + strServer + ":" + nPort.ToString() + "/MathService";
EndPoint address TCP binding
Collapse | Copy Code
strEPadr = strEPAdr = "net.tcp://" + strServer + ":" + nPort.ToString() + "/MathService";
Create a channel through a specified end point address:
Collapse | Copy Code
ep = new EndpointAddress(strEPAdr);
IMathService mathSvcObj = channelFactory.CreateChannel(ep);
Invoke methods through the channel and display output:
Collapse | Copy Code
double dblResult = 0;
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;
}
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();
Points of interest
- No configuration file.
- There is no proxy generation using SvcUtil.
Output
Running Service with TCP endpoint
Client
Running Service with HTTP endpoint
Note: While running the Service in HTTP mode, you need to open the command prompt in Administrator mode.
Client
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.