当SCT过期时如何更新WCF客户端?

10

我正在使用WCF连接至一个使用"TransportWithMessageCredential"安全模式的SOAP端点。

WCF客户端/服务器使用SCT(安全上下文令牌)来维护安全连接,在一般情况下工作正常。

但是,在一段不活动时间后,SCT将过期,并且下一个方法调用将导致MessageSecurityException:

从对方接收到未经保护或保护不正确的故障。请参见内部FaultException以获取故障代码和详细信息。

内部异常:

无法处理该消息。这很可能是因为操作'http://tempuri.org/IMyService/MyMethod'不正确,或者是因为消息包含无效或过期的安全上下文令牌,或者是因为绑定之间存在不匹配。如果服务因不活动而中止通道,则安全上下文令牌将无效。要防止服务过早地中止空闲会话,请增加服务端点绑定上的接收超时。

在后续的调用中,当我看到CommunicationState处于故障状态时,我会更新连接。但在进行方法调用之前,我找不到预先检查SCT是否过期的方法。


这基本上是我在这里提出的同一个问题:http://stackoverflow.com/questions/494906/automatically-resend-a-message-in-wcf非常有兴趣看到解决方案。我想看到一个使用内置WCF钩子的解决方案,不涉及更改代理类(因为我的是自动生成的)。 - davogones
Dasblonde 的下载似乎可以在这里找到:http://www.dasblonde.net/downloads/Proxies.zip - Charlesdwm
4个回答

5

在第一个答案的基础上,我提出了这个解决方案,它通用地包装了由svcutil.exe创建的自动生成的客户端代理:

public class ProxyWrapper<T> where T : ICommunicationObject
{
    private T _service;

    public ProxyWrapper()
    {
        _service = CreateNewInstance();
    }

    public void Invoke(Action<T> action)
    {
        try
        {
            action(_service);
        }
        catch (MessageSecurityException)
        {
            if (_service.State != CommunicationState.Faulted)
            {
                throw;
            }

            _service.Abort();
            _service = CreateNewInstance();

            action(_service);
        }
    }

    public TResult Invoke<TResult>(Func<T, TResult> func)
    {
        try
        {
            return func(_service);
        }
        catch (MessageSecurityException)
        {
            if (_service.State != CommunicationState.Faulted)
            {
                throw;
            }

            _service.Abort();
            _service = CreateNewInstance();

            return func(_service);
        }
    }

    private T CreateNewInstance()
    {
        Type type = typeof(T);
        return (T)type.GetConstructor(Type.EmptyTypes).Invoke(null);
    }
}

使用这个功能,您只需要做以下几步操作:
ProxyWrapper<ServiceClient> client = new ProxyWrapper<ServiceClient>();
client.Invoke(s => s.SomeAction());
int val = client.Invoke<int>(s => s.ReturnsAnInteger());

注意:由于我仅使用客户端代理的默认构造函数,因此仅支持此功能。


5
您可以通过使用委托来解决问题。这将允许安全地调用操作,并且如果失败,捕获异常,构造新的服务实例并再次执行操作。
using System;
using System.ServiceModel;
using System.ServiceModel.Security;

public static class Service
{
    private static IService _service;

    public static void Invoke(Action<IService> action)
    {
        try
        {
            action(_service);
        }
        catch (MessageSecurityException)
        {
            if (_service.State != CommunicationState.Faulted)
            {
                throw;
            }

            _service.Abort();
            _service = CreateFreshInstance();

            action(_service);
        }           
    }
}

您可以像这样调用您的帮助类:Service.Invoke(s => s.Method()); 来调用 IService.Method()。

0

1
无法找到http://www.dasblonde.net/2008/04/24/MyProxyWrapperAndTheEVILSUOFile.aspx。无法下载代理包装器的源代码(在会话过期时重试)。有人在github上可能有关于它的源代码吗? - PreguntonCojoneroCabrón

0
你可以尝试使用装饰器模式来处理WCF代理的异常。如果这条路对你是开放的,你可以考虑像这样设置来处理代理故障并重新初始化它以供调用者使用。随后的异常将被抛到调用者处。
//Proxy implements this
interface IMyService
{

  void MyMethod();

}

//Decorator 1
public class MyServiceProxyRetryingDecorator : IMyService
{

  //This is the real proxy that might be faulted
  private realProxy = new RealProxy();

  public void MyMethod()
  {
    ReEstablishProxyIfNecessary();
    //now just pass the call to the proxy, if it errors again, 
    //do more handling or let the exception bubble up
    realProxy.MyMethod();
  }

  private void ReEstablishProxyIfNecessary()
  {
    if(realProxy.CommunicationState == CommunicationState.Faulted)
    {
       realProxy.Abort();
       realProxy = new RealProxy();
    }
  }
}

装饰器的另一个版本可以处理您的MessageSecurityException,并在捕获它时重新初始化真实代理:

//Decorator 2
public class MyServiceProxyExceptionHandlingDecorator : IMyService
{

  //This is the real proxy that might be faulted
  private realProxy = new RealProxy();

  public void MyMethod()
  {
    try {realProxy.MyMethod(); } 
    catch (ExceptionYouAreInterestedIn ex)
    { 
    ReEstablishProxyIfNecessary(); 
    realProxy.MyMethod(); //do it again
    }
  }

  private void ReEstablishProxyIfNecessary()
  {
    if(realProxy.CommunicationState == CommunicationState.Faulted)
    {
       realProxy.Abort();
       realProxy = new RealProxy();
    }
  }
}

你的模式不起作用。整个问题在于,“realProxy”仅在调用MyMethod之后才进入故障状态,而不是之前。问题是如何预先发现代理将失败,因为客户端显然必须了解其SCT到期情况。 - mbp
我不确定你可以在调用方法之前通过 SCT 错误发现它会出错。我已经修改了我的帖子,展示如何处理异常,让客户端无需处理。 - Jimmy McNulty

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接