- Download sample 1 - 6.48 KB
- Download sample 2 - 22.4 KB
- Download sample 3 - 111 KB
- Download sample 4 - 111 KB
- Download sample 5 - 110 KB
Introduction
This project intends to demonstrate three different ways a client can consume a WCF service via a proxy. Namely:- VS Generated Proxy
- svcutil Proxy
- Proxy by hand
Background
At the company I work for, we have been using WCF for over a year now, and one of the things we have discovered is that the code in the system generated proxies is frequently hideous, sometimes doesn’t build, and the config files it generates are truly hideous. We ended up doing something similar to the third option I present below.Basic Project
In order to create a nice, simple WCF service to illustrate the different ways of generating proxies, I used VS 2008. In VS 2008, there is an option to create a new project of type ‘WCF Service Library’, which generates almost everything I need. If not using VS 2008, just copy the following code into a new project and resolve the references, and you have a (very) basic WCF service. Removing most of the guff and leaving a single methodHelloWorld()
gives us the following service and config file:ServiceImplementation
public class ProxyServ : IProxyServ
{
public void HelloWorld() { }
}
ServiceContract
[ServiceContract]
public interface IProxyServ
{
[OperationContract]
void HelloWorld();
}
Config
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<services>
<service name="ProxyGeneration.ProxyServ"
behaviorConfiguration="ProxyGeneration.Service1Behavior">
<endpoint address ="" binding="wsHttpBinding"
contract="ProxyGeneration.IProxyServ"/>
<endpoint address="mex" binding="mexHttpBinding"
contract="ProxyGeneration.IProxyServ"/>
<host>
<baseAddresses>
<add baseAddress="http://localhost:8080/ProxyServ"/>
</baseAddresses>
</host>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="ProxyGeneration.Service1Behavior">
<serviceMetadata httpGetEnabled="True"/>
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
</configuration>
You can just run this in VS 2008, and it will generate a host for you
on the fly and allow you to work with that, but I prefer to make my own
hosts.Host
I added a Console project to the solution, and pasted the following bit of code into it. I always use the same bit of code, and tweak it to make it work for each solution.static void Main(string[] args)
{
ServiceProc();
}
private static void ServiceProc()
{
ServiceHost host = new ServiceHost(typeof(ProxyServ));
try
{
Console.WriteLine("WCF Service: Starting Up");
host.Opened += new EventHandler(host_Opened);
host.Open();
Console.WriteLine("Please press any key to close...");
Console.ReadLine();
}
finally
{
if (host.State != System.ServiceModel.CommunicationState.Closed)
{
host.Close();
}
}
}
private static void host_Opened(object sender, EventArgs e)
{
Console.WriteLine("WCF Service: Running");
System.ServiceModel.ServiceHost host = (System.ServiceModel.ServiceHost)sender;
StringBuilder detailBuilder = new StringBuilder();
foreach (Uri baseAddress in host.BaseAddresses)
{
detailBuilder.AppendFormat("\r\nBase Address: {0}", baseAddress.ToString());
}
foreach (ServiceEndpoint endPoint in host.Description.Endpoints)
{
string address = endPoint.Address.ToString();
string bindingName = endPoint.Binding.Name;
detailBuilder.AppendFormat("\r\nEndpoint({0}): {1}", bindingName, address);
}
Console.WriteLine(detailBuilder.ToString());
}
Resolve the references, and then move the app.config from
the service to the host. Now, we can run the console host, and it will
host our WCF service for us, which means we don’t need to run the
service from VS 2008 anymore, we can just run the exe for the console
application.Client
I am going to create a client project for each type of proxy. I am using a Windows Forms client. You can use whatever you want really. Also, you will note that the methodHelloWorld()
doesn’t return anything, that’s OK. As long as it doesn’t throw an error, we know the operation worked.Generating the Proxies
Adding a Service Reference
In VS 2005, you could run the service host, and still add a service reference to another project in the solution. This doesn’t seem to work in my version of VS 2008, annoyingly. So, instead, you have to go to the bin directory of the console host and run it from there. Once it is running, you can add a service reference in your client. VS 2008 gets a rather nicer looking form than you get in VS 2005, which allows you to take a preview of the service and its methods, as well as name it.Something I always try to do is name the proxy something other than the service, it just helps to keep the two separated in my head. Anyways, click OK, and you will end up with a folder called ‘Service References’ and a little world wide web icon. You can delve into the actual code files hidden by VS 2008 via Windows Explorer, and you will find a file called ‘Reference.cs’. Reference.cs is the actual proxy you have just created, but if all you want to do is get your service to work, don’t worry about it, it doesn’t really matter. Using it is about as easy as they come. These lines of code work quite nicely:
private void btnTest_Click(object sender, EventArgs e)
{
ProxyServClient client = new ProxyServClient("WSHttpBinding_IProxyServ");
client.HelloWorld();
}
As far as I can tell, all that adding a service reference (via VS 2008) does is wrap the command line glory of svcutil behind a form which sets the various arguments. It's nice and shiny, but it generates a fair amount of extra guff in the app.config and in the proxy (as does svcutil).svcutil
I am only including this method for completeness sake, there are extensions for VS 2005 which allow you to add a service reference, and it comes natively with VS 2008.Edit: OK, Chris Richner pointed out in the comments that not everyone has access to the Professional versions of VS, and will be using the Express versions of VS. These versions pretty much guarantee you will be using svcutil.
svcutil itself is a damned horror to use. OK, not really, but if you love GUI and hate command line, just skip to the next section. If, however, you really want to know how to use it, read on.
I've renamed the client project to ReferenceClient, and created a similar project called svcutilClient. To access svcutil, open the VS command prompt, and type 'svcutil', and hit Enter for pages and pages of switches and arguments. There are a ton of them, and I am not going into all of them. Check out the MSDN article here, assuming you can ever get the site to load. The following command is run (after running the host):
svcutil.exe "http://localhost:8080/ProxyServ"
/out:"C:\Projects\Testing\ProxyGeneration\svcutilClient\ProxySVC"
/config:"C:\Projects\Testing\ProxyGeneration\svcutilClient\app.config"
I am telling the tool which service to look at (you could also use
the host exe and config to generate this, but this way is less typing),
where exactly I want the generated proxy to be put, and what to call it,
ditto with the config. (Something strange: The default config name is “output.config” which is rubbish, as the proxy generated for you apparently can only ‘see’ config’s called “app.config”. Or at least, that’s what happened to me.)You should see some output, something like this:
Microsoft (R) Service Model Metadata Tool
[Microsoft (R) Windows (R) Communication Foundation, Version 3.0.4506.648]
Copyright (c) Microsoft Corporation. All rights reserved.
Attempting to download metadata from 'http://localhost:8080/ProxyServ' using WS-
Metadata Exchange or DISCO.
Generating files...
C:\Projects\Testing\ProxyGeneration\svcutilClient\ProxySVC.cs
C:\Projects\Testing\ProxyGeneration\svcutilClient\app.config
If you see this, great success! Now, we go to VS, and ‘Add existing
item’ for the generated CS file and config. Same testing code as before
in our lovely form.private void btnTest_Click(object sender, EventArgs e)
{
ProxyServClient client = new ProxyServClient("WSHttpBinding_IProxyServ");
client.HelloWorld();
}
Build it, and we are done!Crafted Proxy
OK, now option 3. Doing it by hand.Yes, it does take a fair whack longer than doing it using either the Service Reference or even the svcutil method; however, you end up with code that is more succinct, and a config that is way cleaner and easier to read, as you will see shortly. This portion is a bit longer than it could have been, I have added a whole extra project called Proxy which compiles into a DLL, which I then reference from another new class called
ProxyClient
. This is not strictly necessary for this project, but it shows the scalability of doing it this way.The idea is to show that when building a WCF service, you can build the proxy as a project in the service (keeping the proxy in the service makes for nice, happy maintenance), and then reference the proxy as a DLL in the consuming project. The config is also a heavily modified one, with only as much as I need to get it to run, which makes it quite a lot easier on the eyes.
So, first, I create a project called Proxy. It has an interface and a class. The interface
IBob
is a subset of the methods defined in the service interface. This allows you to have a proxy which has only
the methods you are going to be using, rather than the entire lot,
whether you want them or not. If you have a very large service, then
multiple proxies allow you to parcel out the functionality you want. The
class ProxyServiceClient
then inherits from ClientBase<IProxyServ>
and our custom interface. We end up with an interface looking like this (I called it IBob
to make it *really* clear which interface is which):public interface IBob
{
void HelloWorld();
}
And the proxy itself looks like this:public class ProxyServiceClient : ClientBase<IProxyServ>, IBob
{
public ProxyServiceClient()
: base()
{ }
public ProxyServiceClient(string endpoint)
: base(endpoint)
{ }
public void HelloWorld()
{
try
{
base.Channel.HelloWorld();
}
catch (Exception exc)
{
//FANTASTIC error handling here. No, really. It must be awesome.
throw new ApplicationException(exc.Message, exc);
}
}
}
If you have taken a look at the generated proxies, you will note this
is very similar, just a bit trimmed down. I haven’t got any of the
attributes, and I am only using two of the four possible constructors.The config file is on the client and looks like so:
<configuration>
<system.serviceModel>
<client>
<endpoint address="http://localhost:8080/ProxyServ"
binding="wsHttpBinding"
contract="ProxyGeneration.IProxyServ"
name="ProxyServHTTP"/>
</client>
</system.serviceModel>
</configuration>
You will note that there is only the ABC, and
nothing else. Everything is the default value, so we don’t need to
explicitly mention it. We then add a reference to the proxy, and make a
test button as above.That’s it. Happy maintainable coding!
http://www.codeproject.com/KB/WCF/CodeProxyGeneration.aspx
No comments:
Post a Comment