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!
What? Seriousy, what? This would be sooo helpful to me if I understood it! Of all the languages I took in school and beyond (I didn't take any beyond, actually) I did not take Greek (Geek?) I am in awe, don't get me wrong! I have no idea but I know that those who do -- do! So, thank you, on their behalf!
ReplyDeleteSincerely, your not so geeky younger sister.
hahaha ... Shaz, you are the only one who's been commenting lately! Maybe no one is reading my blogs anymore ... =0(
ReplyDeleteWell Bon, I'm sure people are reading!! I know they are. They are gleaning all this great stuff from you and they are in such a hurry that they, more often than not, forget to post a comment! It's a blogger's reality!! Keep posting. Good stuff! Very good stuff! (I think!?) ;)
ReplyDeleteI got to your blog from Google - useful coding! I'm impressed (and you could have been a muse) have a nice day ... Gene Bird (MCSD ebird@ adjacent-tech.com Austin)
ReplyDeleteHi Gene! Thanks for commenting ... I hope you can put some of my code to good use!
ReplyDeleteHello Bonnie,
ReplyDeleteThis looks extremely useful. I can't wait to try it out.
Thank you for sharing Bonnie!
Nick
You're welcome, Nick! Let me know how you like it once you've tried it.
ReplyDeleteLooks excellent.
ReplyDeleteThanks a lot!
C
You're welcome, Cliff! I hope you find it useful!
Delete