【问题标题】:ObjectDisposedException: The CancellationTokenSource has been disposedObjectDisposedException:CancellationTokenSource 已被释放
【发布时间】:2016-10-26 21:28:35
【问题描述】:

我开始在我的 MacBook Pro 上使用 Xamarin Studio 使用 Xamarin.Forms 进行开发。我构建了一个应用程序,其目的是查询 PrestaShop 网站、检索产品并显示它们。

在将应用程序部署到Android时,我遇到了Marshmallow以下版本的一些问题,但我都解决了,所以在此不再赘述。

将应用程序部署到 iOS(模拟器)时,我仍然遇到一个严重问题。应用程序运行,但是当我单击按钮检索数据时,它崩溃给我一个 System.ObjectDisposedException,其消息是“CancellationTokenSource 已被处置”。我把相关的源代码贴在这里:

async void button1_Click(object sender, System.EventArgs e)
{
    try
    {
        HttpClientHandler hnd = new HttpClientHandler();
        hnd.Credentials = new NetworkCredential("[apikey]", "");
        string res;
        using (var client = new HttpClient(hnd))
        {
            var responseText = await client.GetStringAsync("[endpoint]/products");
            using (MemoryStream stream = new MemoryStream())
            using (StreamWriter writer = new StreamWriter(stream))
            {
                writer.Write(responseText.Replace("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n", string.Empty));
                writer.Flush();
                stream.Position = 0;
                XDocument doc = XDocument.Load(stream);
                res = JsonConvert.SerializeXNode(doc, Formatting.Indented, true);
            }
            var data = JsonConvert.DeserializeObject<Dictionary<string, object>>(res);
            //Result.Text = data["products"].GetType().ToString() + Result.Text;
            Func<string, Task> creator_method = async (string id) =>
            {
                try
                {
                    var responseProd = await client.GetStringAsync($"[endpoint]/products/{id}"); // AT THIS ROW THE EXCEPTION IS RAISED!!!
                    using (MemoryStream stream = new MemoryStream())
                    using (StreamWriter writer = new StreamWriter(stream))
                    {
                        writer.Write(responseProd.Replace("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n", string.Empty));
                        writer.Flush();
                        stream.Position = 0;
                        XDocument doc = XDocument.Load(stream);
                        res = JsonConvert.SerializeXNode(doc, Formatting.Indented, true);
                    }
                    var product_rawData = JsonConvert.DeserializeObject<Dictionary<string, object>>(res);
                    var productData = (JObject)product_rawData["product"];
                    try
                    {
                        views.Children.Add(new ProductXaml(productData["name"]["language"]["#cdata-section"].ToString(), float.Parse(productData["price"]["#cdata-section"].ToString().Replace('.', ',')), productData["id_default_image"]["@xlink:href"].ToString()));
                    }
                    catch (NullReferenceException nre)
                    {
                        views.Children.Add(new ProductXaml(productData["name"]["language"]["#cdata-section"].ToString(), float.Parse(productData["price"]["#cdata-section"].ToString().Replace('.', ',')), ""));
                    }
                }
                catch (Exception gex) { throw; }
            };
            foreach (var product in ((JObject)data["products"])["product"])
                try
                {
                    Device.BeginInvokeOnMainThread(async () => { await creator_method.Invoke(product["@id"].ToString()); });
                }
                catch (TaskCanceledException tce) { continue; }
                catch (WebException we) { continue;}
        }
    }
    catch (HttpRequestException ex)
    {
        Result.Text = ex.Message;
    }
}

我该如何解决这个问题?

【问题讨论】:

  • 您的using client = new HttpClient...CancellationToken 似乎已经在第一次client.GetStringAsync() 调用中被使用。您是否尝试过使用单独的 using 块并使用新的 HttpClient 进行第二次调用(在您的第二个 try 块内)?
  • 通过一些工作,这种方法解决了我的问题。谢谢! :)
  • 很高兴听到,我已经发布了一些如何做到这一点的示例,作为未来来者的答案:) - 我想你的代码会非常相似

标签: c# ios xamarin.ios xamarin.forms


【解决方案1】:

“CancellationTokenSource 已被释放”

您的using (var client = new HttpClient())...CancellationToken 似乎已经被第一个client.GetStringAsync() 调用消耗掉了。

如何解决这个问题
我建议将您的第二个电话放在自己的 using 块中,以避免这种情况。

