2017-01-18 10 views
1

Я пытаюсь написать функцию, которая использует Task и TaskCompletion.Задание ожидания не возвращает результат

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

public async Task<byte[]> Sign(byte[] documentContent) 
{ 
    var service = new SignServiceWrapper("https://example.com?wsdl"); 
    var loginResult = await Task.Run(() => service.Login(loginRequest)); 

    //.... 
} 

и мой класс SignServiceWrapper

public class SignServiceWrapper 
{ 
    private static string _webServiceUrl; 
    private BrokerClientClient client; 

    public SignServiceWrapper(string webServiceUrl) 
    { 
     _webServiceUrl = webServiceUrl; 
    } 

    public Task<loginResponse> Login(loginRequest request) 
    { 
     var tcs = new TaskCompletionSource<loginResponse>(); 

     ClientGenerator.WebServiceUrl = _webServiceUrl; 

     ClientGenerator.InitializeService(); 
     client = ClientGenerator.ServiceClient; 

     client.loginCompleted += (sender, loginResult) => 
     { 
      if (loginResult.Error != null) 
       tcs.SetException(loginResult.Error); 
      else 
       tcs.TrySetResult(loginResult.Result); 
     };  

     client.loginAsync(request);     

     return tcs.Task; 
    } 

    // ... 
} 

Если я называю функцию входа, как, что он работает

var loginResult = Task.Run(() => service.Login(loginRequest)); 
loginResult.Wait(); 

Я знаю, что это своего рода тупик, но я не знаю, как решить это здесь и какой объект.

+0

Как долго вы ждали завершения задания? Вы пробовали выполнить задачу, чтобы убедиться, что она обрабатывается? – Takarii

+0

Я положил точку останова в loginCompleted событие, и результат приходит туда, но проблема в том, что он не возвращается – ertan2002

+0

'await' не сломался. Ваш код довольно необычен - вызовы Task.Run * и * TCS * и * веб-службы, которые уже имеют метод loginAsync? Почему бы вам не удалить метод * whole * 'Login' и просто вызвать' var response = wait client.loginAsync (request); '? –

ответ

2

Вот рабочий .NET Fiddle.

Я думаю, что ваш .Login способ пытается сделать слишком много. Первое, что я заметил (и могу только представить, как это реализовано) - статический ClientGenerator, который имеет статическое изменяемое состояние. Это вызывает тревогу и очень специфический запах кода. Мне очень хотелось бы посмотреть, как выглядит клиент и как это реализовано, поскольку это, безусловно, поможет лучше ответить на этот вопрос.

Основываясь на том, что вы поделились до сих пор (и предполагая, что client.loginAsync возвращает Task<loginResponse>), я бы сказал, что вы могли бы сделать следующее:

public class SignServiceWrapper 
{ 
    private static string _webServiceUrl; 
    private BrokerClientClient client; 

    public SignServiceWrapper(string webServiceUrl) 
    { 
     _webServiceUrl = webServiceUrl; 
    } 

    public Task<loginResponse> LoginAsync(loginRequest request) 
    { 
     ClientGenerator.WebServiceUrl = _webServiceUrl; 
     ClientGenerator.InitializeService(); 
     client = ClientGenerator.ServiceClient; 

     return client.loginAsync(request); 
    } 

    // ... 
} 

Вы могли бы потреблять это так:

public async Task<byte[]> Sign(byte[] documentContent) 
{ 
    var service = new SignServiceWrapper("https://example.com?wsdl"); 
    var loginResult = await service.LoginAsync(loginRequest); 

    //... 
} 

Если client.loginAsync не возвращает то, что вы ищете, тогда вам нужно будет подойти к этому делать something similar to your current approach. Или, если вы заблокированы на event-based async pattern, у вас есть другие соображения - например, хотите ли вы поддержать отмену, IsBusy, прогресс, дополнительные результаты, и если у вас есть возможность, чтобы аргументы события наследовали System.ComponentModel.AsyncCompletedEventArgs и т. Д.

Один окончательного рассмотрения, если client.loginAsync является Task возвращение, даже если он не вернет loginResponse вам нужно ждать его следующим образом:

public async Task<loginResponse> Login(loginRequest request) 
{ 
    var tcs = new TaskCompletionSource<loginResponse>(); 

    ClientGenerator.WebServiceUrl = _webServiceUrl; 
    ClientGenerator.InitializeService(); 
    client = ClientGenerator.ServiceClient; 
    client.loginCompleted += (sender, loginResult) => 
    { 
     if (loginResult.Error != null) 
      tcs.SetException(loginResult.Error); 
     else 
      tcs.TrySetResult(loginResult.Result); 
    };  

    await client.loginAsync(request);     

    return tcs.Task; 
} 

Update

После обсуждения с ОП этот .NET Fiddle, казалось, соответствовал его потребностям.

+0

Спасибо за ваш ответ, но loginAsync метод - это метод void, поэтому ждать не получится! Ну, я решил проблему, используя var varRes (=) service.Login (loginRequest). – ertan2002

+0

Это не решает проблему, а скорее настраивает себя на другую тупиковую ситуацию. У вас есть контроль над '.loginAsync' и этим классом клиента? –

+0

Да, я знаю, что это не правильное решение :) loginAsync - это функция webservice, и я не могу получить доступ к веб-сервису, потому что это не наш проект, а внешний источник. – ertan2002

-1

Изменение var loginResult = await Task.Run(() =>service.Login(loginRequest)); Для var loginResult = await service.Login(loginRequest);

+1

Этого, вероятно, недостаточно, так как метод 'Login' также смешивает асинхронные и неасинхронные методы. Обратите внимание на TCS * и * метод loginAsync. Проблема может возникнуть в 'ClientGenerator' –