Introduction
In one of my recent interviews, I was asked about how to use a WCF Service without Adding
a Service Reference or Proxy in an ASP.NET project. At that time I only knew to
do it by adding a service reference from Visual Studio or using svcutil.exe to generate
a proxy class. I did not have
an answer at that time (of course I passed the interview). But after sometime I figured out the solution, this solution might benefit others.
Background
Say for example you are working on a website project and you are using a WCF service to get data or for other reasons and
your client’s
requirements change so rapidly; to accommodate a new requirement, you might need to add
a new function in your WCF service but you can’t afford to add a new service
reference or regenerate the proxy class or stop your application and restart again. But what if I tell you without going through all of this, you can still modify
a WCF Service to add a new function and you don’t need to interrupt your running application.
Using the code
Let us understand this solution in detail. There are two ways you can create
an ASP.NET user interface project in Visual Studio. You create an ASP.NET Web Application or
ASP.NET Website. There are plenty of articles out on the internet about ASP.NET Web Application
vs. ASP.NET Website,
so I am not go in to much details, but just know that websites are dynamically compiled and
the source is distributed with them and Web Applications can be
compiled into a single DLL and deployed. So there are pros and cons when using
an ASP.NET Web Application or ASP.NET Web Site. The solution I
am recommending here is only going to work with an ASP.NET Website and while you will come to know why it only works with
an ASP.NET Website. So let
us get started…
- We are going to create three different projects: ISampleService, WCFSampleService and SampleServiceHost.
- ISampleService: we will keep all Interfaces separate in this project.
- WCFSampleService: we will define all functions that our service will expose.
- SampleServiceHost: this will host our WCFSampleService.
- Create a blank solution SampleServiceSolution and add a WCF Service Library project ISampleService to it. Once the project
is created, delete the Service1.cs and App.config files from it, because we just want interfaces in this project.
Following is the code for IService1
. We will define a Service Contract. For
the purposes of this article, I am going to have just
a simple Service Contract GetMessage
and later we will add more Service contracts to
IService1
.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;
namespace SampleService
{
[ServiceContract]
public interface IService1
{
[OperationContract]
string GetMessage();
}
}
- Add a second WCF Service Library project WCFSampleService to the solution SampleServiceSolution.
Once the project is created, delete the IService1.cs and App.config files from it. Add
a Reference ISampleService to it.
[Note: both projects use the namespace SampleService
.]
Following is the code for Service1
. Now we will implement the Service Contract that we defined in
IService1
.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;
namespace SampleService
{
public class Service1 : IService1
{
public string GetMessage()
{
return "Hello World";
}
}
}
- Since our WCFSampleService is ready, now we are going to create a host application to host this service.
For this article's
purpose we are going to host our service on IIS and for that we are going to create
a new Website project SampleServiceHost and add to it to SampleServiceSolution.
It gets a little tricky here, I am going to show you step by step.
In Visual Studio, right click on SampleServiceSolution, click Add, click New Web Site.
Select project template WCF Service, click OK.
This will create a WCF Service Website SampleServiceHost in the SampleServiceSolution solution.
We already have created our WCFSampleService in a separate project. We will refer to it in Service.svc.
We will delete the IService.cs and Service.cs files from the App_Code folder. Right click SampleServiceHost, Add Reference WCFSampleService to it.
Open the Service.svc file from SampleServiceHost and modify it so it looks like this:
<%@ ServiceHost Language="C#" Debug="true" Service="SampleService.Service1""%>
Now check to see how we did it so far, let us set SampleServiceHost as the startup project and the Service.svc
file to set as the Startup Page and then run SampleServiceHost.
If everything is OK, your screen should look like:
Now let us modify our Web.Config of SampleServiceHost as follows. You can modify your System.ServiceModel
section according to your project needs.
Such as your binding, your endpoint address, etc.
//
// Web.Config of SampleServiceHost
//
="1.0"="UTF-8"
<configuration>
<system.web>
<compilation debug="true" targetFramework="4.0" />
</system.web>
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="BasicHttpBinding_IService1" closeTimeout="00:01:00"
openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00"
allowCookies="false" bypassProxyOnLocal="false" hostNameComparisonMode="StrongWildcard"
maxBufferSize="65536" maxBufferPoolSize="524288" maxReceivedMessageSize="65536"
messageEncoding="Text" textEncoding="utf-8"
transferMode="Buffered" useDefaultWebProxy="true">
<readerQuotas maxDepth="32" maxStringContentLength="8192"
maxArrayLength="16384" maxBytesPerRead="4096" maxNameTableCharCount="16384" />
<security mode="None">
<transport clientCredentialType="None" proxyCredentialType="None" realm="" />
<message clientCredentialType="UserName" algorithmSuite="Default" />
</security>
</binding>
</basicHttpBinding>
</bindings>
<services>
<service behaviorConfiguration="mexBehavior" name="EnterpriseService.Service1">
<endpoint address="http://localhost/SampleService/Service.svc"
binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_IService1"
contract="EnterpriseService.IService1" />
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="false" />
</behavior>
<behavior name="mexBehavior">
<serviceMetadata httpGetEnabled="true" />
</behavior>
</serviceBehaviors>
</behaviors>
<serviceHostingEnvironment multipleSiteBindingsEnabled="false" />
</system.serviceModel>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true" />
<defaultDocument>
<files>
<add value="Service.svc" />
</files>
</defaultDocument>
</system.webServer>
</configuration>
Now it is time to deploy our SampleServiceHost website on IIS, so it will be available to the outside world. I deployed mine on http://localhost/SampleService/Service.svc on IIS.
Once you deploy SampleServiceHost, it is time to test our WCFSampleService.
We are going create a separate Web site project TestClientForSampleService.
Add Reference ISampleService to it.
Next we are going to modify the Web.Config of the TestClientForSampleService Website as follows:
//
// modify Web.Config of TestClientForSampleService
// add servicemodel section
//
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="BasicHttpBinding_IService1" closeTimeout="00:01:00"
openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00"
allowCookies="false" bypassProxyOnLocal="false" hostNameComparisonMode="StrongWildcard"
maxBufferSize="65536" maxBufferPoolSize="524288" maxReceivedMessageSize="65536"
messageEncoding="Text" textEncoding="utf-8" transferMode="Buffered"
useDefaultWebProxy="true">
<readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"
maxBytesPerRead="4096" maxNameTableCharCount="16384" />
<security mode="None">
<transport clientCredentialType="None" proxyCredentialType="None"
realm="" />
<message clientCredentialType="UserName" algorithmSuite="Default" />
</security>
</binding>
</basicHttpBinding>
</bindings>
<client>
<endpoint address="http://localhost/SampleService/Service.svc"
binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_IService1"
contract="SampleService.IService1" name="BasicHttpBinding_IService1" />
</client>
</system.serviceModel>
Next we are going create a ServiceClient
class under the App_Code folder as follows:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.ServiceModel;
using System.Configuration;
public class ServiceClient<T> : ClientBase<T> where T : class
{
private bool _disposed = false;
public ServiceClient()
: base(typeof(T).FullName)
{
}
public ServiceClient(string endpointConfigurationName)
: base(endpointConfigurationName)
{
}
public T Proxy
{
get { return this.Channel; }
}
protected virtual void Dispose(bool disposing)
{
if (!this._disposed)
{
if (disposing)
{
if (this.State == CommunicationState.Faulted)
{
base.Abort();
}
else
{
try
{
base.Close();
}
catch
{
base.Abort();
}
}
_disposed = true;
}
}
}
}
Next we are going to add two ASP.NET Label
s on default.aspx as follows:
<%@ Page Title="Home Page" Language="C#"
MasterPageFile="~/Site.master" AutoEventWireup="true"
CodeFile="Default.aspx.cs" Inherits="_Default" %>
<asp:Content ID="HeaderContent" runat="server" ContentPlaceHolderID="HeadContent">
</asp:Content>
<asp:Content ID="BodyContent" runat="server" ContentPlaceHolderID="MainContent">
<h2>
Welcome to ASP.NET!
</h2>
<div>
<asp:Label ID="Label1" runat="server" /><br />
<asp:Label ID="Label2" runat="server" /><br />
</div>
</asp:Content>
Next in code-behind, Default.aspx.cs page on the Page_Load
event, we are going to use our ServiceClient
class to call our WCFSampleService.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using SampleService;
public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
using (ServiceClient<IService1> ServiceClient = new ServiceClient<IService1>("BasicHttpBinding_IService1"))
{
this.Label1.Text = ServiceClient.Proxy.GetMessage();
}
}
}
Now it is time to run our TestClientForSampleService Website. Your screen should look like this.
Now the real magic comes here, say for example if my client wants me to add a new function that displays
a customer’s address, all I need to do is modify my ISampleService
to add a new service contract and modify my WCFSampleService to add a new function that implements
a new service contract. In my TestClientForSampleService website,
I don’t need to add a ISampleService
reference again, that is because
the TestClientForSampleService website automatically gets a changed referenced DLL,
thus without modifying the ISampleService
reference to it, I still get
a new function available to my TestClientForSampleService Website.
So let us modify ISampleService
to add a new service contract
GetAddress
as follows and then build the project.
namespace SampleService
{
[ServiceContract]
public interface IService1
{
[OperationContract]
string GetMessage();
[OperationContract]
string GetAddress();
}
}
Modify WCFSampleService to implement a new service contract and then build the project:
namespace SampleService
{
public class Service1 : IService1
{
public string GetMessage()
{
return "Hello World";
}
public string GetAddress()
{
return "123 New Street, New York, NY 12345";
}
}
}
Finally build the SampleServiceHost project. Next build the TestClientForSampleService project, you don’t need to change the ISampleService
reference to it. Then modify the default.aspx.cs file of the TestClientForSampleService website as follows:
protected void Page_Load(object sender, EventArgs e)
{
using (ServiceClient<IService1> ServiceClient =
new ServiceClient<IService1>("BasicHttpBinding_IService1"))
{
this.Label1.Text = ServiceClient.Proxy.GetMessage();
this.Label2.Text = ServiceClient.Proxy.GetAddress();
}
}
Points of Interest
What I have tried to do here is to show you how you can loosely couple your WCF service.
History
- 1 June 2012: First version.