Collection 已修改;枚举操作可能不会随机弹出 - HTTP?

Collection was modified; enumeration operation may not execute randomly popping up - HTTP?

我的日志中随机出现 "Collection was modified; enumeration operation may not execute." 异常,这不是持续性错误,而是一个非常随机且烦人的错误。

好像起源于System.Net.Http?添加 headers 时,但我不确定是什么原因导致此错误弹出。我到处寻找这个错误并且我理解原因的概念但是我没有做任何可能引发这个异常的列表的手动循环和编辑(除了添加 headers)?

我有一个使用 HTTPClient 与外部 API 通信的服务,这在大多数情况下都有效,但有时会因上述异常而失败,我尝试调试它,但我似乎无法找到决议!希望有人能发现问题并指出正确的方法。

异常:

Source: System.Private.CoreLib 
 Message: Collection was modified; enumeration operation may not execute. 
 Stack trace:    at System.Collections.Generic.List`1.Enumerator.MoveNextRare()
   at System.Net.Http.Headers.HttpHeaders.AddHeaderInfo(HeaderDescriptor descriptor, HeaderStoreItemInfo sourceInfo)
   at System.Net.Http.Headers.HttpHeaders.AddHeaders(HttpHeaders sourceHeaders)
   at System.Net.Http.Headers.HttpRequestHeaders.AddHeaders(HttpHeaders sourceHeaders)
   at System.Net.Http.HttpClient.PrepareRequestMessage(HttpRequestMessage request)
   at System.Net.Http.HttpClient.SendAsync(HttpRequestMessage request, HttpCompletionOption completionOption, CancellationToken cancellationToken)
   at System.Net.Http.HttpClient.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
   at System.Net.Http.HttpClient.PostAsync(Uri requestUri, HttpContent content, CancellationToken cancellationToken)
   at System.Net.Http.HttpClient.PostAsync(String requestUri, HttpContent content)
   at Hidden.Core.Service.BlaService.MyMethod(MyMethodModel model) in C:\Source\Hidden.Core\Service\BlaService.cs:line 168
   at Hidden.Web.Api.Controllers.ResourceController.MyMethod(MyMethodModel model) in C:\Source\Hidden.Web.Api\Controllers\ResourceController.cs:line 387
   at Microsoft.AspNetCore.Mvc.Internal.ActionMethodExecutor.TaskOfIActionResultExecutor.Execute(IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments)
   at System.Threading.Tasks.ValueTask`1.get_Result()
   at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.InvokeActionMethodAsync()
   at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.InvokeNextActionFilterAsync()
   at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.Rethrow(ActionExecutedContext context)
   at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
   at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.InvokeInnerFilterAsync()
   at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeNextResourceFilter()
   at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.Rethrow(ResourceExecutedContext context)
   at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
   at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeFilterPipelineAsync()
   at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeAsync()
   at Microsoft.AspNetCore.Builder.RouterMiddleware.Invoke(HttpContext httpContext)
   at Hidden.Web.Api.Infrastructure.ExceptionHandlerMiddleware.Invoke(HttpContext context, IActionResultExecutor`1 executor) in C:\Source\Hidden.Web.Api\Infrastructure\ExceptionHandlerMiddleware.cs:line 31 
 Inner Exception: 

我的服务:

private static readonly HttpClient client = new HttpClient();

public async Task<HttpResponseMessage> MyMethod(MyMethodModel model)
{
    //Escape chars
    model.Location = model.Location.Replace("'", "''");
    model.DirectoryName = model.DirectoryName.Replace("'", "''");

    //Set Endpoint
    var sharePointEndpoint = $"https://{model.Hostname}/sites/{model.Site}/_api/web/folders";

    //Set default headers
    client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", model.SharePointToken); //Set token
    client.DefaultRequestHeaders.Add("Accept", "application/json;odata=verbose");

    //Pre-Body data setup
    ResourceBodyMetaDataModel metaData = new ResourceBodyMetaDataModel() { type = "SP.Folder" };

    //Body data setup
    var bodyModel = new Dictionary<string, object>();
    bodyModel.Add("__metadata", metaData);
    bodyModel.Add("ServerRelativeUrl", $"{model.Location}/{model.DirectoryName}/");

    //Set content headers
    var strContent = new StringContent(JsonConvert.SerializeObject(bodyModel));
    strContent.Headers.ContentType = new MediaTypeHeaderValue("application/json");
    strContent.Headers.ContentType.Parameters.Add(new NameValueHeaderValue("odata", "verbose"));

    // Send request, grab response
    var response = await client.PostAsync(sharePointEndpoint, strContent); //Exception Points HERE

    //Return response message
    return response;
}

概览:

似乎一旦我向 API 发送了多个请求,这些请求就会相互冲突,试图覆盖 headers。我什至尝试先清除 headers 然后添加它们,但我仍然遇到上述问题。该问题还开始包含异常:An item with the same key has already been added. Key: System.Net.Http.Headers.HeaderDescriptor.

解析:

为了解决这个问题,我重构了我的服务以执行以下操作:

//Setup request
var request = new HttpRequestMessage(HttpMethod.Post, sharePointEndpoint)
{
    Content = strContent
};

request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", model.SharePointToken);
request.Headers.Add("Accept", "application/json;odata=verbose");
request.Headers.Add("X-HTTP-Method", "POST");
request.Headers.Add("ContentLength", bodyModelJson.Length.ToString());

// Send request, grab response
var response = await _httpClient.SendAsync(request);

在我的例子中,headers 取决于我想进行的操作。我删除了为 HTTPClient 添加默认 headers 的部分,而是将它们添加到正在发出的请求中。如您所见,我现在还使用了 SendAsync();

我还将HTTPClient作为单例注入。我必须使用我的 Constructor initiate 然后之后每当我想使用 HTTPClient.

我的服务:

private readonly HttpClient _httpClient;

public MyService(HttpClient httpClient)
{
    _httpClient = httpClient;
}

Startup.cs:

public void ConfigureServices(IServiceCollection services)
{
    /// SERVICES
    services.AddSingleton<HttpClient>();
}

希望这可以帮助遇到此问题的其他人。