Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/327.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 异步下载和反序列化_C#_Asynchronous_Xamarin.android - Fatal编程技术网

C# 异步下载和反序列化

C# 异步下载和反序列化,c#,asynchronous,xamarin.android,C#,Asynchronous,Xamarin.android,晚上好, 我正在尝试使用异步编程来优化一些代码,我想知道下面的代码是否编写得很好,以及是否有任何改进的方法 其目的是从给定的URL下载一些JSON,并将其反序列化为对象 我有三个(现在是四个)问题(和一个在文章末尾描述的问题): 我应该使用TaskEx.Run来运行吗 Newtonsoft.Json.JsonConvert.DeserializeObject 有没有什么好办法(不检查对象属性)来 知道是否成功创建了rootObject 我是否应该在某处检查是否有人要求取消 (新问题)我是否应该

晚上好,

我正在尝试使用异步编程来优化一些代码,我想知道下面的代码是否编写得很好,以及是否有任何改进的方法

其目的是从给定的URL下载一些JSON,并将其反序列化为对象

我有三个(现在是四个)问题(和一个在文章末尾描述的问题):

  • 我应该使用
    TaskEx.Run
    来运行吗
    Newtonsoft.Json.JsonConvert.DeserializeObject
  • 有没有什么好办法(不检查对象属性)来 知道是否成功创建了
    rootObject
  • 我是否应该在某处检查是否有人要求取消
  • 新问题)我是否应该在执行新请求之前取消PendingRequests
不用多说,下面是代码:

internal static class WebUtilities
{
    /// <summary>
    /// Downloads the page of the given url
    /// </summary>
    /// <param name="url">url to download the page from</param>
    /// <param name="cancellationToken">token to cancel the download</param>
    /// <returns>the page content</returns>
    internal static async Task<string> DownloadStringAsync(string url, CancellationToken cancellationToken)
    {
        try
        {
            // create Http Client and dispose of it even if exceptions are thrown (same as using finally statement)
            using (var client = new HttpClient() {Timeout = TimeSpan.FromSeconds(5)})
            {
                // should I always do this?
                client.CancelPendingRequests();

                // do request and dispose of it when done
                using (var response = await client.GetAsync(url, cancellationToken).ConfigureAwait(false))
                {
                    // if response was successful (otherwise it will return null)
                    if (response.IsSuccessStatusCode)
                    {
                        // return its content
                        return await response.Content.ReadAsStringAsync().ConfigureAwait(false);
                    }
                }
            }
        }
        catch (Exception ex) when (ex is System.Net.Sockets.SocketException || 
                                    ex is InvalidOperationException || 
                                    ex is OperationCanceledException ||
                                    ex is System.Net.Http.HttpRequestException)
        {
            WriteLine("DownloadStringAsync task has been cancelled.");
            WriteLine(ex.Message);
            return null;
        }

        // return null if response was unsuccessful
        return null;
    }

    /// <summary>
    /// Downloads Json from a given url and attempts its deserialization to a given reference type (class)
    /// </summary>
    /// <typeparam name="T">the class to deserialize to</typeparam>
    /// <param name="url">url to download the json from</param>
    /// <param name="cancellationToken">token to cancel the download</param>
    /// <returns>the deserialized object</returns>
    internal static async Task<T> DownloadJsonAndDeserialize<T>(string url, CancellationToken cancellationToken) where T : class, new()
    {
        // download json from the given url
        var jsonResponse = await DownloadStringAsync(url, cancellationToken).ConfigureAwait(false);

        // if the response is invalid, no need to go further
        if (string.IsNullOrEmpty(jsonResponse))
            // return a default constructor instance
            return new T();

        // try to deserialize
        try
        {
            // Deserialize json data to the given .NET object
            // Should I use TaskEx.Run here?
            return Newtonsoft.Json.JsonConvert.DeserializeObject<T>(jsonResponse);
        }
        catch (Exception ex) when (ex is JsonException)
        {
            WriteLine("Something went wrong while deserializing json to the given reference type.");
            WriteLine(ex.Message);
        }

        // return a default constructor instance
        return new T();
    }
}
内部静态类WebUtilities
{
/// 
///下载给定url的页面
/// 
///从中下载页面的url
///取消下载的令牌
///页面内容
内部静态异步任务下载StringAsync(字符串url,CancellationToken CancellationToken)
{
尝试
{
//创建Http客户机并在抛出异常的情况下处置它(与使用finally语句相同)
使用(var client=new HttpClient(){Timeout=TimeSpan.fromsons(5)})
{
//我应该一直这样做吗?
client.CancelPendingRequests();
//请求并在完成后处理
使用(var response=await client.GetAsync(url,cancellationToken).ConfigureAwait(false))
{
//如果响应成功(否则将返回null)
if(响应。IsSuccessStatusCode)
{
//返回其内容
返回wait response.Content.ReadAsStringAsync().configurewait(false);
}
}
}
}
当(ex是System.Net.Sockets.SocketException||时捕获(异常ex)
ex为无效操作例外| |
ex是操作取消异常||
ex是System.Net.Http.HttpRequestException)
{
WriteLine(“DownloadStringAsync任务已取消”);
WriteLine(例如消息);
返回null;
}
//如果响应不成功,则返回null
返回null;
}
/// 
///从给定url下载Json并尝试将其反序列化为给定引用类型(类)
/// 
///要反序列化到的类
///从中下载json的url
///取消下载的令牌
///反序列化的对象
内部静态异步任务下载JSONAND反序列化(字符串url,CancellationToken CancellationToken),其中T:class,new()
{
//从给定的url下载json
var jsonResponse=await DownloadStringAsync(url,cancellationToken);
//如果响应无效,则无需进一步说明
if(string.IsNullOrEmpty(jsonResponse))
//返回默认构造函数实例
返回新的T();
//尝试反序列化
尝试
{
//将json数据反序列化到给定的.NET对象
//我应该使用TaskEx。在这里运行吗?
返回Newtonsoft.Json.JsonConvert.DeserializeObject(jsonResponse);
}
捕获(异常ex)时(ex是JsonException)
{
WriteLine(“将json反序列化为给定引用类型时出错。”);
WriteLine(例如消息);
}
//返回默认构造函数实例
返回新的T();
}
}
要调用代码,可以执行以下操作:

