0

I have an interesting problem.. I have a login method which works a WCF service.

I created a taskcompletion and waits until result is come.

Well problem is, if I call 2 times login method, the second one does not return anything. I put break point and it enters the completed event and it calls trysetresult but nothing return.

here is my code

    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;
    }

I call like that;

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

For example, if user one time entered wrong login info, in the second try, nothing will return.

PS: _registeredEventList holds if event is already subscribed or not. If yes then it does not creat again. When I delete that part, it works.

unbalanced
  • 1,192
  • 5
  • 19
  • 44
  • 2
    If your event is registered already you basically just return Task that does nothing (tcs variable is not used). – Evk Jun 08 '16 at 16:40
  • @Evk, thank you for your reply, but i dont know how to solve it? – unbalanced Jun 08 '16 at 16:42
  • Hard to tell given just code provided, but one things that comes to mind is store all TaskCompletionSource in a list (field) and when LoginByUserNameCompleted fires - set result of _all_ task completion sources. – Evk Jun 08 '16 at 16:45
  • @Evk, I am not sure if it solves the problem, because the event is triggered and TrySetResult is worked.. but it does not return. I think I will unsubscribe event and subscribe again, it seems one solution for now – unbalanced Jun 08 '16 at 16:52

2 Answers2

0

As Evk commented, the problem is that your code has a condition where it will never return the completed task. Specifically, the first time this code is called, it will add an entry to _registeredEventList (which is presumably never removed). All later invocations will return a Task that is never completed, which is a major no-no in asynchronous programming.

To fix this, I recommend modifying your EAP wrapper by unsubscribing as part of the callback:

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;
}

(I also made it an extension method and had it follow the TAP naming parameters for TAP-over-EAP wrappers).

Stephen Cleary
  • 437,863
  • 77
  • 675
  • 810
-1

I have just solved the same problem. For me its was not inheriting IDisposable and adding the following

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

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

        this.disposed = true;
    }
Ashley Kilgour
  • 1,110
  • 2
  • 15
  • 33