Click here to Skip to main content
Click here to Skip to main content

Dynamically Generate a WCF Proxy

By , 8 Jan 2011
 

Contents

Introduction

Most programmers that start with Windows Communication Foundation (WCF) use the provided samples and tutorials from Microsoft. All these samples are based on static generated service references or proxies. When you want to connect from a client to a server, you create a reference from the client to the server project using the Visual Studio Add Service Reference function. Visual Studio then connects to the server, extracts the meta data from the server, and generates a proxy. The client uses this proxy to actually connect and call methods on the server.

Most programmers don't know that there is another way for WCF to generate this proxy. That is dynamically. This dynamic generation of a proxy has a lot of advantages.

Background

As we all know, sometimes during development, an interface of a service changes. The same is possible for the configuration of a service; for example, the port number on which a service listens changes. Now, say that a developer changes the interface or the configuration of a service and somehow he or she forgets to change the client (this has happened to me more than once). With a statically generated proxy, you would notice the error late in the development cycle, probably during the first test.

This scenario has happened to me many times, and led me to thinking that there had to be another way. Wouldn't it be much better to tackle this problem during compilation on your local workstation or during a build on your build server? With dynamically generated proxy, this is possible.

Structuring Your Assemblies

To be able to use the dynamic proxy and its advantages, you have to structure your assemblies or classes somewhat different. Both the client and the server reference and use a separate assembly that contains the interface and the configuration of the WCF service. The picture below shows the idea:

How to divide assemblies to use a dynamic WCF proxy

The assembly called "Interface + Configuration" contains the WCF configuration of the server, including the interface itself. By structuring your assemblies this way, it is not possible to forget changing the client when the interface changes. It simply won't compile!

Using the Code

The source code contains three assemblies, the server, the client, and the interface / configuration assembly. These are contained within a single Visual Studio 2008 solution. When you run the application, both the server and the client will start. The client sends a couple of messages to the server.

Generating the dynamic proxy using WCF is not difficult, the ChannelFactory from WCF makes it possible. The source code to generate a dynamic proxy is shown below.

The Client

EndpointAddress endpointAddress = 
	new EndpointAddress(Configuration.CreateServerUriString());
ChannelFactory<imessagesender> channelFactory =
  new ChannelFactory<imessagesender>(Configuration.CreateBinding(), endpointAddress);
IMessageSender messageSender = channelFactory.CreateChannel(endpointAddress);
messageSender.ShowMessage("Test message");
((IChannel)messageSender).Close();

ChannelFactory uses the interface IMessageSender to create a channel through the method CreateChannel. The interface IMessageSender and Configuration are both from the shared assembly.

The server side also uses this information from the Configuration class. It creates a new ServiceHost and listens on the Uri from the Configuration.

The Server

Server singleServerInstance = new Server();
Uri baseUri = new Uri(Configuration.CreateServerUriString());
ServiceHost serviceHost = new ServiceHost(singleServerInstance, baseUri);
serviceHost.AddServiceEndpoint(typeof(IMessageSender),
            Configuration.CreateBinding(), baseUri);
serviceHost.Open();
Console.ReadLine();
serviceHost.Close();

The method Configuration.CreateBinding() creates the binding. This method returns an instance of a specific binding which is configured in the Configuration class. If you change the binding of the server, for example, from HTTP to TCP, both the client and the server will automatically use it.

Points of Interest

This solution is not exactly rocket science, but it has helped me and my company save time in the past. Which is the reason that I want to share this information with you. When someone forgets to change the client after an interface change, the solution won't compile. This way, it fails fast, which is a good thing. Another advantage is that you could stop publishing meta data of your service, this will increase the security of your service.

Central Binding Configuration

Together with the configuration, the binding is also configured centrally in the configuration assembly. This enables you to configure the binding for the client as well as for the server. You could, for example, set the MaxReceivedMessageSize and the ReceiveTimeout, which should be set for the client as for the server. By centralizing the configuration, this can now be done in a single place instead of two or more configuration files. The code below shows an example of how to do this.

