Recently I had to work with a CryptoStream in .NET Standard 2.0. Unfortunately (in Standard 2.0), we have no available constructor overload for keeping the passed Stream open when the CryptoStream is disposed.
Alternative solutions I have seen include creating a custom CryptoStream, playing with fields using reflections or simply not disposing the CryptoStream directly. What I’m sure several people have thought off, but which I didn’t see at that time, was a custom Stream implementation for the stream we pass along:
/// <summary> /// A simple wrapper to prevent streams from being closed/disposed by other code /// </summary> public class KeepOpenStreamWrapper : Stream { /// <summary> /// The original stream this instance wraps around /// </summary> public Stream WrappedStream { get; } /// <inheritdoc /> public override bool CanRead => WrappedStream.CanRead; /// <inheritdoc /> public override bool CanSeek => WrappedStream.CanSeek; /// <inheritdoc /> public override bool CanWrite => WrappedStream.CanWrite; /// <inheritdoc /> public override long Length => WrappedStream.Length; /// <inheritdoc /> public override long Position { get => WrappedStream.Position; set => WrappedStream.Position = value; } private KeepOpenStreamWrapper(Stream stream) => WrappedStream = stream; /// <summary> /// Creates a new instance of <see cref="KeepOpenStreamWrapper"/> for the given <see cref="Stream"/> /// </summary> /// <param name="stream"></param> /// <returns>A new instance wrapping around <paramref name="stream"/>, or null if <paramref name="stream"/> is null</returns> public static KeepOpenStreamWrapper Wrap(Stream stream) => stream is null ? null : new KeepOpenStreamWrapper(stream); /// <inheritdoc /> public override void Close() { //Do nothing } /// <inheritdoc /> public override void Flush() => WrappedStream.Flush(); /// <inheritdoc /> public override long Seek(long offset, SeekOrigin origin) => WrappedStream.Seek(offset, origin); /// <inheritdoc /> public override void SetLength(long value) => WrappedStream.SetLength(value); /// <inheritdoc /> public override int Read(byte[] buffer, int offset, int count) => WrappedStream.Read(buffer, offset, count); /// <inheritdoc /> public override void Write(byte[] buffer, int offset, int count) => WrappedStream.Write(buffer, offset, count); /// <inheritdoc /> protected override void Dispose(bool disposing) { //Do nothing } }
Very simple implementation. Essentially all Stream methods/properties are redirected into the actual stream. Only Close and Dispose are special, in that they don’t do anything at all.
You can still close/dispose the original stream, as long as you use its reference. CryptoStream would only get the wrapper though, meaning the stream was not closed after disposing it.
