【问题标题】:"Cannot access a disposed object" in catch blockcatch 块中的“无法访问已处理的对象”
【发布时间】:2016-04-18 09:09:27
【问题描述】:

我正在编写一个 Windows 服务。我有一个计时器方法(private void OnTimer(object sender, ElapsedEventArgs args)),它调用业务逻辑并最终得到这个方法:

public ServiceHelperResponse CallService(HttpMethod httpMethod, string uri, HttpContent content, string expectedResultcontent)
{
    try
    {
        using (var client = new HttpClient())
        {
            Task<HttpResponseMessage> response;
            switch (httpMethod)
            {
                case HttpMethod.Get:
                    response = client.GetAsync(uri);
                    break;
                case HttpMethod.Post:
                    response = client.PostAsync(uri, content);
                    break;
                default:
                    return ServiceHelperResponse.UnhandledError;
            }

            if (response.Result.StatusCode == HttpStatusCode.OK)
            {
                var responseContent = response.Result.Content.ReadAsStringAsync().Result;

                if (!responseContent.Equals(expectedResultcontent))
                {
                    return ServiceHelperResponse.ExpectedResultNotMatching;
                }
                return ServiceHelperResponse.Ok;
            }

            if (response.Result.StatusCode == HttpStatusCode.NoContent)
            {
                return ServiceHelperResponse.Ok;
            }

            return ServiceHelperResponse.WrongResponsecode;
        }
    }
    catch (Exception ex)
    {
        ServiceMonitor.EventLogger.WriteEntry(
            string.Format("Exception while tyring to send http request.\r\n\r\nUri: {0}\r\nContent:\r\n{1}\r\n\r\n{2}",
                uri, content.ReadAsStringAsync(), ex.Message
            ),
            EventLogEntryType.Error
        );
        return ServiceHelperResponse.UnhandledError;
    }
}

如您所见,http 内容是作为参数传入的。问题是当因为 content 被释放而发生异常时(“无法访问已释放的对象。 对象名称:'System.Net.Http.StringContent'。")

我发现了一些建议使用“使用”块的帖子,因此我将整个方法(实际上是我的 try...catch 块)包装在 using(content) 中,但这并不能解决问题。然而,有效的是在方法的最开始(尝试之前)添加对内容的引用 - var contentForException = content.ReadAsStringAsync().Result;,然后在 catch 块中使用 contentForException 而不是 content

我想了解的是为什么它会以这种方式运行(即 为什么 在 'catch' 块之前是 content disposed ,即使那里需要它)。特别是为什么它在将方法的内容包装在其中时会忽略“使用”块。


如果它很重要,这就是我从业务逻辑中调用此方法的方式:

var uri = ServiceMonitor.Config.Services.BankDetailsValidationService.Uri;

var content = new StringContent(
    JsonConvert.SerializeObject(
    new
    {
        sortCode = "...",
        accountNumber = "..."
    }
), Encoding.UTF8, "application/json");

var expectedResultcontent = "...";

return _restfulServiceHelper.CallService(HttpMethod.Post, uri, content, expectedResultcontent);

【问题讨论】:

  • using 块不会阻止对象被释放 - 它确保对象被释放。
  • 如果您告诉我们哪个错误发生在哪一行,这可能会有所帮助。有些东西正在处理您的内容。看起来 PostAsync 过于热心了。
  • @HenkHolterman 尝试 WriteEntry 时在“catch”块中发生错误 - 我正在使用 content 的块中唯一的一个位置。多一点解释(关于 PostAsync)将不胜感激。
  • 为什么会遇到问题?我的意思是原来的错误。
  • @HenkHolterman 在这条线上很受欢迎:if (response.Result.StatusCode == HttpStatusCode.OK)。这是聚合异常,但最里面的异常是 System.Net.Sockets.SocketException - 无法建立连接,因为目标机器主动拒绝它 (因为我要检查的服务没有运行)跨度>

标签: c# timer windows-services


【解决方案1】:

using 块只会在 using 块的末尾强制 dispose,它不会阻止 dispose 中途。一旦您访问了方法顶部的响应主体,content 是否被释放就无关紧要了,因为您已经检索了响应的主体并将其存储在 contentForException

【讨论】:

  • 好的,这有帮助。当我不使用“contentForException”时,任何线索为什么我的content 被置于“catch”块之前?
  • 我不确定为什么要处理它,但要找出答案,您可以尝试调试它或阅读框架​​代码。要调试 BCL,请看这里:blogs.msdn.microsoft.com/sburke/2008/01/16/… 要通读代码搜索“class HttpContent github”,您将看到 corefx 结果。这就是核心 BCL 程序集的来源。
【解决方案2】:

从我所看到的 HttpContent 类来看,它似乎在 post 方法中使用时处理了 StringContent 对象。我想这也是这里正在发生的事情。

【讨论】:

  • 这就是我一直在寻找的答案。非常感谢。在 foreach 循环中,我必须每次都创建 StringContent。
猜你喜欢
  • 2011-10-23
  • 2011-03-25
  • 1970-01-01
  • 2020-07-21
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多