Search code examples
c#asp.netwebformskentico

PdfGenerator EmailMessage EmailAttachment - Cannot access a closed Stream


An error System.ObjectDisposedException with message Cannot access a closed Stream is being thrown when attempting to attach and send an System.IO.MemoryStream EmailAttachment to an EmailMessage. The attachment is being generated using HtmlRenderer.PdfSharp. The attaching and sending is being done within a using statement.

using (MemoryStream ms = new MemoryStream())
{
    var pdf = PdfGenerator.GeneratePdf("<html><body>foo</body></html>", PdfSharp.PageSize.Letter);
    // 2nd argument is to NOT close stream
    pdf.Save(ms, false);

    EmailMessage em = new EmailMessage();
    em.EmailFormat = EmailFormatEnum.Html;
    em.From = "[email protected]";
    em.Recipients = "[email protected]";
    em.Subject = "Attachment Name";
    em.Body = "There is an attachment";

    var attachment = new EmailAttachment(ms, "foo.pdf");
    em.Attachments.Add(attachment);

    // SiteContext.CurrentSiteName argument is just a site name required for Kentico CMS
    // true argument is send immediately
    EmailSender.SendEmail(SiteContext.CurrentSiteName, em, true);    
}

If I do the following instead, it is successful the error does not occur as long as I do not attempt to ms.Dispose() within the try block:

MemoryStream ms = new MemoryStream();
try
{
    var pdf = PdfGenerator.GeneratePdf(html, PdfSharp.PageSize.A4);
    pdf.Save(ms, false);

    EmailMessage em = new EmailMessage();
    em.EmailFormat = EmailFormatEnum.Html;
    em.From = "[email protected]";
    em.Recipients = "[email protected]";
    em.Subject = "Attachment Name";
    em.Body = "There is an attachment";

    var attachment = new EmailAttachment(ms, "foo.pdf");
    em.Attachments.Add(attachment);

    EmailSender.SendEmail(SiteContext.CurrentSiteName, em, true);
}
catch (Exception)
{
    ms.Dispose();
}

The concern with the 2nd approach is that the MemoryStream is not being explicitly disposed. Is the MemoryStream inherently being disposed with this approach? If put ms.Dispose(); after this entire block the same Cannot access a closed Stream occurs. Is this a memory leak waiting to happen? How can I dispose the MemoryStream without closing the stream to allow the email message to send the attachment?

Is this an issue with needing to somehow wait for SendEmail() to actually execute it's functionality?

Thank you for any help you can provide.


Solution

  • I was able to come to a solution using the following code. It really may have been more of a Kentico CMS issue regarding the sendImmediately argument of SendMail(). By setting that to false (default), I was able to attach the stream as an EmailAttachment without the System.ObjectDisposedException error all within using statement.

    using (MemoryStream ms = new MemoryStream())
    {
        var pdf = PdfGenerator.GeneratePdf(html, PdfSharp.PageSize.Letter);
        pdf.Save(ms, false);
    
        EmailMessage em = new EmailMessage();
        em.EmailFormat = EmailFormatEnum.Html;
        em.From = "[email protected]";
        em.Recipients = "[email protected]";
        em.Subject = "Attachment Name";
        em.Body = "There is an attachment.";
    
        var attachment = new EmailAttachment(ms, "foo.pdf");
        em.Attachments.Add(attachment);
    
        // default sendImmediately of false
        EmailSender.SendEmail(SiteContext.CurrentSiteName, em);
    }