public static class Configuration
{
  public static Binding CreateBinding()
  {
     NetTcpBinding binding = new NetTcpBinding();
     binding.MaxReceivedMessageSize = 25 * 1024 * 1024;
     binding.ReceiveTimeout = new TimeSpan(0, 2, 0);
     return new NetTcpBinding();
  }
}

Further Extension of the Configuration

By standardizing the configuration of your services, it is possible to extend this even further. For example, you could create a standard base class or interface for all your configurations. This standardization makes it possible to extract information from your configuration, for example, documentation. This would enable automatic documentation creation from your configuration classes. The result, complete up to date documentation of all your services. A good way to start is to define an interface with methods for all the attributes to document. See the code below for an example.

public interface IWcfConfiguration
{
   string RetrieveUriString();
   int RetrievePortNumber();
   TimeSpan RetrieveTimeOut();
}

Using Callbacks

Callbacks are possible using this solution. When using callbacks, the ChannelFactory used in the client should be changed to a DuplexChannelFactory. The constructor of a DuplexChannelFactory expects an instance where the callback method(s) can be called by the server. Also the client should call the subscribe and unsubscribe methods to add the callback method. See the code below for an example. The provided source code in the download includes an example using callbacks.

DuplexChannelFactory<imessagesenderwithcallback /> duplexChannelFactory = 
	new DuplexChannelFactory<imessagesenderwithcallback />(this, binding, endpointAddress);
IMessageSenderWithCallBack duplexMessageSender = 
	duplexChannelFactory.CreateChannel(endpointAddress);
duplexMessageSender.Subscribe();
duplexMessageSender.ShowMessage("message");
duplexMessageSender.Unsubscribe();

Disadvantages

This solution has its disadvantages. It works only if you control both sides of the wire. If you publish a service for usage outside your company, it's a different scenario. You could publish the interface and configuration assembly, but I think that enabling meta-data on your service would be easier for such a situation.

Revision History

  • 22/05/2010
    • First version
  • 06/01/2011
    • Added callbacks remarks in the article and in the provided source code. Thanks to Farhad Bheekoo for asking the question and providing the sample source code.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Patrick Kalkman
Architect http://www.hinttech.nl
Netherlands Netherlands
Patrick Kalkman is a senior Software Architect with more than 20 years professional development experience. He works for Hinttech where he develops state of the art web applications.

Patrick enjoys writing his blog. It discusses software architectures using semantic web technologies. Patrick can be reached at pkalkie@gmail.com.
 
Published Windows 8 apps:
 
Published Windows Phone apps:
 
Awards:

Best Mobile article of March 2012
Best Mobile article of June 2012
Follow on   Twitter

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
GeneralTesting your solution with standard rights (converted to Net 4)memberMaxZ17-May-11 3:02 
Upon debugging Main() the following line results in the exception below:
 
serviceHost.Open();
 

