Обработка постоянного клиента WCF, входящего в состояние с ошибками - PullRequest
19 голосов
/ 28 марта 2011

У нас есть служба 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 ]

20 голосов
/ 29 марта 2011

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

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

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

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

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

13 голосов
/ 29 марта 2011

Попробуйте обработать событие .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 раз, а затем регистрирует ошибку. 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.

...