调用attachment.load时出现EWS SaveHttpResponseHeaders异常:已添加相同键的项
这个错误是EWS托管API的已知缺陷:旧版本的ExchangeServiceBase.SaveHttpResponseHeaders方法在存储服务端返回的HTTP响应头时,直接调用Dictionary.Add方法插入键值对,当Exchange服务端返回了重复名称的响应头时,就会触发重复键异常。
错误信息: 已添加具有相同键的项。
错误堆栈跟踪:
在 System.ThrowHelper.ThrowArgumentException(ExceptionResource resource)
在 System.Collections.Generic.Dictionary2.Insert(TKey key, TValue value, Boolean add) 在 System.Collections.Generic.Dictionary2.Add(TKey key, TValue value)
在 Microsoft.Exchange.WebServices.Data.ExchangeServiceBase.SaveHttpResponseHeaders(WebHeaderCollection headers)
在 Microsoft.Exchange.WebServices.Data.ExchangeServiceBase.ProcessHttpResponseHeaders(TraceFlags traceType, IEwsHttpWebResponse response)
在 Microsoft.Exchange.WebServices.Data.SimpleServiceRequestBase.ReadResponse(IEwsHttpWebResponse response)
在 Microsoft.Exchange.WebServices.Data.MultiResponseServiceRequest`1.Execute()
在 Microsoft.Exchange.WebServices.Data.FileAttachment.Load(Stream stream)
在 ERSupportGetOutlookEmail.EmailExchange.SaveEmailContentToDB(EmailMessage email)
- 方案1:升级EWS托管API版本
微软在EWS托管API 2.2及以上版本修复了该问题,新版本会先判断键是否存在,存在则覆盖而非直接新增。直接将项目中引用的Microsoft.Exchange.WebServicesNuGet包升级到最新稳定版即可解决绝大多数该类问题。 - 方案2:重写响应头处理逻辑(无法升级版本时使用)
继承ExchangeService类,重写响应头处理方法,提前对响应头去重:public class SafeExchangeService : ExchangeService { public SafeExchangeService(ExchangeVersion version) : base(version) { } protected override void ProcessHttpResponseHeaders(TraceFlags traceType, IEwsHttpWebResponse response) { // 先对响应头去重后再传给基类处理 var deduplicatedHeaders = new WebHeaderCollection(); foreach (string headerKey in response.Headers.AllKeys) { if (!deduplicatedHeaders.AllKeys.Contains(headerKey)) { deduplicatedHeaders.Add(headerKey, response.Headers[headerKey]); } } // 自定义实现IEwsHttpWebResponse接口,包装原响应并替换为去重后的Headers即可 var safeResponse = new EwsHttpWebResponseWrapper(response, deduplicatedHeaders); base.ProcessHttpResponseHeaders(traceType, safeResponse); } } - 方案3:临时重试规避
偶发场景下可在Attachment.Load外层加重试逻辑,降低报错概率:int retryLeft = 3; while (retryLeft > 0) { try { attachment.Load(targetStream); break; } catch (ArgumentException ex) when (ex.Message.Contains("相同键")) { retryLeft--; if (retryLeft == 0) throw; Thread.Sleep(50); } }
内容的提问来源于stack exchange,提问作者Tanveer Fatima

