1

这是我第一次在 StackOverflow 上提问,所以如果您需要更多说明,请告诉我。我正在尝试向系统用户发送带有附件的电子邮件,但是在发送消息后我很难清理文件系统上的附件。

背景

我正在使用 SSIS 编译来自 SQL 数据库的电子邮件收件人和消息内容列表,然后使用脚本组件使用 C# 进行实际的电子邮件发送。我更像是一名 DBA/项目经理,并且知道足够的编码来做一些小事情,但是自从我每天进行任何 .Net 编码以来已经有好几年了。

每次我运行例程时,附件文件(在本例中为 Excel 文件)都会在专用目录中成功创建,然后使用这些附件生成电子邮件并放置在拾取文件夹中。在创建和发送每封电子邮件时,我想从其目录中删除附件,但出现此错误:

该进程无法访问文件“D:\mailtest\{filename}.xls”,因为它正被另一个进程使用。

每次出错的特定文件都不同,在要生成的约 3000 封电子邮件中,它在大约 1200-1500 封电子邮件标记处失败。

代码

        //Create message
    using (MailMessage msg = new MailMessage())
    {
        msg.To.Add(new MailAddress(EmailRecipient));
        if (Row.Cc.Length > 0)
        {
            msg.CC.Add(new MailAddress(Row.Cc));
        }
        if (Row.Bcc.Length > 0)
        {
            msg.Bcc.Add(new MailAddress(Row.Bcc));
        }
        msg.From = new MailAddress(EmailSender);
        msg.Subject = MessageSubject;
        msg.Body = MessageBody +
            "\n" +
            "\n" +
            this.Variables.EmailFooter;
        msg.IsBodyHtml = true;
        msg.BodyEncoding = System.Text.Encoding.UTF8;
        //Add attachment data        
        if (File.Exists(attachmentPath))
        {
            Attachment data = new Attachment(attachmentPath);
            data.ContentDisposition.FileName = "Expiring Training.xls";
            msg.Attachments.Add(data);
        }
        if (this.Variables.UsePickupDirectory)  //Drops items into pickup directory
        {
            SmtpClient client = new SmtpClient(SMTPEndPoint, SMTPPort)
            {
                EnableSsl = false,
                DeliveryMethod = SmtpDeliveryMethod.SpecifiedPickupDirectory,
                PickupDirectoryLocation = this.Variables.PickupDirectory,
                Credentials = new NetworkCredential(UserName, Password)
            };
            try
            {
                client.Send(msg);
            }
            catch (Exception e)
            {
                //Debug.WriteLine(e);
                Row.ErrorMessage = e.Message;
            }
            finally
            {
                msg.Dispose();  //Release file
            }
        }
        else //Send to SMTP Server
        {
            SmtpClient client = new SmtpClient(SMTPEndPoint, SMTPPort)
            {
                EnableSsl = true,
                DeliveryMethod = SmtpDeliveryMethod.Network,
                Credentials = new NetworkCredential(UserName, Password)
            };
            try
            {
                client.Send(msg); 
            }
            catch (Exception e)
            {
                //Debug.WriteLine(e);
                Row.ErrorMessage = e.Message;
            }
            finally
            {
                //Add sleep to prevent sending more than 10 emails per second
                System.Threading.Thread.Sleep(100);
                msg.Dispose();  //Release file
            }
        }
    }
    //Remove attachment file
    if (File.Exists(attachmentPath))
    {
        File.Delete(attachmentPath);
    }

我尝试过的事情

  • 最初,我没有 using 块。我在类似的 SO question 上看到了这个建议,但在这里添加它似乎没有任何区别。
  • 我已将 Dispose() 添加到每个路径的 finally 块中(在这种情况下仅使用拾取目录),据我所知应该释放用作附件的文件上的所有锁。
  • 如果没有这些东西,它会在遇到的第一个文件上失败,这让我相信它正在工作,但只是一段时间,然后在执行过程中的某个随机点突然失败。
  • 错误中指定的文件在我搜索时没有显示在进程资源管理器中,所以它可能在错误发生后很快被释放,以至于我无法及时搜索?
  • 我尝试将“删除”功能完全移至单独的进程,在所有电子邮件发送后直接运行,但无论如何都会出现相同的消息。

如果有人知道可能发生的事情,我将不胜感激。

新信息

添加一些额外的错误处理确实改善了一些事情,但并没有完全解决它。我已经成功运行了几次,每当出现错误时,它只出现在大约 3000 个文件中的 1 个上。但它确实显示了相同的错误:“该进程无法访问文件 'D:\mailtest{...}.xls',因为它正被另一个进程使用。” 该错误来自 File.Delete 行。这让我想知道如何通过添加更多的东西来减少错误。发送和删除是否发生得如此之快以至于它踩到了自己的脚趾?把东西扔进去会减慢它的速度,让它能够跟上吗?

新信息 2

我接受了 Jamal 的建议,并在发送和删除之间添加了 500 毫秒的延迟,但前提是第一次删除尝试失败。到目前为止,连续 10 次运行没有错误,而在添加之前,它每次运行都以某种方式失败。但是,FireInformation 消息从未出现在输出窗口中,导致我认为它从未到达该块,所以我不确定为什么添加它似乎有效。

        //Remove attachment file
    if (File.Exists(attachmentPath))
    {
        try
        {
            File.Delete(attachmentPath);
        }
        catch
        {
            try
            {
                this.ComponentMetaData.FireInformation(0, "Delete failed", "Trying Delete again after 500ms", "", 0, fireAgain);
                System.Threading.Thread.Sleep(500);
                File.Delete(attachmentPath);
            }
            catch (Exception e)
            { 
                Row.ErrorMessage = e.Message;
                this.ComponentMetaData.FireInformation(e.HResult, e.Source, e.Message, e.HelpLink, 0, fireAgain);
            }
        }
    }
4

0 回答 0