这是我第一次在 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);
}
}
}