1

Редактировать См. Заголовок «Проблема» в конце моего вопроса, чтобы взломать этот вопрос.Цепочные задачи в csharp с успехом и обработчиком ошибок

Исходя из nodejs, где мы могли бы связывать обещания, на C# Я вижу Async Tasks, почти сопоставимые. Вот моя попытка.

Edit - Я не могу отметить мои методы звонящего уровня убер как async в качестве библиотеки на основе длл называет его объект

Caller

public void DoSomething(MyRequest request) 
{ 
    Delegate.Job1(request) 
     .ContinueWith(Delegate.Job2) 
     .ContinueWith(Fault, TaskContinuationOptions.OnlyOnFaulted) 
     .ContinueWith(Result); 
} 

public void Result(Task<MyRequest> task) 
{ 
    MyRequest request = task.Result; 
    Console.Writeline(request.result1 + " " + request.result2); 
} 

public void Fault(Task<MyRequest> task) 
{ 
    MyRequest request = task.Result; 
    Console.Writeline(request.result); 
} 

Делегат Объект

public async Task<MyRequest> Job1(MyRequest request) 
{ 
    var data = await remoteService.Service1Async(); 
    request.result1 = data; 
    return request; 
} 

public async Task<MyRequest> Job2(Task<MyRequest> task) 
{ 
    var data = await remoteService.Service2Async(); 
    request.result2 = data; 
    return request; 
} 

Проблема:

1) Edit (фиксировано, связанный длл к моему проекту пропускал это связано DLL) Task.Result (запрос) приходит в нуль в методе Result, также Status = Faulted

2) Кроме того, это Правильно ли работает ошибка? Я ожидаю, что Fault будет вызываться только тогда, когда исключение происходит в методах делегата, иначе оно должно пропустить.

2-б) Другим вариантом является проверка в рамках функции Result (удалить Fault функцию), если Task.status = RanTocompletion и ветви там успеха или вина

Редактировать после ответа

У меня есть Гоча, что делать, если я не могу сделать мой контроллер асинхронным.

Контроллер

public void ICannotBeAsync() 
{ 
    try 
    { 
     var r = await caller.DoSomething(request); // though I can use ContinueWith here, ??? 
    } 
    catch(Exception e) 
    { 
     //exception handling 
    } 
} 

Caller

public async Task DoSomethingAsync(MyRequest request) 
{ 
    request.result1 = await delegateInstance.Job1(request); 
    request.result2 = await delegateInstance.Job2(request); 
    Console.Writeline(request.result1 + " " + request.result2); 
    return result; 
} 

Edit 2 - на основе VMAtm Edit, пожалуйста, просмотрите опцию OnlyOnFaulted (Fault).

Delegate.Job1(request) 
    .ContinueWith(_ => Delegate.Job2(request), TaskContinuationOptions.OnlyOnRanToCompletion) 
    .ContinueWith(() => {request.result = Task.Exception; Fault(request);}, TaskContinuationOptions.OnlyOnFaulted) 
    .ContinueWith(Result, TaskContinuationOptions.OnlyOnRanToCompletion); 

Проблема -

Дал ему испытание, фактический код ниже, ни один из Result или Fault становится называется, хотя метод GetCustomersAsync вернулся успешно. Мое понимание все останавливается на Fault, потому что оно помечено для запуска только на Fault, выполнение останавливается там, а обработчик Result не вызывается.

Customer.GetCustomersAsync(request) 
    .ContinueWith(_ => { Debug.WriteLine("not executing"); Fault(request); }, TaskContinuationOptions.OnlyOnFaulted) 
    .ContinueWith(_ => { Debug.WriteLine("not executing either"); Result(request); }, TaskContinuationOptions.OnlyOnRanToCompletion); 

Редактировать 3 Опираясь на ответ EVK в.

Task<Request> task = Customer.GetCustomersAsync(request); 
task.ContinueWith(_ => Job2Async(request), TaskContinuationOptions.OnlyOnRanToCompletion); 
task.ContinueWith(_ => Job3Async(request), TaskContinuationOptions.OnlyOnRanToCompletion); 
task.ContinueWith(_ => Result(request), TaskContinuationOptions.OnlyOnRanToCompletion); 
task.ContinueWith(t => { request.Result = t.Exception; Fault(request); }, TaskContinuationOptions.OnlyOnFaulted); 
+1

