title: .Net Framework Bug: System.Net.Mail.AttachmentBase disposes of streams it did't create
body: <p>Before submitting this as a bug, I wanted to get community feedback to see if this should be considered a bug.</p>
  
  <p>When creating a <code>LinkedResource</code> or other class that derives from <code>AttachmentBase</code>, there are constructor overloads that take either a <a href="https://referencesource.microsoft.com/#System/net/System/Net/mail/Attachment.cs,0a9d3f581698a5b7,references" rel="nofollow noreferrer">file name</a> or a <a href="https://referencesource.microsoft.com/#System/net/System/Net/mail/Attachment.cs,5072a46ba8103c1e,references" rel="nofollow noreferrer"><code>Stream</code></a>. When given a file name, <code>AttachmentBase</code> internally creates a <code>FileStream</code> and assigns it the private <code>MimePart part</code> content using <code>MimePart.SetContent(Stream)</code>. When creating an <code>AttachmentBase</code> using the constructor that takes a <code>Stream</code>, it directly assigns the underlying content to this <code>Stream</code>.</p>
  
  <p>When the <code>AttachmentBase</code> is <a href="https://referencesource.microsoft.com/#System/net/System/Net/mail/Attachment.cs,546805343337df9b,references" rel="nofollow noreferrer">disposed</a>, it internally disposes of the <code>MimePart</code>, which internally disposes of the <code>Stream</code> content. If the constructor taking a <code>Stream</code> was used, this <code>MimePart</code> class just disposed of a <code>Stream</code> it did not create! This bug is a side effect. Disposing of objects that a class/method doesn't own makes it impossible to reuse that object later. When creating a disposable object, that object should be able to be disposed before it is garbage collected. This allows it to free up resources like file handles captured by <code>FileStreams</code>.</p>
  
  <p>Suppose you need to send out a lot of emails, and each email has one image. The image is very large, and all the emails use this same exact image. You should be able to read this file into a <code>MemoryStream</code>, and reuse this <code>Stream</code> for the <code>LinkedResource</code> in each email. This would allow little disk read and low memory usage. <code>Dispose()</code> is invoked on each message and its resources to free up any handles and unmanaged memory once the message is sent. (Again, if it implements <code>IDisposable</code>, it should be able to be disposed as soon as it's no longer needed. The code that called <code>Dispose()</code> should always be the same code that created the object.) Because this <code>Dispose()</code> method is not implemented correctly, it just disposed the underlying <code>Stream</code> to the image that it did not create. When subsequent messages try to use this <code>Stream</code>, they will throw an <code>ObjectDisposedException</code>.</p>
  
  <p>A temporary workaround for this bug is to copy the <code>MemoryStream</code> for each message, and use the copied <code>Streams</code>. This still results in little disk read, but now the large image is copied a bunch of times in memory.</p>
  
  <p>This could have all been avoided by following correct patterns and practices and allowing the code that created the <code>Stream</code> to be the one to dispose of it, and not assuming it would only be used once. Here is a fix for this bug. The code should replace the similar methods. This allows <code>MimePart</code> to be backward compatible. The change to <code>AttachmentBase</code> may be a breaking change for code that passes in a <code>Stream</code>. The fix is to handle the <code>Stream</code> you pass in by disposing of it yourself properly.</p>
  
  <pre><code>public abstract class AttachmentBase : IDisposable
  {
      MimePart part;
  
      protected AttachmentBase(Stream contentStream)
      {
          part.SetContent(contentStream, true);
      }
  
      protected AttachmentBase(Stream contentStream, string mediaType)
      {
          part.SetContent(contentStream, null, mediaType, true);
      }
  
      internal AttachmentBase(Stream contentStream, string name, string mediaType)
      {
          part.SetContent(contentStream, name, mediaType, true);
      }
  
      protected AttachmentBase(Stream contentStream, ContentType contentType)
      {
          part.SetContent(contentStream, contentType, true);
      }
  }
  
  internal class MimePart : MimeBasePart, IDisposable
  {
      private bool _keepOpen;
  
      internal void SetContent(Stream stream, bool keepOpen = false)
      {
          if (stream == null)
          {
              throw new ArgumentNullException("stream");
          }
  
          if (streamSet &amp;&amp; !_keepOpen)
          {
              this.stream.Dispose();
          }
  
          this.stream = stream;
          streamSet = true;
          streamUsedOnce = false;
          TransferEncoding = TransferEncoding.Base64;
          _keepOpen = keepOpen;
      }
  
      internal void SetContent(Stream stream, string name, string mimeType, bool keepOpen = false)
      {
          if (stream == null)
          {
              throw new ArgumentNullException("stream");
          }
  
          if (!string.IsNullOrEmpty(mimeType))
          {
              contentType = new ContentType(mimeType);
          }
  
          if (!string.IsNullOrEmpty(name))
          {
              ContentType.Name = name;
          }
  
          SetContent(stream, keepOpen);
      }
  
      internal void SetContent(Stream stream, ContentType contentType, bool keepOpen = false)
      {
          if (stream == null)
          {
              throw new ArgumentNullException("stream");
          }
  
          this.contentType = contentType;
          SetContent(stream, keepOpen);
      }
  
      public void Dispose()
      {
          if (stream != null &amp;&amp; !_keepOpen)
          {
              stream.Dispose();
          }
      }
  }
  </code></pre>