Обработка постоянного клиента WCF, переходящего в состояние с ошибками

У нас есть служба WCF, которую мы используем в веб-приложении. Используемый нами клиент был сгенерирован с помощью опции "Добавить ссылку на службу" в Visual Studio. Поскольку это веб-приложение, и поскольку природа приложения может привести к относительно коротким сеансам, мы решили создать экземпляр клиента, когда пользователь входит в систему, и сохранять его в течение всей жизни сеанса, а затем обрабатывать его по окончании сеанса.

Это подводит меня к моему вопросу - мы пытаемся решить, как лучше всего обработать канал клиента, входящий в состояние Faults. Обыскав некоторых, мы придумали это:

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
}

Но это пахнет. Очевидно, мы могли бы избежать этого, используя новый клиент и распределяя его для каждого звонка. Это кажется ненужным, но если это правильный путь, то я думаю, что это то, что мы выберем. Итак, каков наилучший способ изящной обработки определить, находится ли клиент в неисправном состоянии, а затем что-то с этим делать? Должны ли мы просто получать нового клиента для каждого звонка?

Следует помнить еще одну вещь - создание экземпляра клиента и вся эта проверка и обработка происходят в классе-оболочке для клиента. Если мы сделаем это так, как намеревались, это станет прозрачным для самого приложения - для выполнения вызовов и обработки исключений из них не требуется никакого специального кода.

2 ответа

Решение

Чтобы ответить на ваш вопрос, вы можете обработать событие Faulted свойства ChannelFactory следующим образом:

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

Это должно позволить вам выполнять все необходимые операции регистрации / очистки.

Как общая рекомендация, вы не должны оставлять канал открытым на время сеанса, поэтому убедитесь, что вы правильно закрываете канал (прерывание по исключению) после того, как закончите с ним.

Также, если возможно, рассмотрите возможность НЕ использовать ссылку на службу Visual Studio Add Service или, по крайней мере, очистить сгенерированный код / ​​конфигурацию. Я рекомендую, если вы хотите использовать прокси-реализацию, создайте свою собственную, производную от ClientBase, или используйте реализацию ChannelFactory. Поскольку вы упоминаете класс-оболочку, я бы порекомендовал вам использовать ChannelFactory и обрабатывать событие Faults для ваших нужд очистки.

Попробуйте обработать событие.Faults на клиентском прокси, например:

((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);
}

Он должен запускать моментальные сбои канала, давая вам возможность вновь открыть его.

Вы все равно должны также обернуть каждый вызов client метод в блоке try-catch, и, возможно, даже обернуть это в while() цикл, который повторяет вызов n раз, а затем регистрирует ошибку. НАПРИМЕР:

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 прокси.

Другие вопросы по тегам