Вы бы просто просто ждать каждой задачи, то попробуйте поймать его. Узел JS Promises должен был предотвратить аддон. Async/Await в C# предотвращает обратный ад. Таким образом, нет необходимости передавать через Node Js мышление на C#, поскольку он уже имеет дело с этим сценарием. –

+0

Мне нравится цепочка, которая поставляется с реализацией Task, я буду использовать ее сильно, но просто не могу ее получить прямо сейчас, я не уверен, почему 'Task.Result' приходит как null в моей функции« Результат » , любые исправления? – user2727195

+4

Я бы посоветовал вам следовать более стилю C#, потому что если у вас есть другие разработчики или вы смотрите на документацию для фреймворков, вы/они могут запутаться, потому что у вас есть своего рода сдвиг парадигмы. Кроме того, вы можете столкнуться с проблемами позже, изменив свои реализации при изменении требований. –

ответ

0

Многое было здесь сказано, так что я только ответить на последний " раздел Проблема»:

Customer.GetCustomersAsync(request) 
    .ContinueWith(_ => { Debug.WriteLine("not executing"); Fault(request); }, TaskContinuationOptions.OnlyOnFaulted) 
    .ContinueWith(_ => { Debug.WriteLine("not executing either"); Result(request); }, TaskContinuationOptions.OnlyOnRanToCompletion); 

Проблема здесь (и в исходном примере тоже) является то, что происходит следующее:

  1. Вы продолжаете GetCustomersAsync с продолжением «только при неисправности».
  2. Затем вы продолжаете , что продолжение, а не GetCustomersAsync, со следующим продолжением, которое может работать только при завершении.

В результате оба continations могут выполнять только когда GetCustomersAsync терпит неудачу. Чтобы исправить:

var request = Customer.GetCustomersAsync(request); 
request.ContinueWith(_ => { Debug.WriteLine("not executing"); Fault(request); }, TaskContinuationOptions.OnlyOnFaulted); 
request.ContinueWith(_ => { Debug.WriteLine("not executing either"); Result(request); }, TaskContinuationOptions.OnlyOnRanToCompletion); 

Обратите внимание, что даже если вы не можете изменить подпись каким-либо способом, и это абсолютно необходимо вернуть пустоту, вы можете пометить его как асинхронном:

public async void DoSomethingAsync(MyRequest request) 
{ 
    try { 
     await Customer.GetCustomersAsync(request); 
     Result(request);  
    } 
    catch (Exception ex) { 
     Fault(request); 
    } 
} 
+0

интересный подход, теперь, если мы расширим это, допустим, я добавил еще 5 «цепочек ContinueWith async operations» с опцией 'OnlyOnRanToCompletion' в строке # 2 вашего кода и нажмите' Fault' (строка 2) и 'Result' (строка 3) вниз, будет ли он работать должным образом, то есть задачи будут продолжаться, выполняя весь путь, чтобы добиться успеха, и если он не удастся, он прыгнет прямо к ошибке, пропуская любые промежуточные операции. – user2727195

+0

просмотрите Edit 3 на основе вашего ответа, в обработчике ошибок я присвоил результат из 't.Exception', чтобы позволить вызывающему абоненту или запрашивающим клиентам знать, что пошло не так. – user2727195

+0

Выглядит хорошо, но обеспечивает правильную обработку ошибок: если Job2Async или Job3Async выдаст исключение, он будет незаметным и может привести к сбою всего процесса. Если в Job2Async и Job3Async есть блок try-catch, вы должны быть в порядке. Конечно, ожидание async намного чище и гораздо менее подвержено ошибкам, но если вам абсолютно необходимо использовать ContinueWIth - должно быть достаточно. – Evk

1

Есть несколько проблем с этим кодом:

Delegate.Job1(request) 
    .ContinueWith(Delegate.Job2) 
    .ContinueWith(Fault, TaskContinuationOptions.OnlyOnFaulted) 
    .ContinueWith(Result); 