internal static async Task CallAsync()
    {
        RootObject root = null;

        using (var cts = new CancellationTokenSource(TimeSpan.FromSeconds(10)))
        {
            var token = cts.Token;

            root = await WebUtilities.DownloadJsonAndDeserialize<RootObject>(URL, token).ConfigureAwait(false);
        }

        // do something with rootObject
        // any good way to know if rootObject was successfully created, before moving on?
    }
内部静态异步任务CallAsync()
{
RootObject root=null;
使用(var cts=new CancellationTokenSource(TimeSpan.FromSeconds(10)))
{
var-token=cts.token;
root=await WebUtilities.DownloadJsonAndDeserialize(URL,令牌)。ConfigureAwait(false);
}
//对rootObject执行一些操作
//在继续之前,有什么好方法可以知道是否成功创建了rootObject?
}
你会改变什么?为什么

谢谢

编辑:

  • @codran建议-每个
    wait
    现在都在使用
    configurewait(false)
  • CancellationTokenSource
    现在已被处置(使用语句)
  • @codran建议-一些异常现在已被过滤
  • DownloadStringAsync
    现在在一个
    try catch
    块以提高可读性(而不是
    try catch finally
  • 现在检查响应是否
    IsSuccessStatusCode
发现问题(为其创建了另一个问题-):

有趣的是,当无法联系到主机时(例如,脱机本地服务器),
GetAsync
之后不会发生任何事情几分钟后(大约3分钟)抛出一个
System.Net.WebException
,说错误:连接失败(连接超时)。内部异常是
System.Net.Sockets.SocketsException
(此处的完整日志:)

我已尝试将
客户端.Timeout
设置为5秒,但似乎不起作用

不过,可能是Xamarin的bug()

无论哪种方式,都不应该在10秒后自动取消cancellationToken

此超时问题在以下时间发生:

  • 脱机/无法访问的IP地址(在我的示例中为 请求脱机本地服务器(如192.168.1.101:8080)(如Ge
    // Creating and disposing HttpClient is unnecessary
    // if you are going to use it multiple time
    // reusing HttpClient improves performance !!
    // do not worry about memory leak, HttpClient
    // is designed to close resources used in single "SendAsync"
    // method call
    private static HttpClient client;
    
    
    public Task<T> DownloadAs<T>(string url){
    
        // I know I am firing this on another thread
        // to keep UI free from any smallest task like
        // preparing httpclient, setting headers
        // checking for result or anything..., why do that on
        // UI Thread?
    
        // this helps us using excessive logging required for
        // debugging and diagnostics
    
        return Task.Run(async ()=> {
    
           // following parses url and makes sure
           // it is a valid url
           // there is no need for this to be done on 
           // UI thread
           Uri uri = new Uri(url);
    
           HttpRequestMessage request = 
              new HttpRequestMessage(uri,HttpMethod.Get);
    
           // do some checks, set some headers...
           // secrete code !!!
    
           var response = await client.SendAsync(request,
               HttpCompletionOption.ReadHeaders);
    
           string content = await response.Content.ReadAsStringAsync();
    
           if(((int)response.StatusCode)>300){
    
               // it is good to receive error text details
               // not just reason phrase
    
               throw new InvalidOperationException(response.ReasonPhrase  
                  + "\r\n" + content);
           }
           return JsonConvert.DeserializeObject<T>(content);
    
        });
    }