使用EWS发送回复邮件时抛出System.InvalidOperationException异常
问题描述
使用Exchange WebService(EWS)开发回复邮件功能时触发异常,流程阻塞,异常信息如下:
System.InvalidOperationException HResult=0x80131509 Message=This operation can't be performed because this service object already has an ID. To update this service object, use the Update() method instead. Source=Microsoft.Exchange.WebServices StackTrace: at Microsoft.Exchange.WebServices.Data.ServiceObject.ThrowIfThisIsNotNew() at Microsoft.Exchange.WebServices.Data.Item.InternalCreate(FolderId parentFolderId, Nullable`1 messageDisposition, Nullable`1 sendInvitationsMode) at Microsoft.Exchange.WebServices.Data.Item.Save(FolderId parentFolderId) at DataLoadLibrary.DataLoad.Email.EWSOAuthAdapter.genericEmail(String recepient, String subject, String bodyMessage, Dictionary`2 replacementStrings, String cc, String bcc, String headerMessage, String footerMessage, FileInfo[] attachments, String[] filenames, EmailMessage originalEmail, ExchangeService exchangeClient) in C:\Path\DataLoadLibrary\DataLoad\Email\EWSOAuthAdapter.cs:line 95 at DataLoadLibrary.DataLoad.Email.OAuthBasedTestPortRequest.startDataload() in C:\Path\DataLoadLibrary\DataLoad\Email\OAuthBasedTestPortRequest.cs:line 132 at TestPortEmailReader.Program.<Main>d__8.MoveNext() in C:\Path\TestPortEmailReader\Program.cs:line 93 This exception was originally thrown at this call stack: [External Code] DataLoadLibrary.DataLoad.Email.EWSOAuthAdapter.genericEmail(string, string, string, System.Collections.Generic.Dictionary<string, string>, string, string, string, string, System.IO.FileInfo[], string[], Microsoft.Exchange.WebServices.Data.EmailMessage, Microsoft.Exchange.WebServices.Data.ExchangeService) in EWSOAuthAdapter.cs DataLoadLibrary.DataLoad.Email.OAuthBasedTestPortRequest.startDataload() in OAuthBasedTestPortRequest.cs TestPortEmailReader.Program.Main(string[]) in Program.cs
问题代码
public bool genericEmail( string recepient, string subject, string bodyMessage, Dictionary<string, string> replacementStrings = null, string cc = "", string bcc = "", string headerMessage = "", string footerMessage = "", FileInfo[] attachments = null, string[] filenames = null, EmailMessage originalEmail = null, ExchangeService exchangeClient = null) { EmailMessage email = EmailMessage.Bind(exchangeClient, originalEmail.Id, PropertySet.FirstClassProperties); //exchangeClient.LoadPropertiesForItems((IEnumerable<Item>)email, PropertySet.FirstClassProperties); email.Sender.Address = config.exchangeUsername + "@domain.com"; email.Sender.Name = "ABC"; if (recepient.Length == 0) { email.ToRecipients.Add(iPACTTeamEmail); } else { email.ToRecipients.Add(recepient.TrimEnd(';')); } if (cc.Length != 0) email.CcRecipients.Add(cc.TrimEnd(';')); if (config.environment != "PR") { email.Sender.Address = config.exchangeUsername + "@domain.com"; email.CcRecipients.Add(null); } if (attachments != null) { int i = 0; foreach (FileInfo fi in attachments) { FileStream theStream = new FileStream(fi.FullName, FileMode.OpenOrCreate); if (filenames == null) email.Attachments.AddFileAttachment(fi.Name, theStream); else email.Attachments.AddFileAttachment(filenames[i], theStream); i++; } } if (originalEmail != null) { string originalEmailFilename = config.extractFolder + "\\Emails" + originalEmail.Subject.Trim().Replace('\\', '_').Replace('/', '_').Replace(":", "_").Replace("\"", "_").Replace("*", "_").Replace("?", "_").Replace("<", "_").Replace(">", "_").Replace("|", "_") + "_" + originalEmail.DateTimeReceived.ToString("yyyyMMdd_HHmmss") + ".msg"; originalEmail.Save(originalEmailFilename); //此处触发上述异常 email.Attachments.AddFileAttachment(originalEmailFilename); } if (config.environment == "PR") email.Subject = subject.Trim(); else email.Subject = "[" + config.environment + "] " + subject.Trim(); if (replacementStrings != null) { foreach (KeyValuePair<string, string> kv in replacementStrings) { bodyMessage = bodyMessage.Replace(kv.Key, kv.Value); } } bodyMessage = bodyMessage.Replace("{headerMessage}", headerMessage); bodyMessage = bodyMessage.Replace("{footerMessage}", footerMessage); email.Body = new MessageBody(BodyType.HTML, bodyMessage); email.Send(); return true; }
根因分析
- 异常触发的直接原因:
originalEmail是通过EmailMessage.Bind()从Exchange服务器获取的已存在对象,本身已经分配了唯一ID。EWS托管API中,不带参数/传入FolderId参数的Save()方法仅能用于从未提交到服务器的新建服务对象,作用是将新对象保存到服务器指定邮箱文件夹;对已存在的服务对象调用该方法就会抛出题目中的异常,提示需要用Update()方法更新服务器上的对象。 - 代码逻辑错误:EWS SDK根本没有提供传入本地路径将邮件保存为本地文件的
Save(string localPath)重载,你写的originalEmail.Save(originalEmailFilename)不会实现存本地文件的效果,SDK会把传入的字符串错误识别为FolderId,走保存到服务器的逻辑,必然触发异常。 - 额外隐藏问题:
- 直接Bind原邮件对象修改后发送,相当于直接修改服务器上的原邮件内容,不是创建回复邮件,不符合回复邮件的业务逻辑
- 非PR环境下
email.CcRecipients.Add(null)会触发空参数异常 - 打开附件文件流后没有做Dispose释放,会造成文件句柄泄漏
修复方案
- 导出原邮件到本地的正确实现:通过加载邮件的MIME内容,将字节流写入本地文件,EWS原生支持导出为.eml格式,如果需要.msg格式需要引入第三方MIME处理库转换,示例代码:
// 替换原有的originalEmail.Save(originalEmailFilename)逻辑 string originalEmailFilename = config.extractFolder + "\\Emails" + originalEmail.Subject.Trim().Replace('\\', '_').Replace('/', '_').Replace(":", "_").Replace("\"", "_").Replace("*", "_").Replace("?", "_").Replace("<", "_").Replace(">", "_").Replace("|", "_") + "_" + originalEmail.DateTimeReceived.ToString("yyyyMMdd_HHmmss") + ".eml"; // 加载原邮件的MIME内容 originalEmail.Load(new PropertySet(ItemSchema.MimeContent)); using (FileStream fs = new FileStream(originalEmailFilename, FileMode.Create, FileAccess.Write)) { fs.Write(originalEmail.MimeContent.Content, 0, originalEmail.MimeContent.Content.Length); } email.Attachments.AddFileAttachment(originalEmailFilename);
- 正确创建回复邮件对象,不要直接Bind原邮件修改:
// 替换原有的EmailMessage email = EmailMessage.Bind(...)逻辑 EmailMessage email = originalEmail.CreateReply(true); // true表示回复带原邮件内容
- 删除非PR环境下
email.CcRecipients.Add(null)这行无效代码 - 所有文件流操作都用
using块包裹,确保资源释放
内容的提问来源于stack exchange,提问作者Aaditya R Krishnan
相关产品推荐
相关产品推荐