这是您的代码现在的样子,带有第二个 using 语句:

async void button1_Click(object sender, System.EventArgs e)
{
    try
    {
        HttpClientHandler hnd = new HttpClientHandler();
        hnd.Credentials = new NetworkCredential("[apikey]", "");
        string res;
        using (var client = new HttpClient(hnd))
        {
            var responseText = await client.GetStringAsync("[endpoint]/products");
            using (MemoryStream stream = new MemoryStream())
            using (StreamWriter writer = new StreamWriter(stream))
            {
                writer.Write(responseText.Replace("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n", string.Empty));
                writer.Flush();
                stream.Position = 0;
                XDocument doc = XDocument.Load(stream);
                res = JsonConvert.SerializeXNode(doc, Formatting.Indented, true);
            }
            var data = JsonConvert.DeserializeObject<Dictionary<string, object>>(res);
            //Result.Text = data["products"].GetType().ToString() + Result.Text;
            Func<string, Task> creator_method = async (string id) =>
            {
                try
                {
                    using (var newClient = new HttpClient(hnd)) // Create a new client to avoid your other one's token being consumed
                    {
                        var responseProd = await newClient.GetStringAsync($"[endpoint]/products/{id}");
                        using (MemoryStream stream = new MemoryStream())
                        using (StreamWriter writer = new StreamWriter(stream))
                        {
                            writer.Write(responseProd.Replace("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n", string.Empty));
                            writer.Flush();
                            stream.Position = 0;
                            XDocument doc = XDocument.Load(stream);
                            res = JsonConvert.SerializeXNode(doc, Formatting.Indented, true);
                        }
                        var product_rawData = JsonConvert.DeserializeObject<Dictionary<string, object>>(res);
                        var productData = (JObject)product_rawData["product"];
                        try
                        {
                            views.Children.Add(new ProductXaml(productData["name"]["language"]["#cdata-section"].ToString(), float.Parse(productData["price"]["#cdata-section"].ToString().Replace('.', ',')), productData["id_default_image"]["@xlink:href"].ToString()));
                        }
                        catch (NullReferenceException nre)
                        {
                            views.Children.Add(new ProductXaml(productData["name"]["language"]["#cdata-section"].ToString(), float.Parse(productData["price"]["#cdata-section"].ToString().Replace('.', ',')), ""));
                        }
                    }
                }
                catch (Exception gex) { throw; }
            };
            foreach (var product in ((JObject)data["products"])["product"])
                try
                {
                    Device.BeginInvokeOnMainThread(async () => { await creator_method.Invoke(product["@id"].ToString()); });
                }
                catch (TaskCanceledException tce) { continue; }
                catch (WebException we) { continue;}
        }
    }
    catch (HttpRequestException ex)
    {
        Result.Text = ex.Message;
    }
}

希望这会有所帮助! :)

【讨论】:

    【解决方案2】:

    我遇到了同样的问题,我对所有 api 请求都使用了一个 httpclient。

    private static HttpClient httpClient;
    
    public static HttpClient GetHttpClient()
    {
        if (httpClient == null)
        {
            httpClient = new HttpClient()
            {
                BaseAddress = new Uri(BaseUrl)
            };
            httpClient.DefaultRequestHeaders.Accept.Clear();
            httpClient.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
        }
    
        return httpClient;
    }
    

    看来您使用 (var client = new HttpClient())...的 CancellationToken 已经被第一个消费了 client.GetStringAsync() 调用。

    Geoff 的回答我删除了我的静态 httpclient,现在为所有请求创建了一个新的 httpclient,它解决了我的问题。

    public static HttpClient GetHttpClient()
    {
        HttpClient httpClient = new HttpClient()
        {
            BaseAddress = new Uri(BaseUrl)
        };
        httpClient.DefaultRequestHeaders.Accept.Clear();
        httpClient.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
    
        return httpClient;
    }
    

    【讨论】:

      【解决方案3】:

      像这样创建一个新的 HttpClient 实例

      public static HttpClient GetHttpClient()
      {
          HttpClient httpClient = new HttpClient()
          {
              BaseAddress = new Uri(BaseUrl)
          };
          httpClient.DefaultRequestHeaders.Accept.Clear();
          httpClient.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
      
      return httpClient;
      

      }

      【讨论】:

        猜你喜欢
        • 2016-05-27
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-09-12
        相关资源
        最近更新 更多