ObjectDisposedException: CancellationTokenSource已被处理

10

我开始在我的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;
    }
}

我该如何解决这个问题?


1
看起来你的 using client = new HttpClient...CancellationToken 已经在第一次 client.GetStringAsync() 调用中被使用了。你尝试过在第二个 try 块内使用一个新的 HttpClient 和一个单独的 using 块进行第二次调用吗? - Geoff James
通过一些工作,这个方法解决了我的问题。谢谢! :) - jinzo78
很高兴听到这个消息,我已经发布了一个示例作为未来读者的答案 :) - 我猜你的代码会非常相似。 - Geoff James
3个回答

10
" 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;
    }
}

希望这能帮到你!:)


0

像这样创建一个新的 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;

}


-1

我曾经遇到过同样的问题,我在所有的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;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接