You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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释放,会造成文件句柄泄漏
修复方案
  1. 导出原邮件到本地的正确实现:通过加载邮件的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);
  1. 正确创建回复邮件对象,不要直接Bind原邮件修改:
// 替换原有的EmailMessage email = EmailMessage.Bind(...)逻辑
EmailMessage email = originalEmail.CreateReply(true); // true表示回复带原邮件内容
  1. 删除非PR环境下email.CcRecipients.Add(null)这行无效代码
  2. 所有文件流操作都用using块包裹,确保资源释放

内容的提问来源于stack exchange,提问作者Aaditya R Krishnan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.03 05:15:54