{"id":4305,"date":"2014-03-30T09:44:59","date_gmt":"2014-03-30T09:44:59","guid":{"rendered":"https:\/\/unknownerror.org\/index.php\/2014\/03\/30\/wcf-retry-proxy-collection-of-common-programming-errors\/"},"modified":"2014-03-30T09:44:59","modified_gmt":"2014-03-30T09:44:59","slug":"wcf-retry-proxy-collection-of-common-programming-errors","status":"publish","type":"post","link":"https:\/\/unknownerror.org\/index.php\/2014\/03\/30\/wcf-retry-proxy-collection-of-common-programming-errors\/","title":{"rendered":"WCF Retry Proxy-Collection of common programming errors"},"content":{"rendered":"<ul>\n<li><img decoding=\"async\" src=\"http:\/\/www.gravatar.com\/avatar\/e01f4ab4fa4ede8a7a23fe91ba902337?s=32&amp;d=identicon&amp;r=PG\" \/><br \/>\nBob Horn<\/p>\n<p>I&#8217;m struggling with trying to find the best way to implement WCF retries. I&#8217;m hoping to make the client experience as clean as possible. There are two approaches of which I&#8217;m aware (see below). My question is: &#8220;<strong>Is there a third approach that I&#8217;m missing? Maybe one that&#8217;s the generally accepted way of doing this?<\/strong>&#8220;<\/p>\n<p><strong>Approach #1<\/strong>: Create a proxy that implements the service interface. For each call to the proxy, implement retries.<\/p>\n<pre><code>public class Proxy : ISomeWcfServiceInterface\n{\n    public int Foo(int snurl)\n    {\n        return MakeWcfCall(() =&gt; _channel.Foo(snurl));\n    }\n\n    public string Bar(string snuh)\n    {\n        return MakeWcfCall(() =&gt; _channel.Bar(snuh));\n    }\n\n    private static T MakeWcfCall(Func func)\n    {\n        \/\/ Invoke the func and implement retries.\n    }\n}\n<\/code><\/pre>\n<p><strong>Approach #2<\/strong>: Change MakeWcfCall() (above) to public, and have the consuming code send the func directly.<\/p>\n<p>What I don&#8217;t like about approach #1 is having to update the Proxy class every time the interface changes.<\/p>\n<p>What I don&#8217;t like about approach #2 is the client having to wrap their call in a func.<\/p>\n<p>Am I missing a better way?<\/p>\n<p><strong>EDIT<\/strong><\/p>\n<p>I&#8217;ve posted an answer here (see below), based on the accepted answer that pointed me in the right direction. I thought I&#8217;d share my code, in an answer, to help someone work through what I had to work through. Hope it helps.<\/p>\n<\/li>\n<li><img decoding=\"async\" src=\"http:\/\/www.gravatar.com\/avatar\/639ced0d6d8b40ff2472650a8e349145?s=32&amp;d=identicon&amp;r=PG\" \/><br \/>\nJim<\/p>\n<p>I have done this very type of thing and I solved this problem using .net&#8217;s RealProxy class.<\/p>\n<p>Using <code>RealProxy<\/code>, you can create an actual proxy at runtime using your provided interface. From the calling code it is as if they are using an <code>IFoo<\/code> channel, but in fact it is a <code>IFoo<\/code> to the proxy and then you get a chance to intercept the calls to any method, property, constructors, etc.<\/p>\n<p>Deriving from <code>RealProxy<\/code>, you can then override the Invoke method to intercept calls to the WCF methods and handle CommunicationException, etc.<\/p>\n<\/li>\n<li><img decoding=\"async\" src=\"http:\/\/www.gravatar.com\/avatar\/18fce96d020cb3b88bb8e10b1521c171?s=32&amp;d=identicon&amp;r=PG\" \/><br \/>\nGarath<\/li>\n<li><img decoding=\"async\" src=\"http:\/\/www.gravatar.com\/avatar\/e01f4ab4fa4ede8a7a23fe91ba902337?s=32&amp;d=identicon&amp;r=PG\" \/><br \/>\nBob Horn<\/p>\n<p>Note: This shouldn&#8217;t be the accepted answer, but I wanted to post the solution in case it helps others. Jim&#8217;s answer pointed me in this direction.<\/p>\n<p><strong>First, the consuming code, showing how it works:<\/strong><\/p>\n<pre><code>static void Main(string[] args)\n{\n    var channelFactory = new WcfChannelFactory(new NetTcpBinding());\n    var endpointAddress = ConfigurationManager.AppSettings[\"endpointAddress\"];\n\n    \/\/ The call to CreateChannel() actually returns a proxy that can intercept calls to the\n    \/\/ service. This is done so that the proxy can retry on communication failures.            \n    IPrestoService prestoService = channelFactory.CreateChannel(new EndpointAddress(endpointAddress));\n\n    Console.WriteLine(\"Enter some information to echo to the Presto service:\");\n    string message = Console.ReadLine();\n\n    string returnMessage = prestoService.Echo(message);\n\n    Console.WriteLine(\"Presto responds: {0}\", returnMessage);\n\n    Console.WriteLine(\"Press any key to stop the program.\");\n    Console.ReadKey();\n}\n<\/code><\/pre>\n<p><strong>The WcfChannelFactory:<\/strong><\/p>\n<pre><code>public class WcfChannelFactory : ChannelFactory where T : class\n{\n    public WcfChannelFactory(Binding binding) : base(binding) {}\n\n    public T CreateBaseChannel()\n    {\n        return base.CreateChannel(this.Endpoint.Address, null);\n    }\n\n    public override T CreateChannel(EndpointAddress address, Uri via)\n    {\n        \/\/ This is where the magic happens. We don't really return a channel here;\n        \/\/ we return WcfClientProxy.GetTransparentProxy(). That class will now\n        \/\/ have the chance to intercept calls to the service.\n        this.Endpoint.Address = address;            \n        var proxy = new WcfClientProxy(this);\n        return proxy.GetTransparentProxy() as T;\n    }\n}\n<\/code><\/pre>\n<p><strong>The WcfClientProxy:<\/strong> (This is where we intercept and retry.)<\/p>\n<pre><code>    public class WcfClientProxy : RealProxy where T : class\n    {\n        private WcfChannelFactory _channelFactory;\n\n        public WcfClientProxy(WcfChannelFactory channelFactory) : base(typeof(T))\n        {\n            this._channelFactory = channelFactory;\n        }\n\n        public override IMessage Invoke(IMessage msg)\n        {\n            \/\/ When a service method gets called, we intercept it here and call it below with methodBase.Invoke().\n\n            var methodCall = msg as IMethodCallMessage;\n            var methodBase = methodCall.MethodBase;\n\n            \/\/ We can't call CreateChannel() because that creates an instance of this class,\n            \/\/ and we'd end up with a stack overflow. So, call CreateBaseChannel() to get the\n            \/\/ actual service.\n            T wcfService = this._channelFactory.CreateBaseChannel();\n\n            try\n            {\n                var result = methodBase.Invoke(wcfService, methodCall.Args);\n\n                return new ReturnMessage(\n                      result, \/\/ Operation result\n                      null, \/\/ Out arguments\n                      0, \/\/ Out arguments count\n                      methodCall.LogicalCallContext, \/\/ Call context\n                      methodCall); \/\/ Original message\n            }\n            catch (FaultException)\n            {\n                \/\/ Need to specifically catch and rethrow FaultExceptions to bypass the CommunicationException catch.\n                \/\/ This is needed to distinguish between Faults and underlying communication exceptions.\n                throw;\n            }\n            catch (CommunicationException ex)\n            {\n                \/\/ Handle CommunicationException and implement retries here.\n                throw new NotImplementedException();\n            }            \n        }\n    }\n<\/code><\/pre>\n<p>Sequence diagram of a call being intercepted by the proxy:<\/p>\n<p><img decoding=\"async\" src=\"http:\/\/i.stack.imgur.com\/wK1v0.png\" \/><\/p>\n<\/li>\n<li><img decoding=\"async\" src=\"http:\/\/i.stack.imgur.com\/6IEWW.jpg?s=32&amp;g=1\" \/><br \/>\nErno de Weerd<\/p>\n<p>Do not mess with generated code because, as you mentioned, it will be generated again so any customization will be overridden.<\/p>\n<p>I see two ways:<\/p>\n<ol>\n<li>\n<p>Write\/generate a partial class for each proxy that contains the retry variation. This is messy because you will still have to adjust it when the proxy changes<\/p>\n<\/li>\n<li>\n<p>Make a custom version of svcutil that allows you to generate a proxy that derives from a different base class and put the retry code in that base class. This is quite some work but it can be done and solves the issue in a robust way.<\/p>\n<\/li>\n<\/ol>\n<\/li>\n<li><img decoding=\"async\" src=\"http:\/\/www.gravatar.com\/avatar\/86f90edf05caa5f810b1d3bf9c07f36d?s=32&amp;d=identicon&amp;r=PG\" \/><br \/>\nxPridex<\/p>\n<p>go through approach 1, then wrape all context event (open, opened, faulted, &#8230;) into event to be exposed by your class proxy, once the communication is faulted then re-create the proxy or call some recursive method inside proxy class. i can share with you some wok i have just tested.<\/p>\n<pre><code>    public class DuplexCallBackNotificationIntegrationExtension : IExtension, INotificationPusherCallback {\n    #region - Field(s) -\n    private static Timer _Timer = null;\n    private static readonly object m_SyncRoot = new Object();\n    private static readonly Guid CMESchedulerApplicationID = Guid.NewGuid();\n    private static CancellationTokenSource cTokenSource = new CancellationTokenSource();\n    private static CancellationToken cToken = cTokenSource.Token;\n    #endregion\n\n    #region - Event(s) -\n    \/\/\/ \n    \/\/\/ Event fired during Duplex callback.\n    \/\/\/ \n    public static event EventHandler CallBackEvent;\n\n    public static event EventHandler InstanceContextOpeningEvent;\n    public static event EventHandler InstanceContextOpenedEvent;\n    public static event EventHandler InstanceContextClosingEvent;\n    public static event EventHandler InstanceContextClosedEvent;\n    public static event EventHandler InstanceContextFaultedEvent;\n    #endregion\n\n    #region - Property(ies) -\n    \/\/\/ \n    \/\/\/ Interface extension designation.\n    \/\/\/ \n    public string Name {\n        get {\n            return \"Duplex Call Back Notification Integration Extension.\";\n        }\n    }\n\n    \/\/\/ \n    \/\/\/ GUI Interface extension.\n    \/\/\/ \n    public IUIExtension UIExtension {\n        get {\n            return null;\n        }\n    }\n    #endregion\n\n    #region - Constructor(s) \/ Finalizer(s) -\n    \/\/\/ \n    \/\/\/ Initializes a new instance of the DuplexCallBackNotificationIntegrationExtension class.\n    \/\/\/ \n    public DuplexCallBackNotificationIntegrationExtension() {\n        CallDuplexNotificationPusher();\n    }\n    #endregion\n\n    #region - Delegate Invoker(s) -\n\n    void ICommunicationObject_Opening(object sender, System.EventArgs e) {\n        DefaultLogger.DUPLEXLogger.Info(\"context_Opening\");\n        this.OnInstanceContextOpening(e);\n    }\n\n    void ICommunicationObject_Opened(object sender, System.EventArgs e) {\n        DefaultLogger.DUPLEXLogger.Debug(\"context_Opened\");\n        this.OnInstanceContextOpened(e);\n    }\n\n    void ICommunicationObject_Closing(object sender, System.EventArgs e) {\n        DefaultLogger.DUPLEXLogger.Debug(\"context_Closing\");\n        this.OnInstanceContextClosing(e);\n    }\n\n    void ICommunicationObject_Closed(object sender, System.EventArgs e) {\n        DefaultLogger.DUPLEXLogger.Debug(\"context_Closed\");\n        this.OnInstanceContextClosed(e);\n    }\n\n    void ICommunicationObject_Faulted(object sender, System.EventArgs e) {\n        DefaultLogger.DUPLEXLogger.Error(\"context_Faulted\");\n        this.OnInstanceContextFaulted(e);\n\n        if (_Timer != null) {\n            _Timer.Dispose();\n        }\n\n        IChannel channel = sender as IChannel;\n        if (channel != null) {\n            channel.Abort();\n            channel.Close();\n        }\n\n        DoWorkRoutine(cToken);\n    }\n\n    protected virtual void OnCallBackEvent(Notification objNotification) {\n        if (CallBackEvent != null) {\n            CallBackEvent(this, new CallBackEventArgs(objNotification));\n        }\n    }\n\n    protected virtual void OnInstanceContextOpening(System.EventArgs e) {\n        if (InstanceContextOpeningEvent != null) {\n            InstanceContextOpeningEvent(this, e);\n        }\n    }\n\n    protected virtual void OnInstanceContextOpened(System.EventArgs e) {\n        if (InstanceContextOpenedEvent != null) {\n            InstanceContextOpenedEvent(this, e);\n        }\n    }\n\n    protected virtual void OnInstanceContextClosing(System.EventArgs e) {\n        if (InstanceContextClosingEvent != null) {\n            InstanceContextClosingEvent(this, e);\n        }\n    }\n\n    protected virtual void OnInstanceContextClosed(System.EventArgs e) {\n        if (InstanceContextClosedEvent != null) {\n            InstanceContextClosedEvent(this, e);\n        }\n    }\n\n    protected virtual void OnInstanceContextFaulted(System.EventArgs e) {\n        if (InstanceContextFaultedEvent != null) {\n            InstanceContextFaultedEvent(this, e);\n        }\n    }\n    #endregion\n\n    #region - IDisposable Member(s) -\n\n    #endregion\n\n    #region - Private Method(s) -\n\n    \/\/\/ \n    \/\/\/ \n    \/\/\/ \n    void CallDuplexNotificationPusher() {\n        var routine = Task.Factory.StartNew(() =&gt; DoWorkRoutine(cToken), cToken);\n        cToken.Register(() =&gt; cancelNotification());\n    }\n\n    \/\/\/ \n    \/\/\/ \n    \/\/\/ \n    \/\/\/ \n    void DoWorkRoutine(CancellationToken ct) {\n        lock (m_SyncRoot) {\n            var context = new InstanceContext(this);\n            var proxy = new NotificationPusherClient(context);\n            ICommunicationObject communicationObject = proxy as ICommunicationObject;\n            communicationObject.Opening += new System.EventHandler(ICommunicationObject_Opening);\n            communicationObject.Opened += new System.EventHandler(ICommunicationObject_Opened);\n            communicationObject.Faulted += new System.EventHandler(ICommunicationObject_Faulted);\n            communicationObject.Closed += new System.EventHandler(ICommunicationObject_Closed);\n            communicationObject.Closing += new System.EventHandler(ICommunicationObject_Closing);\n\n\n            try {\n                proxy.Subscribe(CMESchedulerApplicationID.ToString());\n            }\n            catch (Exception ex) {\n                Logger.HELogger.DefaultLogger.DUPLEXLogger.Error(ex);                    \n\n                switch (communicationObject.State) {\n                    case CommunicationState.Faulted:\n                        proxy.Close();\n                        break;\n\n                    default:\n                        break;\n                }\n\n                cTokenSource.Cancel();\n                cTokenSource.Dispose();                    \n                cTokenSource = new CancellationTokenSource();\n                cToken = cTokenSource.Token;\n                CallDuplexNotificationPusher();  \n            }\n\n            bool KeepAliveCallBackEnabled = Properties.Settings.Default.KeepAliveCallBackEnabled;\n            if (KeepAliveCallBackEnabled) {\n                _Timer = new Timer(new TimerCallback(delegate(object item) {\n                    DefaultLogger.DUPLEXLogger.Debug(string.Format(\"._._._._._. New Iteration {0: yyyy MM dd hh mm ss ffff} ._._._._._.\", DateTime.Now.ToUniversalTime().ToString()));\n                    DBNotificationPusherService.Acknowledgment reply = DBNotificationPusherService.Acknowledgment.NAK;\n                    try {\n                        reply = proxy.KeepAlive();\n                    }\n                    catch (Exception ex) {\n                        DefaultLogger.DUPLEXLogger.Error(ex);\n\n                        switch (communicationObject.State) {\n                            case CommunicationState.Faulted:\n                            case CommunicationState.Closed:\n                                proxy.Abort();\n                                ICommunicationObject_Faulted(null, null);\n                                break;\n\n                            default:\n                                break;\n                        }\n                    }\n\n                    DefaultLogger.DUPLEXLogger.Debug(string.Format(\"Acknowledgment = {0}.\", reply.ToString()));\n                    _Timer.Change(Properties.Settings.Default.KeepAliveCallBackTimerInterval, Timeout.Infinite);\n                }), null, Properties.Settings.Default.KeepAliveCallBackTimerInterval, Timeout.Infinite);\n            }\n        }\n    }\n\n    \/\/\/ \n    \/\/\/ \n    \/\/\/ \n    void cancelNotification() {\n       DefaultLogger.DUPLEXLogger.Warn(\"Cancellation request made!!\");\n    }\n    #endregion \n\n    #region - Public Method(s) -\n    \/\/\/ \n    \/\/\/ Fire OnCallBackEvent event and fill automatic-recording collection with newest \n    \/\/\/ \n    \/\/\/ \n    public void SendNotification(Notification objNotification) {\n\n        \/\/ Fire event callback.\n        OnCallBackEvent(objNotification);\n    }\n    #endregion\n\n    #region - Callback(s) -\n    private void OnAsyncExecutionComplete(IAsyncResult result) {\n\n    }\n    #endregion\n}\n<\/code><\/pre>\n<\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>Bob Horn I&#8217;m struggling with trying to find the best way to implement WCF retries. I&#8217;m hoping to make the client experience as clean as possible. There are two approaches of which I&#8217;m aware (see below). My question is: &#8220;Is there a third approach that I&#8217;m missing? Maybe one that&#8217;s the generally accepted way of [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-4305","post","type-post","status-publish","format-standard","hentry","category-uncategorized"],"_links":{"self":[{"href":"https:\/\/unknownerror.org\/index.php\/wp-json\/wp\/v2\/posts\/4305","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/unknownerror.org\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/unknownerror.org\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/unknownerror.org\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/unknownerror.org\/index.php\/wp-json\/wp\/v2\/comments?post=4305"}],"version-history":[{"count":0,"href":"https:\/\/unknownerror.org\/index.php\/wp-json\/wp\/v2\/posts\/4305\/revisions"}],"wp:attachment":[{"href":"https:\/\/unknownerror.org\/index.php\/wp-json\/wp\/v2\/media?parent=4305"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/unknownerror.org\/index.php\/wp-json\/wp\/v2\/categories?post=4305"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/unknownerror.org\/index.php\/wp-json\/wp\/v2\/tags?post=4305"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}