Tengo una funcionalidad de búsqueda de usuarios. He proporcionado una vista de texto y en ese método cambiado de vista de texto, estoy activando un método para obtener datos del servidor web. Pero me enfrento a un problema cuando el usuario escribe una letra, porque todos los accesos de la API se realizan en una tarea asíncrona. El servicio debe activarse después de 100 milisegundos de espera, lo que significa que si el usuario escribe una letra "a" y luego no escribe durante 100 milisegundos, entonces tenemos que activar el servicio. Pero si el usuario escribe "a", luego "b" y luego "c", por lo que se debe presionar un servicio para "abc", no para todos.

Seguí el enlace oficial, pero no me ayuda https://msdn.microsoft.com/en-us/library/jj155759.aspx

Así que básicamente aquí está mi código

textview.TextChange+= (sender,e) =>{
  CancellationTokenSource cts = new CancellationTokenSource();
      await Task.Delay(500); 
      // here some where I have to pass cancel token
      var lst = await APIClient.Instance.GetUserSearch("/user/get?searchTerm=" + newText, "application/json",cts);
      if (lst != null && lst.Count > 0){ 
        lstSearch.AddRange(lst);
      }
  }

Aquí está mi método para GetUser

 public async Task<JResponse> GetUserSearch<JResponse>(string uri, string contentType,CancellationToken cts)
    {
        try
        {
            Console.Error.WriteLine("{0}", RestServiceBaseAddress + uri);

            string url = string.Format("{0}{1}", RestServiceBaseAddress, uri); 

            var request = (HttpWebRequest)WebRequest.Create(url);
            request.ContentType = contentType;
            if (Utility.CurrentUser != null && !string.IsNullOrWhiteSpace(Utility.CurrentUser.AuthToken))
            {
                request.Headers.Add("api_key", Utility.CurrentUser.AuthToken);
            }
            request.Method = "POST";

            var payload = body.ToString();

            request.ContentLength = payload.Length;

            byte[] byteArray = Encoding.UTF8.GetBytes(body.ToString());
            request.ContentLength = byteArray.Length;

            using (var stream = await request.GetRequestStreamAsync())
            {
                stream.Write(byteArray, 0, byteArray.Length);
                stream.Close();
            }
            using (var webResponse = await request.GetResponseAsync())
            {
                var response = (HttpWebResponse)webResponse;
                using (var reader1 = new StreamReader(response.GetResponseStream()))
                {
                    Console.WriteLine("Finished : {0}", uri);
                    var responseStr = reader1.ReadToEnd();  
                    var responseObj = JsonConvert.DeserializeObject<JResponse>(
                        responseStr,
                        new JsonSerializerSettings()
                        {
                            MissingMemberHandling = MissingMemberHandling.Ignore,
                            NullValueHandling = NullValueHandling.Ignore
                        });
                    return responseObj;
                }
            }
        }
        catch (System.Exception ex)
        {
            Utility.ExceptionHandler("APIClient", "ProcessRequestAsync", ex);
        }

        return default(JResponse);
    }
10
Sagar Panwala 30 ene. 2016 a las 10:28

3 respuestas

La mejor respuesta

En su ejemplo, está creando un CancellationTokenSource; necesita mantener una referencia a él, de modo que la próxima vez que se invoque el controlador, se pueda cancelar la búsqueda anterior. Aquí hay una aplicación de consola de ejemplo que debería poder ejecutar, pero la pieza importante está en el manipulador.

private CancellationTokenSource _cts;

private async void TextChangedHandler(string text)   // async void only for event handlers
{
    try
    {
        _cts?.Cancel();     // cancel previous search
    }
    catch (ObjectDisposedException)     // in case previous search completed
    {
    }

    using (_cts = new CancellationTokenSource())
    {
        try
        {
            await Task.Delay(TimeSpan.FromSeconds(1), _cts.Token);  // buffer

            var users = await _userService.SearchUsersAsync(text, _cts.Token);
            Console.WriteLine($"Got users with IDs: {string.Join(", ", users)}");
        }
        catch (TaskCanceledException)       // if the operation is cancelled, do nothing
        {
        }
    }
}

Asegúrese de pasar el CancellationToken a todos de los métodos async, incluidos los que realizan la solicitud web, de esta manera, indica la cancelación hasta el nivel más bajo.

10
Matt Cole 2 feb. 2016 a las 22:58

Cree una instancia de CancellationTokenSource.

cts = new CancellationTokenSource (); Método de ejemplo

private void cancelButton_Click(object sender, RoutedEventArgs e)  
{  
    if (cts != null)  
    {  
        cts.Cancel();  
    }  
}
0
smf.karim 13 feb. 2018 a las 18:02

Intenta usar el temporizador. La primera vez que cambia el texto, lo crea usted mismo. Luego cambia el texto después de eso, reinicia el temporizador. Si no cambia el texto durante 700 milisegundos, el temporizador activará el método PerformeSearch. Utilice Timeout.Infinite para el parámetro de período del temporizador para evitar que se reinicie.

textview.TextChange += (sender,e) => 
{
   if (_fieldChangeTimer == null)
     _fieldChangeTimer = new Timer(delegate
     {
                PerformeSearch();
     }, null, 700, Timeout.Infinite);
   else
   {
     _fieldChangeTimer.Change(700, Timeout.Infinite);
   }
};
0
user5707100user5707100 1 feb. 2016 a las 13:37