Introduction
Windows Communication Foundation (WCF) is a technology combined with the features of XML Web Services and .NET Remoting, along with some improvements. This article is a comparison of WCF with Web Services and .NET Remoting.
Interoperability
.NET Remoting works in a homogenous environment. A consuming application is also required to be in .NET.
Web Services are platform and language independent, and don't care about the consuming application. But it has restrictions of the HTTP protocol. Performance-wise, they are considered slow.
WCF can be hosted under the Windows environment, but it can be utilized by clients of different languages and different platforms.
Protocol Utilization
.NET Remoting applications can use the HTTP, TCP, and SMTP protocols.
XML Web Services use SOAP, i.e., XML via HTTP.
WCF, along with all these protocols, can use named pipes and MSMQ as well.
Session Management
Web Services don't keep session values by default. A new instance of Web Service is created on each call. The EnableSession
attribute is used with each web method to implement a session. By default, it is false
, so we define it explicitly. This attribute allows a Web Service to access session values directly from the HttpContext.Current.Session
property. For example:
[WebMethod(EnableSession = true)]
public string KeepSessionValue(bool SendValue)
{
string strSessionValue;
if (SendValue)
{
rSessionValue = Session["newValue"].ToString();
}
else
{
if (null==Session["oldValue"])
Session["oldValue"] = System.DateTime.Now.ToLongTimeString();
strSessionValue = Session["oldValue"].ToString();
}
return strSessionValue;
}
The problem with Web Services is that a Web Service proxy doesn't retain values, so if you just set EnableSession
, it will not work. The System.Net.CookieContainer
class is required for use in the client side to keep cookies. In the following example, a new instance of CookieContainer
is created if the session is new; otherwise, it will persist the original cookie value.
protected void button2_Click(object sender, EventArgs e)
{
Service1 obj = new Service1();
CookieContainer objContainer = new CookieContainer();
if (null == Session["cookieContainer"])
{
obj.CookieContainer = objContainer;
Session["cookieContainer"] = objContainer;
}
else
{
objContainer = (CookieContainer)Session["cookieContainer"];
obj.CookieContainer = objContainer;
}
Label1.Text = obj.KeepSessionValue(false);
}
In WCF, sessions are explicitly defined and closed from client side applications, unlike ASP.NET applications where sessions are server initiated. There are two kinds of sessions in WCF: Reliable session which is responsible for transactions, and the other type keeps session values for objects.
We choose endpoint bindings which support session. For example, BasicHttpBinding
does not support sessions. To start, the ServiceContract
attribute's SessionMode
property is set. It can be:
SessionMode.Required
: Session is required to be implemented.SessionMode.Allowed
: Session is allowed to be implemented.SessionMode.NotAllowed
: Session implementation is not allowed. It will not keep the session values.
By default, it is set to Allowed
, so we do not need to set it to Allowed
.
The service behavior can be set to InstanceContextMode.PerSession
, InstanceContextMode.PerCall
, InstanceContextMode.Single
. InstanceContextMode
is used to define the number of instances for each call.
Persession
sets a new instance for each session.PerCall
sets a new instance for each call even for a single user.Single
sets an instance for all calls from all clients. It acts as a singleton; if an instance does not exist, it is created.
In the example below, I would use a counter variable to increase its value on each call. It will keep the previous value if exists, and increase the counter.
The interface would define the SessionMode
as Allowed
.
[ServiceContract (SessionMode=SessionMode.Allowed)]
public interface IService1
{
[OperationContract()]
int IncreaseCounter();
}
The service will implement this function as follows:
public class Service1 : IService1
{
int Acounter;
public int IncreaseCounter()
{
return Acounter++;
}
}
On the client-side, it will increase the counter on each call:
protected void Button1_Click(object sender, EventArgs e)
{
ServiceReference2.Service1Client obj1 =
new ServiceReference2.Service1Client();
Label1.Text = obj1.IncreaseCounter();
Label1.Text += obj1.IncreaseCounter();
Label1.Text += obj1.IncreaseCounter();
}
We can verify this state management by setting SessionMode
to SessionMode.NotAllowed
.
A session for a user is terminated when its proxy is closed, and if a client wants to explicitly close the proxy, then it can be done using the Dispose()
method.
In .NET Remoting, state management is done in two ways:
- Client activated objects
- Server activated objects or Well-known objects
Well-known objects are further divided into SingleCall and Singleton.
- In SingleCall, on each request by the client, a new remote object is created and there is no state management.
- In Singleton, as its name suggests, objects are instantiated once and share state for all client requests.
Client activated objects (CAO) are stateful objects, and keep values for a single client. An instance variable which is created for a client keeps its values on retrieval.
We will create a remoting class to be utilized by the client, a client application, and a listner (server application) for the client to act as a bridge between both the client and remote application.
For our sample, we will use console applications for the Remoting class and to invoke it.
- Create a class file and name it ARemotingClass.cs. Here is its code:
using System;
namespace ARemotingNameSpace
{
public class ARemotingClass : MarshalByRefObject
{
public ARemotingClass()
{
Console.WriteLine("Hi i m a remoting class");
}
public int Add(int i, int j)
{
return i+j;
}
public int Multiply(int i, int j)
{
return i * j;
}
}
}
- Run C:\WINDOWS\Microsoft.NET\Framework\<<your installed framework>>\csc /t:library /debug /r:System.Runtime.Remoting.dll ARemotingClass.cs.
It will generate a DLL file for the C# file in the current directory.
- Create a console application for the server to implement remoting. Add the above DLL reference to it. Add a reference for
System.Runtime.Remoting.Channels.Http
. This application will act as a host which will listen on any port number which we define in the code. We can define either a TCP or an HTTP channel in .NET Remoting applications. Then, we will register the channel. To register the client activated object on the server, RemotingConfiguration.RegisterActivatedServiceType
is used. Following is the code for the class file:
using System;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Http;
using ARemotingNameSpace;
namespace ARemotingNameSpace
{
public class ServerClass
{
public static void Main(string[] args)
{
ChannelServices.RegisterChannel(new HttpChannel(90));
RemotingConfiguration.ApplicationName = "Calculate";
RemotingConfiguration.RegisterActivatedServiceType(typeof(ARemotingClass));
System.Console.WriteLine("Press enter key to close");
System.Console.ReadLine();
}
}
}
- Create a web application as a client application to use this Remoting service. This application will instantiate and invoke the client activated object. Almost in a similar way, we will register a client channel. It should be either TCP or HTTP, based on the server channel.
Then, we will register the remotable class using RemotingConfiguration.RegisterActivatedClientType
.
using System;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Http;
using ARemotingNameSpace;
public partial class _Default : System.Web.UI.Page
{
ARemotingClass objRemotingClass = new ARemotingClass();
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
HttpChannel ObjChannel = new HttpChannel();
ChannelServices.RegisterChannel(ObjChannel);
RemotingConfiguration.RegisterActivatedClientType(typeof(ARemotingClass),
"http://localhost:90/Calculate");
}
}
protected void Button1_Click(object sender, EventArgs e)
{
Label1.Text = "Result of addition is " + objRemotingClass.Add(2, 2) +
" and result of multiplication is " + objRemotingClass.Multiply(5, 2);
}
}
A SingleCall object loses state and is garbage collected. For Singleton and Client Activated Objects, we can implement Lease Based Lifetime explicitly. .NET has a built-in Leased Based Manager; it marks the objects for deletion once not in use. It can be implemented using code and a configuration file.
Exception Handling
In ASP.NET Web Services, unhandled exceptions are returned to the client as SOAP <Fault>
elements in XML format. Web Services can throw an exception, and the message of the exception can be seen once it is deserialized on the client side. This can be achieved by using the SoapException
class which is provided for this purpose to throw and catch SOAP exceptions.
Inside a Web Service, we throw exception details by mentioning the error message, type of error, actor (page which caused the error), and inner exception. For example:
string strError = "a custom error message";
SoapException ex =
new SoapException(strError,SoapException.ClientFaultCode);
throw ex;
On the client side, it can be accessed this way:
catch (SoapException soapExc) {
lblExceptionMessage.Text = soapExc.Message;
}
Unlike ASP.NET applications, Global.asax cannot be used to catch errors on the application level in Web Services.
WCF allows us to hide the details of an error and to show only the required information. It handles errors in both ways: Exception objects and SOAP Faults.
Faults are further categorized as:
In declared faults, operations are declared with the [FaultContract]
attribute.
Here is an example of using a FaultContract
in WCF:
- Inside the interface, define a
DataContract
which would be used as a type for the FaultContract
. Here, I define a custom class, ConnectionError
.
[DataContract]
public class ConnectionError
{
[DataMember]
public string strErrorMessage="Could not connect to database";
}
- Define an operation contract with the attribute of
FaultContract
of a type which is defined above.
public interface IService1
{
[FaultContract(typeof(ConnectionErrorFault))]
[OperationContract]
DataTable GetDataTable();
}
Inside the DataContract
, define a property which would be used to set a message for the exception.
[DataContract]
public class ConnectionErrorFault
{
[DataMember]
private string m_Reason = string.Empty;
public string Reason
{
get
{
return m_Reason;
}
set
{
m_Reason = value;
}
}
}
- Inside .svc files, implement this function and throw an exception of type
ConnectionErrorFault
.
public DataTable GetDataTable()
{
try
{
DataSet ds;
string connectionString = "Persist Security Info=False;
Integrated Security=SSPI;database=test;server=mypc";
SqlConnection myConnection = new SqlConnection(connectionString);
SqlDataAdapter myAdapter = new SqlDataAdapter();
myAdapter.TableMappings.Add("Table", "table1");
myConnection.Open();
SqlCommand myCommand = new SqlCommand("SELECT * FROM table1", myConnection);
myCommand.CommandType = CommandType.Text;
myAdapter.SelectCommand = myCommand;
ds = new DataSet("table1");
myAdapter.Fill(ds);
return ds.Tables[0];
}
catch (Exception)
{
ConnectionErrorFault fault=new ConnectionErrorFault();
fault.Reason = "Could not connect to database";
throw new FaultException<ConnectionErrorFault>(fault, fault.Reason);
}
}
This code will generate an exception of type ConnectionErrorFault
; it will show a custom message. It will hide other unnecessary details.
- Inside the client code, use the same fault exception to show the appropriate message.
try
{
ServiceReference2.Service1Client obj1 = new ServiceReference2.Service1Client();
GridView1.DataSource = obj1.GetDataTable();
GridView1.DataBind();
}
catch (FaultException<connectionerrorfault> ex)
{
Label1.Text= ex.Reason.ToString();
}
The exception's Reason
property is used to get the value on the client side.
Exceptions in Remoting are, by default, in non-XML format, so those can be caught in normal .NET style. For testing purposes, in my example, I threw an exception from the Remoting class as a string and caught it in client side. Here is a simple code for an exception:
public string AnException()
{
throw new Exception("an error is thrown back");
}
So, when we call it in the client-side, it will catch the exception in the try catch
portion of the client side, and the message will be shown:
try
{
Label1.Text =objRemotingClass.AnException();
}
catch (Exception exp)
{
Label1.Text += "<BR>" + exp.Message;
}
.NET also provides us a more better way to implement exception handling in Remoting applications. We can use custom exceptions by overriding the GetObjectData
method and implementing serialization. I will not go in to the details of it in this article.
Hosting
Hosting for .NET Remoting applications can be done in Windows Forms, console applications, Windows Services, and IIS. If we implement Remoting on IIS, it still cannot take advantage of the built-in security options from there.
Web Services are hosted in IIS by pointing the ASMX file to the virtual directory.
Unlike Web Services and .NET Remoting, WCF provides more options for hosting. It combines old hosting options with new ones like hosting on WAS. WCF applications can also be hosted in the following ways:
- Self hosting
- Windows Service
- IIS and WAS
A. Self Hosting
As the word suggests, a WCF service would be started manually and will run till the time it is set to run. This approach is only feasible for the purposes of testing a service. This can be achieved by accessing the service via a process that can be created using a console application or a Windows Form.
By calling the Open()
and Close()
methods of the host, we can control the execution time of a service.
For example, in a Windows Form, we can call a service in this way:
public static void Main()
{
Uri AddressofService = new Uri ("http://localhost:5850/");
SerivceHost host =
new SerivceHost (typeof (WcfService1), AddressofService);
host.Open( );
host.Close( );
}
In this format of hosting, we have to implement custom security options, and cannot take advantage of the built-in security provided by IIS.
B. Windows Service
A WCF service can be hosted by a Windows Service which would be responsible for activating the service. The Windows Service itself is installed by an installer file. The Start()
and Stop()
methods of the Windows Service are used to control the WCF Service.
Here are the steps to use a WCF service by a Windows Service:
- Create a new Windows Service.
- Add a reference to
System.ServiceModel
. - Add a reference to the WCF Service.
- Inside the
OnStart
event, open the WCF Service. - Inside the
OnStop
even, stop the service.
Here is the code for the Windows Service:
using System.ServiceModel;
using WindowsService1.WCFServiceReference1;
namespace WindowsService1
{
public partial class Service1 : ServiceBase
{
ServiceHost WCFHost;
public Service1()
{
InitializeComponent();
}
protected override void OnStart(string[] args)
{
Type WCFService = typeof(Service1Client);
WCFHost = new ServiceHost(WCFService);
WCFHost.Open();
}
protected override void OnStop()
{
if (WCFHost != null)
WCFHost.Close();
}
}
}
- To add an installer file, right click on the Windows Service Designer and select Add Installer.
- Set the properties like the start type: automatic or manual.
- To run the installer, add a setup project from Setup and Deployment Projects.
- Add the Windows Service as a project output.
- Add a custom action. Select the application folder and then select the primary output from the Windows Service.
Now, you can install the WCF Service as a Windows Service after compilation and running the setup file.
Using the Windows Service approach has a few advantages and disadvantages as well. Once it is installed, its properties can be configured like in a Windows Service. It will start automatically, users who have authority to start the service can be specified etc. On the other hand, it needs an installer file to be run on the hosting machine. It doesn't provide security options like IIS does. Also, if the Windows service doesn't start because of user privileges, then it would not allow the WCF service to be started.
C. IIS and WAS
WCF hosting is supported on IIS versions 5.1 and greater. Windows 2000 came with version 5.0, and WCF cannot be hosted in Win2000. We do not need to create a service host if we host in IIS because this hosting needs the .svc file itself.
We can also take advantage of the built-in security in IIS while using a service. To host under IIS 5.1 and 6.0, the service must use the HTTP protocol.
All session level values are saved in memory in this scenario. IIS recycles the application domains and processes under its control periodically. So a service can show unexpected output at that moment of recycling. This interval time is mentioned under the application pool settings, and the recycling option can be customized.
Windows Activation Service (WAS) was introduced in Windows Vista and Windows Longhorn server machines. The invention of WAS has provided a facility to utilize protocols like TCP, named pipes, and MSMQ. So with IIS 7.0, we can host WCF on any of these protocols provided out of the box with it.