6

I was playing around with MailKit/MimeKit and something is bugging me. I wanted to send e-mails with attachments using streams. MimeKit offers the BodyBuilderclass which makes it very easy to create the body message and attach files:

public void SendEmail(string body, Stream attachment, string fileName)
{
    var message = new MimeMessage();
    message.From.Add(new MailboxAddress("Carl", "carl@site.com"));
    message.To.Add(new MailboxAddress("Rick", "rick@site.com"));
    message.Subject = "Things got messy...?";

    var builder = new BodyBuilder();
    builder.TextBody = body;

    builder.Attachments.Add(fileName, attachment);

    message.Body = builder.ToMessageBody();

    using (var client = new SmtpClient())
    {
        // code to send e-mail here...
    }
}

I generate the stream elsewhere in my code and I don't close it, so I can pass it on to MimeKit. The thing that is not clear is: Do MimeKit disposes the stream? Basically (as far as I'm aware) the consumer usually is the responsible for disposing Streams. I'm also aware that calling dispose on a MemoryStream (which I'm using basically) won't free up any resources.. but will prevent from reading/writing to it. But if, in the future, the implementations change to another type of Stream, things can get more complex.

I also dig into MikeKit source code, and found that the Stream passed in to the AttachmentCollection.Add gets 'added' into a MemoryBlockStream which inherits from Stream (and implements Dispose) so I'm supposing it get's disposed but, at this point, I'm just guessing.

Any ideas?

jpgrassi
  • 5,482
  • 2
  • 36
  • 55

2 Answers2

6

By inspecting the code on MimeKit. Specifically the two overloads:

1 - Overload that takes byte[] data, you can see that they create a stream and keep the ownership of it. That's done by creating the stream on a using block. One can assume already that the code downstream (CreateAttachment()) is not handling any Stream dispose.

2 - The overload that you mention, the stream is just passed through to CreateAttachment().

I'd say that in this case you're better off disposing it yourself if you can. That is, if it can be done only once the stream has been consumed.

In fact, after calling Add, your Stream was already consumed. All data will sit in memory once the method returns and you could dispose your stream. You can see this on LoadContent().

Just as you don't want to avoid calling Dispose because you have a MemoryStream knowingly that another stream that requires Dispose could be used later. Disposing your Stream after calling Add could be a bad idea. In case the behavior of the library changes in the future and at the time of the call to Add your stream is not consumed yet (which would be the expected behavior in my opinion).

The stream would only be Read at the time of writing the data to the NetworkSocket. In fact Streaming data, without loading it all in RAM.

In such case you'd only be able to Dispose the Stream after the Mailkit.Send was called.

Bruno Garcia
  • 6,029
  • 3
  • 25
  • 38
4

You don't need to dispose those streams. You can if you want, but it's not necessary.

jstedfast
  • 35,744
  • 5
  • 97
  • 110
  • Can you please elabortate on 'why' this is the case? Is `MimeKit` doing this for you? This question makes me think back to the `HttpClient` scenario where we (well, heaps and heaps of us devs) keept disposing of that class because it inherits`IDispose` but then in 2016 we got told to keep it a singleton and not to dispose (and got explained why with code samples). So - just wanting to learn :) – Pure.Krome Jun 12 '17 at 00:34
  • 1
    That was exactly my point. I kind of knew the answer so one could argue that I didn't need the question in the first place.. but I wanted to know how it works so I don't keep doing things blindly. – jpgrassi Jun 12 '17 at 05:54
  • MemoryBlockStream's Dispose method is a no-op because you can't dispose byte arrays. Dispose methods are only needed when the class makes use of unmanaged resources. – jstedfast Jun 12 '17 at 10:45