0

i've got a problem with closing a temporary file. In my method I'm, generating an ics file, write text in it and then send it using MimeMail. The problem is that I don't know how to close the path so that I can access it to delete it after the mail was send. And MimeMail does not provide a solution like message.Dispose() or message.Close().

Here is my code:

public void SendEmailWithICal(string toEmailAddress, string subject, string textBody)
    {
        var message = new MimeMessage();
        message.From.Add(
            new MailboxAddress("Chronicus Dev", this.UserName));

        message.To.Add(new MailboxAddress(toEmailAddress.Trim(), toEmailAddress.ToString()));

        message.Subject = subject;

        CalenderItems iCalender = new CalenderItems();
        iCalender.GenerateEvent("Neuer Kalendereintrag");

        var termin = iCalender.iCal;

        string path = "TempIcsFiles/file.ics";
        if (File.Exists(path))
        {
            File.Delete(path);
        }
        {
            // Create File and Write into it
            FileStream fs = new FileStream(path, FileMode.OpenOrCreate);
            StreamWriter str = new StreamWriter(fs);
            str.BaseStream.Seek(0, SeekOrigin.End);
            str.Write(termin.ToString());

            //Close Filestream and Streamwriter
            str.Flush();
            str.Dispose();
            fs.Dispose();


                           //Add as Attachment
            var attachment = new MimePart("image", "gif")
            {
                ContentObject = new ContentObject(File.OpenRead(path), ContentEncoding.Default),
                ContentDisposition = new ContentDisposition(ContentDisposition.Attachment),
                ContentTransferEncoding = ContentEncoding.Base64,
                FileName = Path.GetFileName(path)
            };

            var body = new TextPart("plain")
            {
                Text = "Meine ICal Mail"
            };
            //Configure Email
            var multipart = new Multipart("mixed");
            multipart.Add(body);
            multipart.Add(attachment);
            message.Body = multipart;

            //Send Email
            using (var client = new SmtpClient())
            {
                client.Connect(HostName, Port, false);
                client.Authenticate(UserName, Password);
                client.Send(message);
                client.Disconnect(true);
            }

            //TODO Close File

            //Trying to Delete, but Exception
            File.Delete(path);
        }
    }

Thanks for any help!

davidrue
  • 159
  • 11

2 Answers2

2

Try to relocate the File.OpenRead(path), and wrap the whole message object in using() like this:

public void SendEmailWithICal(string toEmailAddress, string subject, string textBody)
{
    CalenderItems iCalender = new CalenderItems();
    iCalender.GenerateEvent("Neuer Kalendereintrag");
    var termin = iCalender.iCal;
    string path = "TempIcsFiles/file.ics";
    if (File.Exists(path))
    {
        File.Delete(path);
    }
    //Create file and write to it
    using (var fs = new FileStream(path, FileMode.OpenOrCreate))
    {
        using (var str = new StreamWriter(fs))
        {
            str.BaseStream.Seek(0, SeekOrigin.End);
            str.Write(termin.ToString());
            //Close Filestream and Streamwriter
            str.Flush();
        }
    }
    //Compose the message
    using (var read_stream = File.OpenRead(path))
    {
        using (var message = new MimeMessage())
        {
            message.From.Add(new MailboxAddress("Chronicus Dev", this.UserName));
            message.To.Add(new MailboxAddress(toEmailAddress.Trim(), toEmailAddress.ToString()));
            message.Subject = subject;
            //Add as Attachment
            var attachment = new MimePart("image", "gif")
            {
                ContentObject = new ContentObject(read_stream, ContentEncoding.Default),
                ContentDisposition = new ContentDisposition(ContentDisposition.Attachment),
                ContentTransferEncoding = ContentEncoding.Base64,
                FileName = Path.GetFileName(path)
            };

            var body = new TextPart("plain")
            {
                Text = "Meine ICal Mail"
            };
            //Configure Email
            var multipart = new Multipart("mixed");
            multipart.Add(body);
            multipart.Add(attachment);
            message.Body = multipart;

            //Send Email
            using (var client = new SmtpClient())
            {
                client.Connect(HostName, Port, false);
                client.Authenticate(UserName, Password);
                client.Send(message);
                client.Disconnect(true);
            }
        }
    }
    //Delete temporary file
    File.Delete(path);
}

this should guarantee a closed file, assuming that client.Send is an entirely synchronous operation.

see also this possible duplicate https://stackoverflow.com/questions/1296380/smtp-send-is-locking-up-my-files-c-sharp

Community
  • 1
  • 1
Cee McSharpface
  • 8,493
  • 3
  • 36
  • 77
  • Thank you for the answer, but i still receive the exception that I can't access the file, when I try to delete it at the end of the method. – davidrue Jan 07 '16 at 17:31
  • That other question I referred to suggests to wrap the message object also in using()... try like using (var mail = new MailMessage()) { } across eveything down until before the File.Delete(). – Cee McSharpface Jan 07 '16 at 17:34
  • Thanks again, I receive an error: "'MimeMessage': type used in a using statement must be implicity convertible to 'System.IDisposable'". The problem is that I can't use normal MailMessage of System.Net, instead of that MimeMessage because MailMessage is not supported in ASP.NET 5 yet – davidrue Jan 07 '16 at 17:47
  • wait a second... where is that "MimeMessage" class coming from? Is that a third party component like aspNetMime, mimeKit? the original .NET MailMessage is IDisposable. MimeMessage might use MailMessage under the hood and not properly dispose it? (just speculations) – Cee McSharpface Jan 07 '16 at 17:52
  • 1
    Yes, MimeMessage is part of MimeKit or/and MailKit.The problem is that there is no possiblity for me to choose the original MailMessage, see this: http://stackoverflow.com/questions/28036820/sending-mail-with-asp-net-vnext – davidrue Jan 07 '16 at 17:59
  • weird. then we must conclude that there is a file handle leak somewhere inside MimeMessage (bad), or it is processing attachments asynchronously (fine). I do not know that component and unless someone else has a better idea I would suggest a) you implement a file garbage collection mechanism which periodically cleans up the ics files; or b) you write termin.ToString() into a memory stream, and assign that instead of the filestream in the attachment constructor, bypassing the need to write to the file system entirely. – Cee McSharpface Jan 07 '16 at 18:07
  • Okay, I'll try these solutions. Thank for your help – davidrue Jan 07 '16 at 18:30
1

Use this code:

File.Create(FilePath).Close();
File.WriteAllText(FileText);
Shibu Thomas
  • 3,148
  • 2
  • 24
  • 26
  • 1
    while this is a lot shorter then the OP's code to write the file, I don't see how it would address the problem. The file likely remains open because it is opened a second time - for reading - in the ContentObject constructor. Open it in a using() { } block before you assign it in your constructor of the var attachment, place the closing } of the using block right before the call to File.Delete() – Cee McSharpface Jan 07 '16 at 17:15
  • @dlatikay Thanks, but what do I insert into the () behind "using"? If i open the file there, will i not get an exception while opening it inside the attachment constructor? – davidrue Jan 07 '16 at 17:18
  • see my answer below - I mean to relocate the File.OpenRead from inside the attachment constructor to two levels of indentation up. – Cee McSharpface Jan 07 '16 at 17:24