0

我有一个有趣的问题。我有一个登录方法,它适用于 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 持有是否已经订阅了事件。如果是,则它不会再次创建。当我删除该部分时,它可以工作。

4

2 回答 2

0

正如 Evk 评论的那样,问题在于您的代码有一个条件,它永远不会返回已完成的任务。具体来说,第一次调用此代码时,它将添加一个条目_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-over-EAP 包装器的 TAP 命名参数)。

于 2016-06-08T20:26:11.367 回答
-1

我刚刚解决了同样的问题。对我来说,它没有继承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;
    }
于 2020-06-29T16:29:30.813 回答