2011-03-28 61 views
15

我们从Web应用程序中获取了一个WCF服务。我们使用的客户端是使用Visual Studio的“Add Service Reference”选项生成的。由于它是一个网络应用程序,并且由于应用程序的性质很可能会导致相对较短的会话,因此我们选择在用户登录并在会话的整个生命周期内保留该客户端时创建客户端实例,然后在会话结束时处理它。处理持久WCF客户端进入故障状态

这使我想到了我的问题 - 我们试图确定处理客户端通道进入故障状态的最佳方式。周围的一些搜索后,我们来到了这一点:

if(client.State = CommuncationState.Faulted) 
{ 
    client = new Client(); 
} 

try 
{ 
    client.SomeMethod(); 
} 
catch //specific exceptions left out for brevity 
{ 
    //logging or whatever we decide to do 
    throw; 
} 

然而,这并不工作,由于这样的事实,至少在我们的情况下,即使服务已关闭,客户端会显示Open状态,直到您实际尝试使用它进行呼叫,然后进入Faulted状态。

所以这让我们去做别的事情。我们想出的另一个选项是:

try 
{ 
    client.SomeMethod(); 
} 
catch 
{ 
    if(client.State == CommunicationState.Faulted) 
    { 
     //we know we're faulted, try it again 
     client = new Client(); 
     try 
     { 
      client.SomeMethod(); 
     } 
     catch 
     { 
      throw; 
     } 
    } 
    //handle other exceptions 
} 

但是那种气味。显然,我们可以通过使用新客户端并在每次调用时处理它来避免这种情况。这似乎是不必要的,但如果这是正确的方式,那么我想这就是我们会选择的。那么,优雅地处理确定客户是否处于故障状态,然后对其进行处理的最佳方式是什么?我们是否应该为每次通话都获得新客户?

要记住的另一件事 - 客户端的实例化以及所有这些检查和处理发生在客户端的包装类中。如果我们按照我们的意图这样做,它对应用程序本身就是透明的 - 调用它们并处理它们的异常不需要特殊的代码。

+0

什么导致客户端进入故障状态?我一直可以让WCF服务正常返回错误,客户可以继续关注它的业务。服务器没有响应或什么? – Tridus 2011-03-28 23:16:45

+0

在这种情况下,我们使用ASP.NET成员资格,并且通过超出“userIsOnlineTimeWindow”属性来运行它。很明显,在这种情况下,将用户重定向到登录页面是有意义的,但我们正在努力确保我们已准备好应对可能陷入故障状态的其他任何情况。 – Zannjaminderson 2011-03-29 13:30:04

回答

18

要回答你的问题,你可以处理的ChannelFactory属性的Faulted event这样的:

client.ChannelFactory.Faulted += new EventHandler(ChannelFactory_Faulted); 

这应该让你执行任何记录/你需要做清理工作。

作为一般性建议,您不应该在会话期间将通道保持打开状态,因此请确保在完成通道后正确关闭通道(异常中止)。如果可能的话,考虑不使用Visual Studio添加服务引用,或者至少清理它生成的代码/配置。我建议如果您想使用代理实现,请通过从ClientBase派生或使用ChannelFactory实现来创建自己的代理实现。既然你提到了一个包装类,我建议你使用ChannelFactory并为你的清理需求处理Faulted event

+0

感谢您的回答。我已经看到很多人在为每个电话打开和关闭一个客户频道,我想知道这样做的理由是什么? – Zannjaminderson 2011-03-29 13:31:33

+2

原因是您的服务只能有这么多的并发呼叫和并发会话。因此,如果您的网络应用程序中的每个会话都有一个开放的会话,您将很快达到默认限制并需要更改它,并且性能会受到影响。 检查了解优化这些(和其他)设置的更多信息: http://msdn.microsoft.com/en-us/library/ee377061(v=bts.10).aspx – BrandonZeider 2011-03-31 17:24:19

+0

谢谢。我一直在读一些关于ChannelFactory的内容,看起来我们将会朝这个方向发展。 – Zannjaminderson 2011-03-31 17:39:40

11

尝试处理在客户端代理.Faulted事件,如:

((ICommunicationObject)client).Faulted += new EventHandler(client_Faulted); 

private void client_Faulted(object sender, EventArgs e) 
{ 
    client = new Client(); 
    ((ICommunicationObject)client).Faulted += new EventHandler(client_Faulted); 
} 

应该触发瞬间通道故障,给你一个机会,重新打开它。

您仍然应该在try-catch块中将每个调用都包装为client方法,甚至可能将其包装在while()循环中,该循环会重试该调用n次,然后记录失败。 EG:

bool succeeded = false; 
int triesLeft = 3; 
while (!succeeded && triesLeft > 0) 
{ 
    triesLeft--; 
    try 
    { 
     client.SomeMethod(); 
     succeeded = true; 
    } 
    catch (exception ex) 
    { 
     logger.Warn("Client call failed, retries left: " + triesLeft; 
    } 
} 
if (!succeeded) 
    logger.Error("Could not call web service"); 

在我的代码,我尽可能使用ManualResetEvent阻断while()循环,直到client_Faulted事件处理程序有机会来重新创建client代理了。

+1

退订'Faulted'事件怎么样?应该在哪里完成? – itsho 2017-09-04 07:52:54