Clear MemoryStream after passing the objects to a different function

1k views Asked by At

My main question is if I pass object to a function that are using a MemoryStream how to I go about clearing the memorystream? See my code below.

In more detail: I'm trying to send multiple emails while creating PDFs on the fly using iTextSharper. I want to make sure that I'm not doing something wrong with the MemoryStream here. I can't use a using statement because when I do my EmailPDF function says the connections are closed, also if I don't use a new MemoryStream for each PDF creation it overrides the previously created one.

I'm adding the pdfs to an attachment and then adding the attachments to a list. I then use that list to add the attachment to a mailmessage. I then send the message.

        private void EmailPDF(List<String> lstFields)
    {            
        MailMessage mm = new MailMessage("fromemailaddress", "toemailaddress")
        {
            Subject = "Test Email",
            IsBodyHtml = true,
            Body = "Testing email"
        };

        SmtpClient smtp = new SmtpClient
        {
            Host = "xxx.xxx.xxx.xxx"
        };

        List<System.Net.Mail.Attachment> attachments = FillAttachmentList(lstFields);

        foreach (System.Net.Mail.Attachment attach in attachments)
        {
            mm.Attachments.Add(attach);
        }

        smtp.Send(mm);
        attachments.Clear();            
    }


        private List<System.Net.Mail.Attachment> FillAttachmentList(List<String> lstFields)
    {
        List<System.Net.Mail.Attachment> attachments = new List<System.Net.Mail.Attachment>();

        foreach (String strField in lstFields)
        {                
            MemoryStream output = new MemoryStream();
            Document doc = new Document(PageSize.LETTER, 25, 25, 25, 25);

            try
            {
                String text = System.IO.File.ReadAllText(@"C:\PDFDirectory\" + strField + ".html");
                StringReader html = new StringReader(text);

                PdfWriter wri = PdfWriter.GetInstance(doc, output);
                doc.Open();

                XMLWorkerHelper.GetInstance().ParseXHtml(wri, doc, html);

                wri.CloseStream = false;
                doc.Close();

                attachments.Add(new Attachment(output, strField + ".pdf"));
                output.Position = 0;
            }
            catch (Exception ex)
            {
            }
        }

        return attachments;
    }
4

There are 4 answers

1
tschmit007 On BEST ANSWER

create a class

public class MyAttachment {
    MemoryStream output {get; set;}
    System.Net.Mail.Attachment {get; set;}
}

rewrite your FillAttachmentList(lstFields); method to provide a List<MyAttachment>;

instead of attachments.Clear(); write:

foreach (MyAttachment attach in attachments)
{
    if ( attach.output != null) {
        attach.output.Close();
    }
}
attachments.Clear();

In fact you should implement a MyAttachmentCollection : IEnumerable<MyAttachment>, IDisposable.

2
Stoyan Uzunov On

One option is to add finally after the catch and use Close() or Dispose(). When they are called on a MemoryStream, they serve to do two things:

Mark the object disposed so that future accidental usage of the object will throw an exception. Possibly release references to managed objects, which can make the GC's job a bit easier depending on the GC implementation. (On today's GC algorithms it makes no real difference, so this is a point for an academic discussion and has no significant real-world impact.)

MemoryStream does not have any unmanaged resources to dispose, so you don't technically have to dispose of it. The effect of not disposing a MemoryStream is roughly the same thing as dropping a reference to a byte[] (the GC will clean both up the same way).

Which one do I call? Is it necessary to call both? The Dispose() method of streams delegate directly to the Close() method2, so both do exactly the same thing. And you can skip it if you want. The GC will clean it after all.

1
Coastpear On

In your context, like @Stoyan said, it makes no much difference, if you want you can force GC to swipe the streams by using GC.Collect().

0
Charles Mager On

Your MemoryStream has no reference to unmanaged resources, so you're probably fine not to close or dispose of it. A search will find other questions that address this point.

However, MailMessage and all its component parts implement IDisposable. If you follow through the reference source you'll see that your stream in your attachment would be closed by disposing of your MailMessage.

So, to be a good citizen you should either call mm.Dispose() directly after sending the message or wrap your use of the MailMessage in a using statement.