C# WCF重试代理

C# WCF重试代理,c#,wcf,oop,C#,Wcf,Oop,我正在努力寻找实现WCF重试的最佳方法。我希望让客户体验尽可能干净。我知道有两种方法(见下文)。我的问题是:“有没有第三种方法是我遗漏的?也许是一种普遍接受的方法?” 方法#1:创建一个实现服务接口的代理。对于每个对代理的调用,实现重试 public class Proxy : ISomeWcfServiceInterface { public int Foo(int snurl) { return MakeWcfCall<int>(() =>

我正在努力寻找实现WCF重试的最佳方法。我希望让客户体验尽可能干净。我知道有两种方法(见下文)。我的问题是:“有没有第三种方法是我遗漏的?也许是一种普遍接受的方法?

方法#1:创建一个实现服务接口的代理。对于每个对代理的调用,实现重试

public class Proxy : ISomeWcfServiceInterface
{
    public int Foo(int snurl)
    {
        return MakeWcfCall<int>(() => _channel.Foo(snurl));
    }

    public string Bar(string snuh)
    {
        return MakeWcfCall<string>(() => _channel.Bar(snuh));
    }

    private static T MakeWcfCall<T>(Func<T> func)
    {
        // Invoke the func and implement retries.
    }
}
public类代理:isomowcfserviceinterface
{
公共int Foo(int snurl)
{
返回MakeWcfCall(()=>_channel.Foo(snurl));
}
公共字符串条(字符串snuh)
{
返回MakeWcfCall(()=>_channel.Bar(snuh));
}
私有静态T MakeWcfCall(Func Func)
{
//调用func并实现重试。
}
}
方法#2:将MakeWcfCall()

我不喜欢这种方法#1,因为每次接口更改时都必须更新代理类

我不喜欢方法2的一点是,客户机必须将其调用封装在func中

我错过了一条更好的路吗

编辑


我在这里发布了一个答案(见下文),这个答案是基于公认的答案,为我指明了正确的方向。我想我应该在回答中分享我的代码,帮助别人完成我必须完成的工作。希望有帮助。

不要弄乱生成的代码,因为正如您所提到的,它将再次生成,因此任何自定义都将被覆盖

