Как заставить исключение из задачи наблюдаться в задаче продолжения?

У меня есть задача выполнить HttpWebRequest с помощью

 Task<WebResponse>.Factory.FromAsync(req.BeginGetRespone, req.EndGetResponse)

который, очевидно, может потерпеть неудачу с WebException. Вызывающему абоненту я хочу вернуть Task<HttpResult>, где HttpResult — вспомогательный тип для инкапсуляции ответа (или нет). В этом случае ответ 4xx или 5xx не является исключением.

Поэтому к задаче запроса я прикрепил два продолжения. Один с TaskContinuationOptions OnlyOnRanToCompletion, а другой с OnlyOnOnFaulted. А затем завернули все это в Task<HttpResult>, чтобы получить один результат в зависимости от того, какое продолжение завершится.

Каждая из трех дочерних задач (запрос плюс два продолжения) создается с параметром AttachedToParent.

Но когда вызывающий объект ожидает возвращенную внешнюю задачу, выдается AggregateException, если запрос не выполнен.

Я хочу, чтобы в продолжении с ошибкой наблюдалось WebException, чтобы клиентский код мог просто посмотреть на результат. Добавление Wait в броски продолжения при ошибке, но try-catch вокруг этого не помогает. Не смотрите и на свойство Exception (поскольку раздел «Наблюдение за исключениями с помощью свойства Task.Exception» намекает здесь).

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

Учитывая экземпляр неисправного Task<T>, есть ли способ пометить его как «обработанный сбой»?

Упрощенный код:

public static Task<HttpResult> Start(Uri url) {
    var webReq = BuildHttpWebRequest(url);
    var result = new HttpResult();
    var taskOuter = Task<HttpResult>.Factory.StartNew(() => {
        var tRequest = Task<WebResponse>.Factory.FromAsync(
                            webReq.BeginGetResponse,
                            webReq.EndGetResponse,
                            null, TaskCreationOptions.AttachedToParent);
        var tError = tRequest.ContinueWith<HttpResult>(
                            t => HandleWebRequestError(t, result),
                            TaskContinuationOptions.AttachedToParent
                            |TaskContinuationOptions.OnlyOnFaulted);
        var tSuccess = tRequest.ContinueWith<HttpResult>(
                            t => HandleWebRequestSuccess(t, result),
                            TaskContinuationOptions.AttachedToParent
                            |TaskContinuationOptions.OnlyOnRanToCompletion);
        return result;
    });

    return taskOuter;
}

с участием:

private static HttpDownloaderResult HandleWebRequestError(
                                        Task<WebResponse> respTask, 
                                        HttpResult result) {
    Debug.Assert(respTask.Status == TaskStatus.Faulted);
    Debug.Assert(respTask.Exception.InnerException is WebException);
    // Try and observe the fault: Doesn't help.
    try {
        respTask.Wait();
    } catch (AggregateException e) {
        Log("HandleWebRequestError: waiting on antecedent task threw inner: "
             + e.InnerException.Message);
    }
    // ... populate result with details of the failure for the client ...
    return result;
}

(HandleWebRequestSuccess в конечном итоге выделит дополнительные задачи, чтобы получить содержание ответа...)

Клиент должен иметь возможность дождаться выполнения задачи, а затем просмотреть ее результат, не выбрасывая ее из-за ожидаемой и уже обработанной ошибки.


person Richard    schedule 19.02.2010    source источник
comment
TaskScheduler.UnobservedTaskException не увольняют, даже несмотря на то, что большинство (если не все) мест, где AggregateException будет брошено, удалены.   -  person Richard    schedule 19.02.2010


Ответы (1)


В конце концов я выбрал самый простой способ, который только мог придумать: скрыть исключение. Это возможно, потому что WebException имеет свойство Response, которое дает доступ к HttpWebResponse, который я хочу:

var requestTask = Task<WebResponse>.Factory.FromAsync(
                        webReq.BeginGetResponse,
                        ia => {
                          try {
                            return webReq.EndGetResponse(ia);
                          } catch (WebException exn) {
                            requestState.Log(...);
                            return exn.Response;
                          }
                        });

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

person Richard    schedule 19.04.2010