{System.ServiceModel.AddressAccessDeniedException: HTTP could not register URL http://+:1471/. Your process does not have access rights to this namespace (see http://go.microsoft.com/fwlink/?LinkId=70353 for details). ---> System.Net.HttpListenerException: Access is denied
   at System.Net.HttpListener.AddAll()
   at System.Net.HttpListener.Start()
   at System.ServiceModel.Channels.SharedHttpTransportManager.OnOpen()
   --- End of inner exception stack trace ---
   at System.ServiceModel.Channels.SharedHttpTransportManager.OnOpen()
   at System.ServiceModel.Channels.TransportManager.Open(TransportChannelListener channelListener)
   at System.ServiceModel.Channels.TransportManagerContainer.Open(SelectTransportManagersCallback selectTransportManagerCallback)
   at System.ServiceModel.Channels.HttpChannelListener.OnOpen(TimeSpan timeout)
   at System.ServiceModel.Channels.CommunicationObject.Open(TimeSpan timeout)
   at System.ServiceModel.Channels.DatagramChannelDemuxer`2.OnOuterListenerOpen(ChannelDemuxerFilter filter, IChannelListener listener, TimeSpan timeout)
   at System.ServiceModel.Channels.SingletonChannelListener`3.OnOpen(TimeSpan timeout)
   at System.ServiceModel.Channels.CommunicationObject.Open(TimeSpan timeout)
   at System.ServiceModel.Dispatcher.ChannelDispatcher.OnOpen(TimeSpan timeout)
   at System.ServiceModel.Channels.CommunicationObject.Open(TimeSpan timeout)
   at System.ServiceModel.ServiceHostBase.OnOpen(TimeSpan timeout)
   at System.ServiceModel.Channels.CommunicationObject.Open(TimeSpan timeout)
   at System.ServiceModel.Security.NegotiationTokenAuthenticator`1.OnOpen(TimeSpan timeout)
   at System.ServiceModel.Security.WrapperSecurityCommunicationObject.OnOpen(TimeSpan timeout)
   at System.ServiceModel.Channels.CommunicationObject.Open(TimeSpan timeout)
   at System.ServiceModel.Security.SecurityUtils.OpenCommunicationObject(ICommunicationObject obj, TimeSpan timeout)
   at System.ServiceModel.Security.SecurityProtocolFactory.Open(String propertyName, Boolean requiredForForwardDirection, SecurityTokenAuthenticator authenticator, TimeSpan timeout)
   at System.ServiceModel.Security.SymmetricSecurityProtocolFactory.OnOpen(TimeSpan timeout)
   at System.ServiceModel.Security.WrapperSecurityCommunicationObject.OnOpen(TimeSpan timeout)
   at System.ServiceModel.Channels.CommunicationObject.Open(TimeSpan timeout)
   at System.ServiceModel.Security.SecurityListenerSettingsLifetimeManager.Open(TimeSpan timeout)
   at System.ServiceModel.Channels.SecurityChannelListener`1.OnOpen(TimeSpan timeout)
   at System.ServiceModel.Channels.CommunicationObject.Open(TimeSpan timeout)
   at System.ServiceModel.Dispatcher.ChannelDispatcher.OnOpen(TimeSpan timeout)
   at System.ServiceModel.Channels.CommunicationObject.Open(TimeSpan timeout)
   at System.ServiceModel.ServiceHostBase.OnOpen(TimeSpan timeout)
   at System.ServiceModel.Channels.CommunicationObject.Open(TimeSpan timeout)
   at System.ServiceModel.Security.SecuritySessionSecurityTokenAuthenticator.OnOpen(TimeSpan timeout)
   at System.ServiceModel.Security.WrapperSecurityCommunicationObject.OnOpen(TimeSpan timeout)
   at System.ServiceModel.Channels.CommunicationObject.Open(TimeSpan timeout)
   at System.ServiceModel.Security.SecurityUtils.OpenCommunicationObject(ICommunicationObject obj, TimeSpan timeout)
   at System.ServiceModel.Security.SecuritySessionServerSettings.OnOpen(TimeSpan timeout)
   at System.ServiceModel.Security.WrapperSecurityCommunicationObject.OnOpen(TimeSpan timeout)
   at System.ServiceModel.Channels.CommunicationObject.Open(TimeSpan timeout)
   at System.ServiceModel.Security.SecurityListenerSettingsLifetimeManager.Open(TimeSpan timeout)
   at System.ServiceModel.Channels.SecurityChannelListener`1.OnOpen(TimeSpan timeout)
   at System.ServiceModel.Channels.CommunicationObject.Open(TimeSpan timeout)
   at System.ServiceModel.Channels.ReliableChannelListenerBase`1.OnOpen(TimeSpan timeout)
   at System.ServiceModel.Channels.CommunicationObject.Open(TimeSpan timeout)
   at System.ServiceModel.Dispatcher.ChannelDispatcher.OnOpen(TimeSpan timeout)
   at System.ServiceModel.Channels.CommunicationObject.Open(TimeSpan timeout)
   at System.ServiceModel.ServiceHostBase.OnOpen(TimeSpan timeout)
   at System.ServiceModel.Channels.CommunicationObject.Open(TimeSpan timeout)
   at DynamicWcfProxyServer.Program.Main(String[] args) in C:\LIBS\Client Projects\Niedersuess Site\Frank ERP\Material\DynamicWcfProxyServer\DynamicWcfProxyServer\Program.cs:line 18
   at System.AppDomain._nExecuteAssembly(Assembly assembly, String[] args)
   at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
   at System.Threading.ThreadHelper.ThreadStart()}
 
