A couple of years ago, when I first started using WCF, it seemed like a real pain in the you-know-what to add Service References to my app in order to consume WCF Services. I wanted a base Proxy class that I could extend to be used for any kind of ServiceContract. The key to writing this class was the use of generics Here’s what I came up with, after much trial and error (be sure you have using System.ServiceModel and using System.ServiceModel.Channels):
public abstract class ProxyBase<T> where T : class { #region Declarations public ChannelFactory<T> Factory = null; public T proxy { get; set; } public string EndpointName { get; set; } private string m_Certificate; public string Certificate { get { return this.m_Certificate; } set { try { this.m_Certificate = value; if (this.m_Certificate.IsNotNullOrEmpty() && Factory != null) { EndpointIdentity Identity = EndpointIdentity.CreateDnsIdentity(this.m_Certificate); if (Identity == null) Console.WriteLine("Identity Is Null"); else { Uri AddressUri = Factory.Endpoint.Address.Uri; Factory.Endpoint.Address = new EndpointAddress(AddressUri, Identity); // not sure, but probably have to remove old EventHandler before recreating the // proxy and recreating the Channel. ((ICommunicationObject)proxy).Faulted -= new EventHandler(ProxyBase_Faulted); proxy = Factory.CreateChannel(); ((ICommunicationObject)proxy).Faulted += new EventHandler(ProxyBase_Faulted); } } } catch (Exception ex) { Factory = null; proxy = null; } } } #endregion #region Constructors public ProxyBase() { } public ProxyBase(string endpoint) { // not the best error-handling, but good enough for now // TODO - make error-handling better // Already handling faulted channels, but now need to do something for bad addresses? // Because I *think* a bad address will cause an exception in the creation of the Factory. try { this.EndpointName = endpoint; Factory = new ChannelFactory<T>(endpoint); proxy = Factory.CreateChannel(); ((ICommunicationObject)proxy).Faulted += new EventHandler(ProxyBase_Faulted); } catch (Exception ex) { Console.WriteLine("Error in Proxy for {0}, Endpoint: {1}\r\n{2)", typeof(T).Name, endpoint, ex.Message); Factory = null; proxy = null; } } public ProxyBase(string endpoint, string address) { // not the best error-handling, but good enough for now // TODO - make error-handling better // Already handling faulted channels, but now need to do something for bad addresses? // Because I *think* a bad address will cause an exception in the creation of the Factory. try { this.EndpointName = endpoint; Factory = new ChannelFactory<T>(endpoint); Factory.Endpoint.Address = new EndpointAddress(address); proxy = Factory.CreateChannel(); ((ICommunicationObject)proxy).Faulted += new EventHandler(ProxyBase_Faulted); } catch (Exception ex) { Console.WriteLine("Proxy for {0}, Endpoint: {1}, Address: {2}\r\n{3}", typeof(T).Name, endpoint, address, ex.Message); Factory = null; proxy = null; } } #endregion #region Methods public void Close() { if (proxy != null) ((IChannel)proxy).Close(); if (Factory != null) Factory.Close(); } #endregion #region Events private void ProxyBase_Faulted(object sender, EventArgs e) { Console.WriteLine("Proxy for {0}\r\n{1}", typeof(T).Name, ((IChannel)proxy).State.ToString()); ((ICommunicationObject)sender).Abort(); proxy = Factory.CreateChannel(); } #endregion }
Seems pretty simple. Notice that it’s an abstract class. You must sub-class it in order to use it. OK, so how do we construct the sub-class? Very simple also. First, you must have a few Interfaces defined, as you would normally have for any WCF service. Here is what I’m going to use for my example:
[ServiceContract(Namespace = "http://MyCompany")] public interface IMessageService { [OperationContract(IsOneWay = true)] void ProcessMessage(IMessage message); } [ServiceContract(Namespace = "http://MyCompany")] public interface ISynchronousMessageService { [OperationContract] bool ProcessMessage(IMessage message); } public interface IMessage { string MessageToSend { get; set; } string MessageReceived { get; set; } } public class MyMessage : IMessage { public string MessageToSend { get; set; } public string MessageReceived { get; set; } }
All WCF Services need to have a ServiceContract and OperationContract, so there they are. I’ve also got an interface defined for the Messages I’m going to send, plus a MyMessage class that implements that IMessage interface.
Next, here is what my sub-classed Proxies might look like:
public class SynchronousProxy : ProxyBase<ISynchronousMessageService>, ISynchronousMessageService, IDisposable { #region Constructors public SynchronousProxy(string endpoint) : base(endpoint) { } public SynchronousProxy(string endpoint, string address) : base(endpoint, address) { } #endregion #region Methods public virtual bool ProcessMessage(IMessage message) { try { if (proxy != null) return proxy.ProcessMessage(message); else return false; } catch (Exception ex) { Console.WriteLine("Exception, ProcessMessage\r\n{0}", ex.Message); return false; } } public void Dispose() { this.Close(); } #endregion } public class Proxy : ProxyBase<IMessageService>, IMessageService, IDisposable { #region Constructors public Proxy(string endpoint) : base(endpoint) { } public Proxy(string endpoint, string address) : base(endpoint, address) { } #endregion #region Methods public virtual void ProcessMessage(IMessage message) { try { if (proxy != null) proxy.ProcessMessage(message); } catch (Exception ex) { Console.WriteLine("Exception, ProcessMessage\r\n{0}", ex.Message); } } public void Dispose() { this.Close(); } #endregion }
And now, how do I actually use these proxy classes? You’d have the usual WCF stuff defined in the .config in the endpoint in the systemServiceModel tags:
<system.serviceModel> <client> <endpoint name="MyEndpoint" address="net.tcp://MyComputer:666/MyMessageService" binding="netTcpBinding" bindingConfiguration="netTcpConfig" contract="MyApp.IMessageService"/> </client> </system.serviceModel>
And then in your code, you’d call the WCF service like this:
using (Proxy proxy = new Proxy("MyEndpoint")) { MyMessage message = new MyMessage(); message.MessageToSend = "MyTestMessage"; proxy.ProcessMessage(message); }
Wow, that’s pretty easy, isn’t it? No need to add ServiceReferences or anything like that. All you need to know are the Interfaces that the Services use and you’re good to go!