Джефф, я не совсем уверен из твоего вопроса, как ты имитируешь сбой соединения. Я предполагаю, что «как будто сервер не отвечает» означает, что вы ищете какую-то ошибку тайм-аута. Вы, вероятно, имитируете неотвечающий сервер, форсируя медленный ответ на ваш вызов службы с помощью Thread.Sleep () на стороне сервера. Правильно? Достаточно близко?
У меня был похожий проект, поэтому я протестировал его и смог успешно перехватить тайм-аут и другие исключения. Я думаю, что вы, возможно, не видели этих исключений, потому что ваше пользовательское связывание имеет очень большой тайм-аут по умолчанию, и вы, возможно, не ждали достаточно долго.
Для пояснения, время ожидания WCF контролируется конфигурацией привязки. Глядя на свой код, вы создаете свою собственную привязку, подобную этой:
var elements = new List<BindingElement>();
elements.Add(new BinaryMessageEncodingBindingElement());
elements.Add(new HttpTransportBindingElement());
var binding = new CustomBinding(elements);
Если вы установите точку останова и осмотрите созданную вами пользовательскую привязку, вы увидите, что она имеет одну минуту SendTimeout по умолчанию. Я подозреваю, что вы либо не ожидаете достаточно долго, либо не устанавливаете симулированный тайм-аут своей службы достаточно долго, поэтому вы не смогли поймать исключение тайм-аута.
Вот некоторые изменения кода для демонстрации. Во-первых, чтобы установить время ожидания для привязки:
var binding = new CustomBinding(elements);
//Set the timeout to something reasonable for your service
//This will fail very quickly
binding.SendTimeout = TimeSpan.FromSeconds(1);
Наконец, чтобы перехватить исключения и вызвать правильные события, вы можете обновить свой делегат AsyncCallback следующим образом. Исключение выдается при вызове EndGet ().
AsyncCallback asyncCallBack = delegate(IAsyncResult result)
{
IFeedService service = ((IFeedService)result.AsyncState);
try
{
var items = service.EndGet(result);
ItemsRetrieved(this, EventArgs.Empty);
}
catch (System.TimeoutException ex)
{
//Handles timeout
ServiceCallError(this, EventArgs.Empty);
}
catch (System.ServiceModel.CommunicationException ex)
{
//Handles a number of failures:
// Lack of cross-domain policy on target site
// Exception thrown by a service
ServiceCallError(this, EventArgs.Empty);
}
catch (System.Exception ex)
{
//Handles any other errors here
ServiceCallError(this, EventArgs.Empty);
}
};
Что касается общего обзора кода, я бы порекомендовал вам избегать жесткого кодирования конфигурации привязки. Вместо этого вы можете объявить свою конфигурацию конечной точки (включая разумные тайм-ауты) в файле ServiceReferences.ClientConfig и создать фабрику канала с именем конечной точки, например:
new ChannelFactory<IFeedService>("feedServiceEndpoint");
Это должно сделать приложение более удобным для обслуживания.
Надеюсь, это поможет.
Jerry
Обновлен:
Джеф,
Пожалуйста, посмотрите на этот код и комментарии внутри, чтобы получить более подробное объяснение того, что здесь происходит. Метод MakeCall () создает функцию (делегат), которая передается методу BeginGet (). Эта функция выполняется позже, когда служба отвечает.
Пожалуйста, внесите предложенные изменения и установите точки останова в строках, начинающихся с «AsyncCallback asyncCallback» и «var items». Вы увидите, что первый проход через отладчик просто объявляет код (функцию / делегат), который будет выполнен, когда служба ответит, а второй проход - фактическая обработка этого ответа, начиная с внутренней части вашего объявления делегата. Это означает, что внешняя попытка / отлов не будет находиться в области действия при обработке ответа на вызов службы.
public void MakeCall(DateTime lastTime, Dictionary<string, string> context)
{
try
{
AsyncCallback asyncCallBack = delegate(IAsyncResult result)
{
try
{
var items = ((IFeedService)result.AsyncState).EndGet(result);
if (ItemsRetrieved != null)
ItemsRetrieved(this, new ServiceCallerEventArgs(items));
}
catch (Exception ex)
{
//This will catch errors from the service call
}
};
_channel.BeginGet(lastTime, context, asyncCallBack, _channel);
}
catch(Exception ex)
{
//This will not catch an error coming back from the service. It will
//catch only errors in the act of calling the service asynchronously.
//The communication with the service and response is handled on a different
//thread, so this try/catch will be out of scope when that executes.
//So, at this point in the execution, you have declared a delegate
//(actually an anonymous delegate the compiler will turn into a hidden class)
//which describes some code that will be executed when the service responds.
//You can see this in action by setting a breakpoint just inside the delegate
//at the line starting with "var items". It will not be hit until the service
// responds (or doesn't respond in a timeout situation).
}
}
Jerry