在 catch 块中出现 "Cannot access a disposed object"。

3

我正在编写一个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内容作为参数传入。问题在于当因“无法访问已释放对象。对象名称:'System.Net.Http.StringContent'”而抛出异常时。
我找到了一些帖子建议使用 "using" 块,因此我将整个方法(实际上是我的 try...catch块)包装在了 using (content) 中,然而那并没有解决问题。但是,在方法开始之前(在try之前),添加引用到 content 可以起作用: var contentForException = content.ReadAsStringAsync().Result;,然后在 catch 块中使用 contentForException 而不是 content
我想理解的是为什么会这样(即为什么在 'catch' 块之前将content 释放即使它在那里也需要)。特别是为什么将方法包装在 "using" 块中时会忽略该问题。
如果有影响的话,这是我从业务逻辑中调用此方法的方式:
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);

2
using 块并不会防止对象被处理 - 它确保对象被处理。 - C.Evenhuis
如果您告诉我们哪个错误发生在哪一行,可能会有所帮助。某些内容正在被处理。看起来PostAsync过于热心了。 - H H
@HenkHolterman 当尝试使用“content”时,在“catch”块中发生错误,这是该块中唯一使用“content”的地方。更多关于PostAsync的解释将不胜感激。 - JakeDiscBrake
为什么它会进入catch块?我是指原始错误。 - H H
@HenkHolterman 在这一行中出现了catch异常:if (response.Result.StatusCode == HttpStatusCode.OK)。这是一个聚合异常,但最内层的异常是System.Net.Sockets.SocketException - 无法建立连接,因为目标计算机积极拒绝了它<IP_address>(因为我正在尝试检查的服务未运行)。 - JakeDiscBrake
2个回答

2

使用using块只会在使用块结束时强制进行一次dispose,它不能防止在中途进行dispose。一旦你在方法顶部访问了响应的正文,即使content被dispose也无所谓,因为你已经检索到了响应的正文并将其存储在contentForException中。


好的,这很有帮助。你知道为什么在没有使用“contentForException”的情况下,在“catch”块之前我的“content”被释放了吗? - JakeDiscBrake
1
我不确定为什么它被释放了,但是要找出原因,你可以尝试调试它或阅读框架代码。要调试基类库,请查看这里:https://blogs.msdn.microsoft.com/sburke/2008/01/16/configuring-visual-studio-to-debug-net-framework-source-code/ 要阅读代码,请搜索“class HttpContent github”,您将看到一个corefx结果。那是核心基础类库程序集的源代码。 - CamW

1
根据我对HttpContent类的了解,它似乎会在使用post方法时处理StringContent对象。我想这也是这里发生的事情。

1
这就是我在寻找的答案。非常感谢。在 foreach 循环中,我必须每次创建 StringContent。 - Daniel

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