This does not happen when VS2010 is started with Admin rights.
 
Are there any special settings required for running this in a secured production environment?
 
I would really like to get this to run! because this approach is awesome - wcf, silverlight, ria etc. is too much of a configurationla pain!
This should be a standard option for SL along with a simple configurator for the various types of communication between services and clients.
 
Thank you!
GeneralRe: Testing your solution with standard rights (converted to Net 4)memberPatrick Kalkman17-May-11 3:08 
Hello MaxZ,
 
This is a known issue, please take a look at http://blogs.msdn.com/b/paulwh/archive/2007/05/04/addressaccessdeniedexception-http-could-not-register-url-http-8080.aspx.
 
It describes several solutions to the problem. I use the HttpNameSpaceManager which works fine.
Patrick Kalkman
 
My latest article: Digest Authentication on WCF
My Blog: SemanticArchitecture.net

GeneralSilverlight 4 client supported?memberMaxZ17-May-11 2:42 
I have been looking for something like this for quite some time!
 
Will this work with a Silverlight 4 client?
GeneralRe: Silverlight 4 client supported?memberPatrick Kalkman17-May-11 2:45 
Yes it will, but you will have to create a seperate silverlight assembly with the interface. You can link ("add as link in vs") the interface files from the .Net assembly to the Silverlight interface assembly.
Patrick Kalkman
 
My latest article: Digest Authentication on WCF
My Blog: SemanticArchitecture.net

GeneralMy vote of 3mvpSacha Barber9-Jan-11 0:08 
I think what you are demonstrating is pretty much standard WCF, nothing new there I feel (sorry), but its not all bad, and shows use of callback and shared assemblies. So for that I give it 3
 
I did some WCF thing with callbacks, threading etc etc some time ago: WCF / WPF Chat Application[^]
 
May be of interest
GeneralMy vote of 5memberEspen Harlinn8-Jan-11 6:15 
5 Nice and Simple, you may find C# Script: The Missing Puzzle Piece[^] of interest too. Another flexible way to implment dynamic services, among many other things.
GeneralReg: SharedInterface in Dynamically generate a WCF proxymembersatishnandigam1-Dec-10 20:44 
Patrick Kalkman,
 
Thanks for the great post.
 
But we have some doubt on SharedInterface . In this you mentioned that SharedInterface can be used as the SharedAssembly. But some time we cahnged the SharedInterface that can be shared on both client and server side. Every time sharing the SharedInterface is required or any other senorio is there.
 
Thanks,
N.Satish
satish

GeneralRe: Reg: SharedInterface in Dynamically generate a WCF proxymemberPatrick Kalkman3-Jan-11 22:37 
Hello Satish,
 
This solution works best if you control both sides of the wire, client and server. If you have third parties using your server, you could offer a shared assembly or enable meta-data publishing of your service and let third parties generate a proxy.
 
The problem I had with generated proxies is that you notice a interface breaking change only during runtime. When I use this solution I am able to detect an interface breaking change during compilation on the build server. This saves us time in development.
 
Regards,
 
Patrick

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web02 | 2.6.130617.1 | Last Updated 8 Jan 2011
Article Copyright 2010 by Patrick Kalkman
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid