Обновление клиента WCF по истечении срока действия SCT?

Я использую WCF для конечной точки мыла, используя режим безопасности "TransportWithMessageCredential".

Клиент/сервер WCF использует SCT (токен Контекста Безопасности) для обеспечения безопасного соединения и работает как обычно в общем случае.

Однако, после периода бездействия, SCT будет истек, а следующий вызов метода вызовет сообщение MessageSecurityException:

От другой стороны был получен необеспеченный или неправильно защищенный отказ. См. Внутреннее исключение FaultException для кода ошибки и подробного описания

Внутреннее исключение:

Сообщение не может быть обработано. Это, скорее всего, потому, что действие " http://tempuri.org/IMyService/MyMethod 'неверно или потому, что сообщение содержит недействительный или истекший токен контекста безопасности или потому, что есть несоответствие между привязками. Токен контекста безопасности был бы недействительным, если служба прервала канал из-за неактивности. Чтобы предотвратить прерывание сеансов бездействия, служба преждевременно увеличивает тайм-аут приема на привязку конечной точки службы.

При последующих вызовах я обновляю соединение, когда вижу, что CommunicationState не работает. Но я не могу найти способ превентивно проверить, закончился ли SCT до вызова метода.

Ответ 2

Вы можете решить свои проблемы, используя делегатов. Это позволит безопасно вызывать действие и, если не удается, поймать исключение, создать новый экземпляр службы и снова выполнить действие.

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().

Ответ 3

Основываясь на первом ответе, я придумал это решение, которое в общих чертах обертывает автоматически сгенерированные клиентские прокси, созданные 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());

Примечание.. Поскольку я использую только конструктор по умолчанию для клиентских прокси, все это поддерживает.

Ответ 4

Возможно, вы сможете использовать шаблон декоратора для обработки исключений с помощью прокси 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();
    }
  }
}