2012-07-11 31 views
6

Cố gắng chuyển đổi phản hồi từ webclient thành Json, nhưng nó đang cố gắng tạo đối tượng JSON trước khi nó được thực hiện downloaing nó từ máy chủ. Có cách nào "đẹp" để tôi đợi WebOpenReadCompleted được thực thi không?Đang chờ sự kiện kết thúc

Phải nói đây là một ứng dụng WP7, vì vậy tất cả mọi thứ là Async

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

Trả lời

2

Bạn có thể sử dụng một EventWaitHandle để chặn độc đáo cho đến khi đọc async hoàn tất. Tôi đã có một yêu cầu tương tự để tải xuống tệp với WebClient. Giải pháp của tôi là phân lớp WebClient. Toàn bộ nguồn dưới đây. Cụ thể, các khối DownloadFileWithEvents độc đáo cho đến khi quá trình tải xuống async hoàn tất.

Sẽ khá đơn giản để sửa đổi lớp học cho mục đích của bạn.

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 có vẻ như là điều phù hợp để sử dụng tại đây. – nickknissen

2

Tôi thấy bạn đang sử dụng OpenReadAsync(). Đây là một phương thức không đồng bộ, có nghĩa là chuỗi gọi không bị treo trong khi trình xử lý đang thực hiện.

Điều này có nghĩa là cài đặt hoạt động gán của bạn jsonsobject xảy ra trong khi WebOpenReadCompleted() vẫn đang thực thi.

Tôi muốn đặt cược tốt nhất của bạn là thay thế OpenReadAsync (URI mới (url)) bằng OpenRead (url Uri mới) trong phương thức Connect (chuỗi url) của bạn.

OpenRead() là một thao tác đồng bộ, do đó phương thức gọi sẽ đợi cho đến khi phương thức WebOpenReadCompleted() hoàn thành trước khi phép gán của bạn xuất hiện trong phương thức Connect().

+0

Quên đề cập đến đó là một ứng dụng WP7 vì vậy không có hoạt động đồng bộ cho webclient, xin lỗi về điều đó – nickknissen

+0

Đủ công bằng ... Sau đó, vâng, bài đăng khác cho thấy việc sử dụng EventWaitHandle là cách để đi – d3v1lman1337

Các vấn đề liên quan