我认为有两种方式:

  • 为包含重试变量的每个代理写入/生成分部类。这是混乱的,因为当代理更改时,您仍然必须对其进行调整

  • 制作一个自定义版本的svcutil,该版本允许您生成从不同基类派生的代理,并将重试代码放入该基类中。这是相当多的工作,但它可以做到,并以一种稳健的方式解决问题


  • 例如,可以使用Castle实现通用代理。这里有一篇好文章。此方法将为实现接口的用户对象提供一个类,您将有一个类负责通信

    通过方法1,然后将所有上下文事件(打开、打开、故障等)包装到事件中,以便由类代理公开,一旦通信出现故障,则重新创建代理或在代理类内调用某个递归方法。我可以和你分享一些我刚刚试过的炒锅

        public class DuplexCallBackNotificationIntegrationExtension : IExtension, INotificationPusherCallback {
        #region - Field(s) -
        private static Timer _Timer = null;
        private static readonly object m_SyncRoot = new Object();
        private static readonly Guid CMESchedulerApplicationID = Guid.NewGuid();
        private static CancellationTokenSource cTokenSource = new CancellationTokenSource();
        private static CancellationToken cToken = cTokenSource.Token;
        #endregion
    
        #region - Event(s) -
        /// <summary>
        /// Event fired during Duplex callback.
        /// </summary>
        public static event EventHandler<CallBackEventArgs> CallBackEvent;
    
        public static event EventHandler<System.EventArgs> InstanceContextOpeningEvent;
        public static event EventHandler<System.EventArgs> InstanceContextOpenedEvent;
        public static event EventHandler<System.EventArgs> InstanceContextClosingEvent;
        public static event EventHandler<System.EventArgs> InstanceContextClosedEvent;
        public static event EventHandler<System.EventArgs> InstanceContextFaultedEvent;
        #endregion
    
        #region - Property(ies) -
        /// <summary>
        /// Interface extension designation.
        /// </summary>
        public string Name {
            get {
                return "Duplex Call Back Notification Integration Extension.";
            }
        }
    
        /// <summary>
        /// GUI Interface extension.
        /// </summary>
        public IUIExtension UIExtension {
            get {
                return null;
            }
        }
        #endregion
    
        #region - Constructor(s) / Finalizer(s) -
        /// <summary>
        /// Initializes a new instance of the DuplexCallBackNotificationIntegrationExtension class.
        /// </summary>
        public DuplexCallBackNotificationIntegrationExtension() {
            CallDuplexNotificationPusher();
        }
        #endregion
    
        #region - Delegate Invoker(s) -
    
        void ICommunicationObject_Opening(object sender, System.EventArgs e) {
            DefaultLogger.DUPLEXLogger.Info("context_Opening");
            this.OnInstanceContextOpening(e);
        }
    
        void ICommunicationObject_Opened(object sender, System.EventArgs e) {
            DefaultLogger.DUPLEXLogger.Debug("context_Opened");
            this.OnInstanceContextOpened(e);
        }
    
        void ICommunicationObject_Closing(object sender, System.EventArgs e) {
            DefaultLogger.DUPLEXLogger.Debug("context_Closing");
            this.OnInstanceContextClosing(e);
        }
    
        void ICommunicationObject_Closed(object sender, System.EventArgs e) {
            DefaultLogger.DUPLEXLogger.Debug("context_Closed");
            this.OnInstanceContextClosed(e);
        }
    
        void ICommunicationObject_Faulted(object sender, System.EventArgs e) {
            DefaultLogger.DUPLEXLogger.Error("context_Faulted");
            this.OnInstanceContextFaulted(e);
    
            if (_Timer != null) {
                _Timer.Dispose();
            }
    
            IChannel channel = sender as IChannel;
            if (channel != null) {
                channel.Abort();
                channel.Close();
            }
    
            DoWorkRoutine(cToken);
        }
    
        protected virtual void OnCallBackEvent(Notification objNotification) {
            if (CallBackEvent != null) {
                CallBackEvent(this, new CallBackEventArgs(objNotification));
            }
        }
    
        protected virtual void OnInstanceContextOpening(System.EventArgs e) {
            if (InstanceContextOpeningEvent != null) {
                InstanceContextOpeningEvent(this, e);
            }
        }
    
        protected virtual void OnInstanceContextOpened(System.EventArgs e) {
            if (InstanceContextOpenedEvent != null) {
                InstanceContextOpenedEvent(this, e);
            }
        }
    
        protected virtual void OnInstanceContextClosing(System.EventArgs e) {
            if (InstanceContextClosingEvent != null) {
                InstanceContextClosingEvent(this, e);
            }
        }
    
        protected virtual void OnInstanceContextClosed(System.EventArgs e) {
            if (InstanceContextClosedEvent != null) {
                InstanceContextClosedEvent(this, e);
            }
        }
    
        protected virtual void OnInstanceContextFaulted(System.EventArgs e) {
            if (InstanceContextFaultedEvent != null) {
                InstanceContextFaultedEvent(this, e);
            }
        }
        #endregion
    
        #region - IDisposable Member(s) -
    
        #endregion
    
        #region - Private Method(s) -
    
        /// <summary>
        /// 
        /// </summary>
        void CallDuplexNotificationPusher() {
            var routine = Task.Factory.StartNew(() => DoWorkRoutine(cToken), cToken);
            cToken.Register(() => cancelNotification());
        }
    
        /// <summary>
        /// 
        /// </summary>
        /// <param name="ct"></param>
        void DoWorkRoutine(CancellationToken ct) {
            lock (m_SyncRoot) {
                var context = new InstanceContext(this);
                var proxy = new NotificationPusherClient(context);
                ICommunicationObject communicationObject = proxy as ICommunicationObject;
                communicationObject.Opening += new System.EventHandler(ICommunicationObject_Opening);
                communicationObject.Opened += new System.EventHandler(ICommunicationObject_Opened);
                communicationObject.Faulted += new System.EventHandler(ICommunicationObject_Faulted);
                communicationObject.Closed += new System.EventHandler(ICommunicationObject_Closed);
                communicationObject.Closing += new System.EventHandler(ICommunicationObject_Closing);
    
    
                try {
                    proxy.Subscribe(CMESchedulerApplicationID.ToString());
                }
                catch (Exception ex) {
                    Logger.HELogger.DefaultLogger.DUPLEXLogger.Error(ex);                    
    
                    switch (communicationObject.State) {
                        case CommunicationState.Faulted:
                            proxy.Close();
                            break;
    
                        default:
                            break;
                    }
    
                    cTokenSource.Cancel();
                    cTokenSource.Dispose();                    
                    cTokenSource = new CancellationTokenSource();
                    cToken = cTokenSource.Token;
                    CallDuplexNotificationPusher();  
                }
    
                bool KeepAliveCallBackEnabled = Properties.Settings.Default.KeepAliveCallBackEnabled;
                if (KeepAliveCallBackEnabled) {
                    _Timer = new Timer(new TimerCallback(delegate(object item) {
                        DefaultLogger.DUPLEXLogger.Debug(string.Format("._._._._._. New Iteration {0: yyyy MM dd hh mm ss ffff} ._._._._._.", DateTime.Now.ToUniversalTime().ToString()));
                        DBNotificationPusherService.Acknowledgment reply = DBNotificationPusherService.Acknowledgment.NAK;
                        try {
                            reply = proxy.KeepAlive();
                        }
                        catch (Exception ex) {
                            DefaultLogger.DUPLEXLogger.Error(ex);
    
                            switch (communicationObject.State) {
                                case CommunicationState.Faulted:
                                case CommunicationState.Closed:
                                    proxy.Abort();
                                    ICommunicationObject_Faulted(null, null);
                                    break;
    
                                default:
                                    break;
                            }
                        }
    
                        DefaultLogger.DUPLEXLogger.Debug(string.Format("Acknowledgment = {0}.", reply.ToString()));
                        _Timer.Change(Properties.Settings.Default.KeepAliveCallBackTimerInterval, Timeout.Infinite);
                    }), null, Properties.Settings.Default.KeepAliveCallBackTimerInterval, Timeout.Infinite);
                }
            }
        }
    
        /// <summary>
        /// 
        /// </summary>
        void cancelNotification() {
           DefaultLogger.DUPLEXLogger.Warn("Cancellation request made!!");
        }
        #endregion 
    
        #region - Public Method(s) -
        /// <summary>
        /// Fire OnCallBackEvent event and fill automatic-recording collection with newest 
        /// </summary>
        /// <param name="action"></param>
        public void SendNotification(Notification objNotification) {
    
            // Fire event callback.
            OnCallBackEvent(objNotification);
        }
        #endregion
    
        #region - Callback(s) -
        private void OnAsyncExecutionComplete(IAsyncResult result) {
    
        }
        #endregion
    }
    
    公共类DuplexCallBackNotificationIntegrationExtension:IExtension,INotificationPusherCallback{ #地区-外地- 专用静态计时器_Timer=null; 私有静态只读对象m_SyncRoot=新对象(); 私有静态只读Guid cmesSchedulerApplicationID=Guid.NewGuid(); 私有静态CancellationTokenSource cTokenSource=新的CancellationTokenSource(); 私有静态取消令牌cToken=cTokenSource.Token; #端区 #地区-活动- /// ///在双工回调期间激发的事件。 /// 公共静态事件EventHandler CallBackEvent; 公共静态事件事件处理程序InstanceContextOpenEvent; 公共静态事件事件处理程序InstanceContextOpenEvent; 公共静态事件事件处理程序InstanceContextClosingEvent; 公共静态事件事件处理程序InstanceContextClosedEvent; 公共静态事件事件处理程序InstanceContextFaultedEvent; #端区 #地区-物业- /// ///接口扩展名称。 /// 公共字符串名{ 得到{ 返回“双工回拨通知集成扩展。”; } } /// ///GUI界面扩展。 /// 公共IUIExtension UIExtension{ 得到{ 返回null; } } #端区 #区域-构造函数/终结器- /// ///初始化DuplexCallBackNotificationIntegrationExtension类的新实例。 /// 公共双工CallbackNotificationIntegrationExtension(){ 调用DuplexNotificationPusher(); } #端区 #区域-委托调用程序- 无效ICommunicationObject_打开(对象发送方,System.EventArgs e){ DefaultLogger.DUPLEXLogger.Info(“上下文打开”); 这是。OnInstanceContextOpening(e); } 无效ICommunicationObject_已打开(对象发送方,System.EventArgs e){ DefaultLogger.DUPLEXLogger.Debug(“上下文_已打开”); 此.onInstanceContextOpen(e); } 无效ICommunicationObject_关闭(对象发送方,System.EventArgs e){ DefaultLogger.DUPLEXLogger.Debug(“上下文_关闭”); 此.OnInstanceContextClosing(e); } 无效ICommunicationObject_已关闭(对象发送方,System.EventArgs e){ DefaultLogger.DUPLEXLogger.Debug(“上下文_关闭”); 此.OnInstanceContextClosed(e); } 无效ICommunicationObject\出现故障(对象发送方,System.EventArgs e){ DefaultLogger.DUPLEXLogger.Error(“上下文_故障”); 此.OnInstanceContextFaulted(e); 如果(_Timer!=null){ _Timer.Dispose(); } IChannel通道=发送方作为IChannel; 如果(通道!=null){ channel.Abort(); channel.Close(); } 工作常规(cToken); } 受保护的虚拟void OnCallBackEvent(通知对象化){ if(CallBackEvent!=null){ CallBackEvent(这是新的CallBackEventArgs(objNotification)); } } InstanceContextOpening上受保护的虚拟无效(System.EventArgs e){ if(InstanceContextExponenceEvent!=null){ InstanceContextOpenEvent(此,e); } } InstanceContextOpen上受保护的虚拟无效(System.EventArgs e){ if(instanceContextExpenseEvent!=null){ InstanceContextExpenseEvent(此,e); } } 受保护的
    internal R ExecuteServiceMethod<I, R>(Func<I, R> serviceCall, string userName, string password) {
    
        //Note all clients have the name Manager, but this isn't a problem as they get resolved        
        //by type
        ChannelFactory<I> factory = new ChannelFactory<I>("Manager");
        factory.Credentials.UserName.UserName = userName;
        factory.Credentials.UserName.Password = password;
    
        I manager = factory.CreateChannel();
        //Wrap below in a retry loop
        return serviceCall.Invoke(manager);
    }
    
    static void Main(string[] args)
    {
        var channelFactory = new WcfChannelFactory<IPrestoService>(new NetTcpBinding());
        var endpointAddress = ConfigurationManager.AppSettings["endpointAddress"];
    
        // The call to CreateChannel() actually returns a proxy that can intercept calls to the
        // service. This is done so that the proxy can retry on communication failures.            
        IPrestoService prestoService = channelFactory.CreateChannel(new EndpointAddress(endpointAddress));
    
        Console.WriteLine("Enter some information to echo to the Presto service:");
        string message = Console.ReadLine();
    
        string returnMessage = prestoService.Echo(message);
    
        Console.WriteLine("Presto responds: {0}", returnMessage);
    
        Console.WriteLine("Press any key to stop the program.");
        Console.ReadKey();
    }
    
    public class WcfChannelFactory<T> : ChannelFactory<T> where T : class
    {
        public WcfChannelFactory(Binding binding) : base(binding) {}
    
        public T CreateBaseChannel()
        {
            return base.CreateChannel(this.Endpoint.Address, null);
        }
    
        public override T CreateChannel(EndpointAddress address, Uri via)
        {
            // This is where the magic happens. We don't really return a channel here;
            // we return WcfClientProxy.GetTransparentProxy(). That class will now
            // have the chance to intercept calls to the service.
            this.Endpoint.Address = address;            
            var proxy = new WcfClientProxy<T>(this);
            return proxy.GetTransparentProxy() as T;
        }
    }
    
        public class WcfClientProxy<T> : RealProxy where T : class
        {
            private WcfChannelFactory<T> _channelFactory;
    
            public WcfClientProxy(WcfChannelFactory<T> channelFactory) : base(typeof(T))
            {
                this._channelFactory = channelFactory;
            }
    
            public override IMessage Invoke(IMessage msg)
            {
                // When a service method gets called, we intercept it here and call it below with methodBase.Invoke().
    
                var methodCall = msg as IMethodCallMessage;
                var methodBase = methodCall.MethodBase;
    
                // We can't call CreateChannel() because that creates an instance of this class,
                // and we'd end up with a stack overflow. So, call CreateBaseChannel() to get the
                // actual service.
                T wcfService = this._channelFactory.CreateBaseChannel();
    
                try
                {
                    var result = methodBase.Invoke(wcfService, methodCall.Args);
    
                    return new ReturnMessage(
                          result, // Operation result
                          null, // Out arguments
                          0, // Out arguments count
                          methodCall.LogicalCallContext, // Call context
                          methodCall); // Original message
                }
                catch (FaultException)
                {
                    // Need to specifically catch and rethrow FaultExceptions to bypass the CommunicationException catch.
                    // This is needed to distinguish between Faults and underlying communication exceptions.
                    throw;
                }
                catch (CommunicationException ex)
                {
                    // Handle CommunicationException and implement retries here.
                    throw new NotImplementedException();
                }            
            }
        }