C# WCF重试代理
我正在努力寻找实现WCF重试的最佳方法。我希望让客户体验尽可能干净。我知道有两种方法(见下文)。我的问题是:“有没有第三种方法是我遗漏的?也许是一种普遍接受的方法?” 方法#1:创建一个实现服务接口的代理。对于每个对代理的调用,实现重试C# WCF重试代理,c#,wcf,oop,C#,Wcf,Oop,我正在努力寻找实现WCF重试的最佳方法。我希望让客户体验尽可能干净。我知道有两种方法(见下文)。我的问题是:“有没有第三种方法是我遗漏的?也许是一种普遍接受的方法?” 方法#1:创建一个实现服务接口的代理。对于每个对代理的调用,实现重试 public class Proxy : ISomeWcfServiceInterface { public int Foo(int snurl) { return MakeWcfCall<int>(() =>
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中
我错过了一条更好的路吗
编辑
我在这里发布了一个答案(见下文),这个答案是基于公认的答案,为我指明了正确的方向。我想我应该在回答中分享我的代码,帮助别人完成我必须完成的工作。希望有帮助。不要弄乱生成的代码,因为正如您所提到的,它将再次生成,因此任何自定义都将被覆盖 我认为有两种方式:
例如,可以使用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();
}
}
}