2

因为对 API 的 Post 请求需要在 Windows Phone 上异步运行,所以我正在努力创建一个简洁易用的库来与 API 交互。

问题是使用该库的人总是需要提供回调函数。

让我们看一些伪代码:

PostRequest 类来帮助我处理 POST 请求:

class PostRequest
{
    private Action<MemoryStream> Callback;

    public PostRequest(string urlPath, string data, Action<MemoryStream> callback)
    {
        Callback = callback;

        // Form the URI
        UriBuilder fullUri = new UriBuilder(urlPath);

        if (!string.IsNullOrEmpty(data))
            fullUri.Query = data;

        // Initialize a new WebRequest
        HttpWebRequest request = (HttpWebRequest)WebRequest.Create(fullUri.Uri);
        request.Method = "POST";

        // Set up the state object for the async request
        DataUpdateState dataState = new DataUpdateState();
        dataState.AsyncRequest = request;

        // Start the asynchronous request
        request.BeginGetResponse(new AsyncCallback(HandleResponse),
            dataState);
    }

    private void HandleResponse(IAsyncResult asyncResult)
    {
        // Get the state information
        DataUpdateState dataState = (DataUpdateState)asyncResult.AsyncState;
        HttpWebRequest dataRequest = (HttpWebRequest)dataState.AsyncRequest;

        // End the async request
        dataState.AsyncResponse = (HttpWebResponse)dataRequest.EndGetResponse(asyncResult);
        if (dataState.AsyncResponse.StatusCode.ToString() == "OK")
        {
            // Create a stream from the response
            Stream response = dataState.AsyncResponse.GetResponseStream();
            TextReader textReader = new StreamReader(response, true);
            string jsonString = textReader.ReadToEnd();
            MemoryStream stream = new MemoryStream(Encoding.UTF8.GetBytes(jsonString));

            // Send the stream through to the callback function
            Callback(stream);
        }
    }
}

public class DataUpdateState
{
    public HttpWebRequest AsyncRequest { get; set; }
    public HttpWebResponse AsyncResponse { get; set; }
}

API 访问对象类:

class APIAuthenticationCredentials
{
    public String Username { get; set; }
    public String Password { get; set; }
}

class APIAO
{
    private String AuthUrl = "http://api.example.com/";
    public static Auth Auth = new Auth();

    //... 
    public static void Authenticate( String data, APIAuthenticationCredentials credentials, Action<MemoryStream> callback )
    {
        PostRequest request = new PostRequest(AuthURL, data, callback);   
    }
    //... 
}

你会注意到我必须一直传递一个回调函数,这样一旦我的PostRequest类中的HandleResponse方法返回了数据,数据就会被转发到某个控制器上,使屏幕对数据进行处理。目前,使用它并不可怕:

private void DisplayData(MemoryStream stream)
{
    DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(Auth));
    APIAO.Auth = (Auth)serializer.ReadObject(stream);
}

//...    
    APIAuthenticationCredentials credentials = new APIAuthenticationCredentials {
        Username = "whatever",
        Password = "whatever"
    }    
    APIAO.Authenticate( credentials, DisplayData );
//... 

问题是我想创建某种存储库样式模式...假设 API 返回了不同的 json 模型,一个调用返回了一系列产品...问题是我想创建一个可爱的存储库调用,例如:

IProductRepository productRepository = new ProductRepository();
productRepository.GetAll();

但我也必须在其中添加一些 GOSH DARN 回调函数,这意味着 API 返回的任何对象类型的每个存储库方法都将具有此 MemoryStream 回调……如果我想更改该功能,我必须在任何地方更新这些东西哟。:( 有没有人见过更好的方法来做这个废话。

这开始变得过于复杂

——哭泣

4

1 回答 1

7

使用较新的语言结构的更简单的答案是:

public static Task<string> GetData(string url, string data)
{
    UriBuilder fullUri = new UriBuilder(url);

    if (!string.IsNullOrEmpty(data))
        fullUri.Query = data;

    WebClient client = new WebClient();
    client.Credentials = CredentialCache.DefaultCredentials;//TODO update as needed
    return client.DownloadStringTaskAsync(fullUri.Uri);
}

在 4.0 项目中,您可以使用 aTaskCompletionSource将非任务异步模型转换为Task

public static Task<string> GetData2(string url, string data)
{
    UriBuilder fullUri = new UriBuilder(url);

    if (!string.IsNullOrEmpty(data))
        fullUri.Query = data;

    WebClient client = new WebClient();
    client.Credentials = CredentialCache.DefaultCredentials;//TODO update as needed

    var tcs = new TaskCompletionSource<string>();

    client.DownloadStringCompleted += (s, args) =>
    {
        if (args.Error != null)
            tcs.TrySetException(args.Error);
        else if (args.Cancelled)
            tcs.TrySetCanceled();
        else
            tcs.TrySetResult(args.Result);
    };

    client.DownloadStringAsync(fullUri.Uri);

    return tcs.Task;
}

调用者现在有一个Task<string>表示此异步操作结果的 。他们可以同步等待它并使用属性获取结果,他们可以添加一个回调Result,当操作完成时执行作为该任务的延续,但没有创建新方法甚至新范围,即ContinueWithawaitasync

public static async Task Foo()
{
    string result = await GetData("http://google.com", "");
    Console.WriteLine(result);
}

这将启动异步任务,向该任务添加一个回调(或延续),以便在它运行时它将继续执行它停止的代码,此时它将结果写入控制台并标记Task方法返回完成,因此方法的任何延续都将执行(允许组合async方法)。

于 2013-09-20T20:26:12.740 回答