2012-07-11 21 views
6

Im intentando convertir la respuesta del cliente web a Json, pero está intentando crear el objeto JSON antes de que se termine de descargarlo del servidor. ¿Hay una manera "agradable" de esperar que WebOpenReadCompleted se ejecute?Esperando a que termine el evento

que mencionar que esta es una aplicación de WP7, así que todo es asíncrono

public class Client 
{ 

    public String _url; 
    private String _response; 
    private WebClient _web; 

    private JObject jsonsobject; 
    private Boolean blockingCall; 


    private Client(String url) 
    { 
     _web = new WebClient(); 
     _url = url; 
    } 

    public JObject Login(String username, String password) 
    { 
     String uriUsername = HttpUtility.UrlEncode(username); 
     String uriPassword = HttpUtility.UrlEncode(password); 

     Connect(_url + "/data.php?req=Login&username=" + uriUsername + "&password=" + uriPassword + ""); 
     jsonsobject = new JObject(_response); 
     return jsonsobject; 
    } 

    public JObject GetUserInfo() 
    { 

     Connect(_url + "/data.php?req=GetUserInfo"); 
     jsonsobject = new JObject(_response); 
     return jsonsobject; 
    } 

    public JObject Logout() 
    { 

     Connect(_url + "/data.php?req=Logout"); 
     jsonsobject = new JObject(_response); 
     return jsonsobject; 
    } 

    private void Connect(String url) 
    { 

     _web.Headers["Accept"] = "application/json"; 
     _web.OpenReadCompleted += new OpenReadCompletedEventHandler(WebOpenReadCompleted); 
     _web.OpenReadAsync(new Uri(url)); 
    } 

    private void WebOpenReadCompleted(object sender, OpenReadCompletedEventArgs e) 
    { 
     if (e.Error != null || e.Cancelled) 
     { 
      MessageBox.Show("Error:" + e.Error.Message); 
      _response = ""; 
     } 
     else 
     { 
      using (var reader = new StreamReader(e.Result)) 
      { 
       _response = reader.ReadToEnd(); 
      }  
     } 
    } 
} 

Respuesta

2

Puede utilizar un EventWaitHandle para bloquear muy bien hasta que la lectura asíncrono se ha completado. Tenía un requisito similar para descargar archivos con WebClient. Mi solución fue la subclase WebClient. La fuente completa está abajo. Específicamente, DownloadFileWithEvents bloquea muy bien hasta que se complete la descarga asincrónica.

Debe ser bastante sencillo modificar la clase para su propósito.

public class MyWebClient : WebClient, IDisposable 
{ 
    public int Timeout { get; set; } 
    public int TimeUntilFirstByte { get; set; } 
    public int TimeBetweenProgressChanges { get; set; } 

    public long PreviousBytesReceived { get; private set; } 
    public long BytesNotNotified { get; private set; } 

    public string Error { get; private set; } 
    public bool HasError { get { return Error != null; } } 

    private bool firstByteReceived = false; 
    private bool success = true; 
    private bool cancelDueToError = false; 

    private EventWaitHandle asyncWait = new ManualResetEvent(false); 
    private Timer abortTimer = null; 

    const long ONE_MB = 1024 * 1024; 

    public delegate void PerMbHandler(long totalMb); 

    public event PerMbHandler NotifyMegabyteIncrement; 

    public MyWebClient(int timeout = 60000, int timeUntilFirstByte = 30000, int timeBetweenProgressChanges = 15000) 
    { 
     this.Timeout = timeout; 
     this.TimeUntilFirstByte = timeUntilFirstByte; 
     this.TimeBetweenProgressChanges = timeBetweenProgressChanges; 

     this.DownloadFileCompleted += new System.ComponentModel.AsyncCompletedEventHandler(MyWebClient_DownloadFileCompleted); 
     this.DownloadProgressChanged += new DownloadProgressChangedEventHandler(MyWebClient_DownloadProgressChanged); 

     abortTimer = new Timer(AbortDownload, null, TimeUntilFirstByte, System.Threading.Timeout.Infinite); 
    } 

    protected void OnNotifyMegabyteIncrement(long totalMb) 
    { 
     if (NotifyMegabyteIncrement != null) NotifyMegabyteIncrement(totalMb); 
    } 

    void AbortDownload(object state) 
    { 
     cancelDueToError = true; 
     this.CancelAsync(); 
     success = false; 
     Error = firstByteReceived ? "Download aborted due to >" + TimeBetweenProgressChanges + "ms between progress change updates." : "No data was received in " + TimeUntilFirstByte + "ms"; 
     asyncWait.Set(); 
    } 

    void MyWebClient_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e) 
    { 
     if (cancelDueToError) return; 

     long additionalBytesReceived = e.BytesReceived - PreviousBytesReceived; 
     PreviousBytesReceived = e.BytesReceived; 
     BytesNotNotified += additionalBytesReceived; 

     if (BytesNotNotified > ONE_MB) 
     { 
      OnNotifyMegabyteIncrement(e.BytesReceived); 
      BytesNotNotified = 0; 
     } 
     firstByteReceived = true; 
     abortTimer.Change(TimeBetweenProgressChanges, System.Threading.Timeout.Infinite); 
    } 

    public bool DownloadFileWithEvents(string url, string outputPath) 
    { 
     asyncWait.Reset(); 
     Uri uri = new Uri(url); 
     this.DownloadFileAsync(uri, outputPath); 
     asyncWait.WaitOne(); 

     return success; 
    } 

    void MyWebClient_DownloadFileCompleted(object sender, System.ComponentModel.AsyncCompletedEventArgs e) 
    { 
     if (cancelDueToError) return; 
     asyncWait.Set(); 
    } 

    protected override WebRequest GetWebRequest(Uri address) 
    {    
     var result = base.GetWebRequest(address); 
     result.Timeout = this.Timeout; 
     return result; 
    } 

    void IDisposable.Dispose() 
    { 
     if (asyncWait != null) asyncWait.Dispose(); 
     if (abortTimer != null) abortTimer.Dispose(); 

     base.Dispose(); 
    } 
} 
+0

EventWaitHandle parece ser lo correcto para usar aquí. – nickknissen

2

Veo que está utilizando OpenReadAsync(). Este es un método asíncrono, lo que significa que el hilo que llama no se suspende mientras el controlador se está ejecutando.

Esto significa que su configuración de operación de asignación jsonsobject ocurre mientras WebOpenReadCompleted() aún se está ejecutando.

Diría que su mejor opción es reemplazar OpenReadAsync (nuevo Uri (url)) con OpenRead (nuevo Uri (url)) en su método Connect (string url).

OpenRead() es una operación síncrona, por lo que el método de llamada esperará hasta que se complete el método WebOpenReadCompleted() antes de que su asignación se produzca en el método Connect().

+0

Olvidé mencionar que es una aplicación WP7 por lo que no hay una operación síncrona para el cliente web, perdón por eso – nickknissen

+0

Lo suficientemente justo ... Entonces sí, la otra publicación que muestra el uso de EventWaitHandle es el camino a seguir – d3v1lman1337

Cuestiones relacionadas