Introduction
Windows communication foundation (WCF) provides a good way to host a service contract over the network. Also build in capabilities like self hosting and different protocol support (TCP, HTTP etc) make it a perfect choice. User has great control over the service and its features.
WCF requires 3 things, one server, one host and one client. Now a simple call mechanism is easy to implement. But in today’s complex business and software models, it is hard to keep each and every functionality at single stage. A system have multiple hop of call over the network, and server may be calling another server internally as a client, and even that another server can be acting as client for another server. So if every pass is through WCF contract, there are technical issues with call after first stage. One such system with a small example is explained here, with certain issues that you may encounter a simple solution.
Assumption: you must have a basic knowledge of WCF contract, and self hosting.
Problem statement: The call to second server fails when first server acts as a client in self hosting model.
Background
Let first understand a two level architecture. Suppose we chose self hosting over basic Http. Server will generate a configuration file, which needs to be consumed by client to be able to communicate with server via host.
Suppose server side, we have 2 components, one is server side code, placed in a ‘dll’. And a host which is an EXE and referring the ‘dll’, every function in that dll is an exposed method that can be called by client. Client side consists of an EXE, and ‘App.config’ file consumed by that EXE. (This configuration is required to bind the connection at runtime). When client call Server, Server ‘dll’ will internally calls another self hosted WCF service. For this purpose, server will need to consume configuration of second server.
WCF call hopping
Using the code - A Real Life Example
Let’s start with an example. There is a 2 level WCF architecture. At one end, there is a client, which is calling a WCF function via self hosting app, in turn Server is calling another WCF service (again via self hosting app). The client app is simple application concatenating 2 strings.
Client App
The client is calling a WCF service to do the task. The client code and contract is as follows:
private void btnClick_Click(object sender, EventArgs e)
{
try
{
ContractConcat.ConcatClient obj = new ContractConcat.ConcatClient();
tbxResult.Text = obj.Concatenate(tbxFirst.Text, tbxSecond.Text);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
<client>
<endpoint address="http://127.0.0.1:4040/ServerCode" binding="wsHttpBinding"
bindingConfiguration="WSHttpBinding_IConcat" contract="ContractConcat.IConcat"
name="WSHttpBinding_IConcat">
<identity>
<userPrincipalName value="AnantBeriwal-PC\Anant Beriwal" />
</identity>
</endpoint>
</client>
I have created a Host with name WCFHost hosting a contract. The Contract Being Hosted is called IConcat. ‘App.Config’ of host is having following entry to facilitate proper communication with Server.
<service name="WCFServer.ServerCode">
<endpoint address="http://127.0.0.1:4040/ServerCode" binding="wsHttpBinding" contract="WCFServer.IConcat">
</endpoint>
</service>
As you can see, it has reference of a ConcatClient. ConcatClient is connecting to a server via a self hosting server. The Server contract is a project named WCFServer, and its ServerCode file and Binding interface are as follows:
namespace WCFServer
{
public class ServerCode : IConcat
{
public string Concatenate(string strfirstString, string strSecondString)
{
ContractValidity.CheckForValidityClient obj = new ContractValidity.CheckForValidityClient();
bool ResultFirst = obj.CheckForValidityOfString(strfirstString);
bool ResultSecond = obj.CheckForValidityOfString(strSecondString);
if (ResultFirst && ResultSecond)
{
return strfirstString + strSecondString;
}
else
{
return "Not a Valid Input";
}
}
}
}
namespace WCFServer
{
[ServiceContract]
interface IConcat
{
[OperationContract]
String Concatenate(string strfirstString, string strSecondString);
}
}
If you see the code part carefully, you will find out that This Server code is calling a IValidity WCF service, so actually there are 2 hops for client when it calls Concatenate. The Second server is called WCFHopServer and a contract IValidity is being exposed by it. Code sample is as follow.
namespace WCFHopServer
{
public class HopServerCode : ICheckForValidity
{
public bool CheckForValidityOfString(string strString)
{
for (int i = 0; i < strString.Length; i++)
{
if (char.IsLetter(strString[i]))
{
continue;
}
else
{
return false;
}
}
return true;
}
}
}
namespace WCFHopServer
{
[ServiceContract]
interface ICheckForValidity
{
[OperationContract]
bool CheckForValidityOfString(string strString);
}
}
Its respective host will have an entry like this to facilitate the communication. (Actually I used the same EXE to expose both services, as I was on the same machine. But it is not affecting the hop mechanism anyway.)
<service name="WCFHopServer.HopServerCode">
<endpoint address="http://127.0.0.1:4040/HopServerCode" binding="wsHttpBinding" contract="WCFHopServer.ICheckForValidity">
</endpoint>
</service>
Final architecture is as follows:
WCF Call Hopping
Now we will run the application. First of all, host is being run. When host is lunched, it will host the both services contracts (This is done for ease for showcase; both contract can be hosted separately).
WCFHost
Now when ‘Concatenate’ button is clicked, what do you expect? Should it work fine or we missed out something. Wait, an error raised up.
The reason is simple. WCFServer do not have WCFHopServer Configuration. Why so, because we never provide it. Actually it is not as easy as it looks. If you observe carefully, WCFServer is already having a configuration. That is of IConcat, provided by WCFHost (a ‘Dll’ do not possess any configuration by itself, Parent EXE provides this to it). As WCFHost do hosting a service IConcat. It has one app.config, which it passes to WCFServer. Now how WCFSever will consume Service Configuration of WCFHopServer?
There are 3 ways to do so.
- Hard code it inside the WCFServer code. Binding class can do this thing for you. But it is not desirable. For some comfort, you can place all configurations in some other file and read the values by yourself, then initiate binding class. But code is an overhead.
- Second if you do not want to write too much code, CofigurationManager class will work most of the work for you (Reading and managing app.config, specially reading nodes of configuration file itself). But still you have to do code of binding by yourself and call server by yourself. Also maintaining another app.config will be headache.
- You can take a third route, for which this article is all about. We do not need to add any code, leave everything to configuration file. We are going to use this technique.
The problem lies because host cannot provide anything about configuration of second server. But we can modify app.config to do so. For classification, in App.config, there are 2 types of contract.
- <Services> define server side contract
- <Client> Define client side contract
We can keep both parts in a single configuration file. Let’s look a client part, which we requires. It is a <Client> tag having details of IValidity Contract.
<client>
<endpoint address="http://127.0.0.1:4040/HopServerCode" binding="wsHttpBinding"
contract="ContractValidity.ICheckForValidity">
</endpoint>
</client>
Add this tag to App.Config of WCFHost. Now WCFHost is working as client as well as server as per configuration file. Final configuration is like this.
<configuration>
<system.web>
<compilation debug="true" />
</system.web>
<system.serviceModel>
<services>
<service name="WCFServer.ServerCode">
<endpoint address="http://127.0.0.1:4040/ServerCode" binding="wsHttpBinding" contract="WCFServer.IConcat">
</endpoint>
</service>
<service name="WCFHopServer.HopServerCode">
<endpoint address="http://127.0.0.1:4040/HopServerCode" binding="wsHttpBinding" contract="WCFHopServer.ICheckForValidity">
</endpoint>
</service>
</services>
<client>
<endpoint address="http://127.0.0.1:4040/HopServerCode" binding="wsHttpBinding"
contract="ContractValidity.ICheckForValidity">
</endpoint>
</client>
</system.serviceModel>
</configuration>
We used the basic property of EXE that it will provide configuration to underlying layer of dll. Now dll was seeking a client contract, but it was receiving a server contract. Hence problem persisted. With new WCFHost and its configuration, Client will not throw any exception and results will be shown.
Final result
That’s all folks. I hope it was helpful, easy and time saving solution.
Sample code can be downloaded from here- SampleCode.zip