C# TaskCompletionSource не работает

У меня есть интересная проблема.. У меня есть метод входа, который работает сервис WCF.

Я создал задание завершения и ждет, пока результат не придет.

Проблема в том, что если я вызываю 2 раза метод входа, второй ничего не возвращает. Я ставлю точку останова, и она входит в завершенное событие и вызывает trysetresult, но ничего не возвращает.

вот мой код

    public Task<User> LoginByUserName(string userName, string password)
    {
        var tcs = new TaskCompletionSource<User>();

        if (!_registeredEventList.Contains ("LoginByUserNameCompleted")) {
            _registeredEventList.Add ("LoginByUserNameCompleted");


            userService.LoginByUserNameCompleted += (object sender, LoginByUserNameCompletedEventArgs args) => {
                if (args.Error != null)
                    tcs.TrySetException (args.Error);
                if (args.Result != null)
                    tcs.TrySetResult (args.Result);
                else
                    tcs.TrySetResult (null);

            };

        }

        userService.LoginByUserNameAsync (userName,password);
        return tcs.Task;
    }

Я так называю;

var loginResult= await Task.Run(()=>serviceHelper.LoginByUserName(userName,password));

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

PS: _registeredEventList содержит, если событие уже подписано или нет. Если да, то он больше не создается. Когда я удаляю эту часть, она работает.

1 ответ

Как прокомментировал Эвк, проблема в том, что у вашего кода есть условие, при котором он никогда не вернет выполненное задание. В частности, при первом вызове этого кода он добавит запись в _registeredEventList (который, вероятно, никогда не удаляется). Все последующие вызовы вернут Task это никогда не завершается, что является основным нет-нет в асинхронном программировании.

Чтобы это исправить, я рекомендую изменить оболочку EAP, отменив подписку как часть обратного вызова:

public static Task<User> LoginByUserNameTaskAsync(this UserService @this, string userName, string password)
{
  var tcs = new TaskCompletionSource<User>();
  LoginByUserNameCompletedDelegate callback = null;
  callback = (object sender, LoginByUserNameCompletedEventArgs args) =>
  {
    @this.LoginByUserNameCompleted -= callback;
    if (args.Error != null)
      tcs.TrySetException(args.Error);
    else
      tcs.TrySetResult(args.Result);
  };
  @this.LoginByUserNameCompleted += callback;

  @this.LoginByUserNameAsync(userName, password);
  return tcs.Task;
}

(Я также сделал это методом расширения и следил за параметрами именования TAP для оболочек TAP-over-EAP).

Я только что решил ту же проблему. Для меня это не наследование IDisposable и добавление следующего

public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposing)
    {
        if (!disposed && disposing)
        {
            _channel?.Dispose();
            _connection?.Dispose();
        }

        this.disposed = true;
    }
Другие вопросы по тегам