Прежде всего, вы продолжаете выполнение с Delegate.Job2 даже если Delegate.Job1 не удалось. Поэтому вам нужно значение OnlyOnRanToCompletion. Подобно продолжению Result, вы продолжаете во всех случаях, поэтому задача с ошибкой все еще проходит через цепочку и, как вы уже видите, находится в Faulted состоянии с null.

Итак, ваш код, если вы не можете использовать на этом уровне await, может быть таким (также, как указано в @Evk, вам пришлось добавить обработку исключений ко всему вашему коду, что действительно уродливо) :

Delegate.Job1(request) 
    .ContinueWith(Delegate.Job2, TaskContinuationOptions.OnlyOnRanToCompletion) 
    .ContinueWith(Fault, TaskContinuationOptions.OnlyOnFaulted) 
    .ContinueWith(Result, TaskContinuationOptions.OnlyOnRanToCompletion) 
    .ContinueWith(Fault, TaskContinuationOptions.OnlyOnFaulted); 

Тем не менее, все еще есть возможность использовать await ключевое слово внутри вашего метода и после этого использовать лямбда, чтобы запустить его синхронно, если это вариант для вас:

public async Task DoSomethingAsync(MyRequest request) 
{ 
    try 
    { 
     request.result1 = await delegateInstance.Job1(request); 
     request.result2 = await delegateInstance.Job2(request); 
     Console.Writeline(request.result1 + " " + request.result2); 
     return result; 
    } 
    catch(Exception e) 
    { 

    } 
} 

public void ICannotBeAsync() 
{ 
    var task = Task.Run(() => caller.DoSomethingAsync(request); 
    // calling the .Result property will block current thread 
    Console.WriteLine(task.Result); 
} 

Исключение обработка может быть выполнена на обоих уровнях, так что это p вам, где его представить. Если что-то пойдет не так во время выполнения, то свойство Result поднимет AggregateException в качестве обертки для внутренних исключений, произошедших во время разговора. Также вы можете использовать метод Wait для задачи, завернутый в пункт try/catch, и после этого проверьте состояние задачи и обработайте его по мере необходимости (он имеет IsFaulted, IsCompleted, IsCanceled свойства boolean).

Кроме того, настоятельно рекомендуется использовать некоторую логику отмены для задач, ориентированных на задачи, чтобы отменить ненужную работу. Вы можете начать с this MSDN article.

Update, основываясь на других вопросах:

Если вы все еще хотите использовать ContinueWith вместо await, и хотите изменить подписи Job1, Job2 методы, вы должны изменить свой код так:

Delegate.Job1(request) 
    .ContinueWith(_ => Delegate.Job2(request), TaskContinuationOptions.OnlyOnRanToCompletion) 
    .ContinueWith(Result, TaskContinuationOptions.OnlyOnRanToCompletion) 
    .ContinueWith(Fault, TaskContinuationOptions.OnlyOnFaulted); 

причина этого заключается в том, что метод ContinueWith принимает Func<Task, Task>, потому что вам нужно, в общем, проверить задание на его статус и/или его результат.

Что касается вопроса о не блокирует вызывающий абонент, вы можете попробовать TaskCompletionSource<TResult> класс, что-то вроде этого:

public void ICannotBeAsync() 
{ 
    var source = new TaskCompletionSource<TResult>(); 
    var task = Task.Run(() => caller.DoSomethingAsync(request, source); 
    while (!source.IsCompleted && !source.IsFaulted) 
    { 
     // yeild the execution to other threads for now, while the result isn't available 
     Thread.Yeild(); 
    } 
} 

public async Task DoSomethingAsync(MyRequest request, TaskCompletionSource<TResult> source) 
{ 
    request.result1 = await delegateInstance.Job1(request); 
    request.result2 = await delegateInstance.Job2(request); 
    Console.Writeline(request.result1 + " " + request.result2); 
    source.SetResult(result); 
} 
+0

Хорошо, что я могу сделать с сигнатурами 'Job1' и' Job2', есть несогласованность, возможно ли, чтобы оба метода получали 'request' вместо' Task ', я пробовал с лямбдой' (prevTask) => Делегат .Job2 (запрос) ', любые идеи – user2727195

+0

@ user2727195 В каком случае? Для использования 'await' или для продолжения? – VMAtm

+0

для continueWith – user2727195