Обрабатывать постоянный клиент WCF, вводящий состояние с ошибкой
У нас есть служба WCF, которую мы потребляем из веб-приложения. Клиент, который мы используем, был создан с использованием опции "Добавить ссылку на службу" Visual Studio. Поскольку это веб-приложение, и поскольку характер приложения, скорее всего, приведет к относительно коротким сеансам, мы решили создать экземпляр клиента, когда пользователь входит в систему и сохраняет его в течение всего сеанса, а затем обрабатывать его, когда сеанс проходит.
Это подводит меня к моему вопросу - мы пытаемся решить лучший способ обработки клиентского канала, входящего в состояние Faulted. После поиска по некоторым причинам мы пришли к следующему:
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
}
Но это пахнет. Очевидно, мы могли бы избежать этого, используя нового клиента и избавляясь от него для каждого вызова. Это кажется ненужным, но если это правильный путь, то я предполагаю, что мы выберем. Итак, каков наилучший способ изящно описать, находится ли клиент в неисправном состоянии, а затем что-то делать? Должны ли мы просто получать нового клиента для каждого звонка?
Еще одна вещь, о которой нужно помнить - создание клиента и вся эта проверка и обработка происходит в классе-оболочке для клиента. Если мы сделаем это так, как мы намеревались, он прозрачен для самого приложения - для выполнения вызовов и обработки исключений из них не требуется никакого специального кода.
Ответы
Ответ 1
Чтобы ответить на ваш вопрос, вы можете обработать Faulted event свойства ChannelFactory следующим образом:
client.ChannelFactory.Faulted += new EventHandler(ChannelFactory_Faulted);
Это должно позволить вам выполнять любые протоколирования/очистки, которые вам нужно выполнить.
Как общая рекомендация, вы не должны оставлять канал открытым в течение всего сеанса, поэтому убедитесь, что вы закрываете канал правильно (прерывание после исключения) после того, как вы закончите с ним.
Также, если это возможно, рассмотрите НЕ использование Visual Studio Add Service Reference или, по крайней мере, очистку создаваемого кода/конфигурации. Я рекомендую, чтобы, если вы хотите использовать прокси-реализацию, создайте свой собственный, создав ClientBase или используйте ChannelFactory. Поскольку вы упоминаете класс-оболочку, я бы рекомендовал вам использовать ChannelFactory и обрабатывать Faulted event для ваших нужд очистки.
Ответ 2
Попробуйте обработать событие .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);
}
Он должен инициировать мгновенные ошибки канала, что дает вам возможность повторно открыть его.
Вы также должны обернуть каждый вызов методу 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
.