I have the following method:
[SuppressMessage("Reliability", "CA2000:Dispose objects before losing scope", Justification = "If we dispose of the csvWriter, it won't be available to write.")]
public static MemoryStream CreateCsvStream(IEnumerable<object> records)
{
MemoryStream memoryStream = new();
StreamWriter streamWriter = new(memoryStream);
CsvWriter csvWriter = new(streamWriter, CultureInfo.InvariantCulture);
csvWriter.Context.TypeConverterCache.AddConverter<bool>(new CollendaBooleanConverter());
csvWriter.Context.TypeConverterCache.AddConverter<bool?>(new CollendaBooleanConverter());
csvWriter.Context.TypeConverterCache.AddConverter<DateOnly>(new CollendaDateOnlyConverter());
csvWriter.Context.TypeConverterCache.AddConverter<DateOnly?>(new CollendaDateOnlyConverter());
csvWriter.Context.TypeConverterCache.AddConverter<decimal>(new CollendaDecimalConverter());
csvWriter.Context.TypeConverterCache.AddConverter<decimal?>(new CollendaDecimalConverter());
csvWriter.WriteRecords(records);
streamWriter.Flush();
return memoryStream;
}
This works, but as hinted in the SuppressMessage, if I use using so MemoryStream, StreamWriter, and/or CsvWriter are disposed of, when I later execute the following code:
private void Upload(MemoryStream memoryStream)
{
_sftpClient.Connect();
_ = memoryStream.Seek(0, SeekOrigin.Begin);
string filePath = _collendaSftpConfig?.FilePath
?.InsertTimestamp()
?? throw new InvalidOperationException("CollendaSftpConfig configuration is missing FilePath.");
// Renci.SshNet's Sftp Client seems to have some async support, but it seems much more complicated to consume.
// There is no clear benefit to using it at this time.
_sftpClient.UploadFile(memoryStream, filePath);
_sftpClient.Disconnect();
}
I'll get an error like:
System.ObjectDisposedException HResult=0x80131622 Message=Cannot access a closed Stream. Source=System.Private.CoreLib StackTrace: at System.ThrowHelper.ThrowObjectDisposedException_StreamClosed(String objectName) at System.IO.MemoryStream.Seek(Int64 offset, SeekOrigin loc) at Enpal.Collenda.Client.SftpUploader.CollendaSftpClient.Upload(MemoryStream memoryStream) in C:\projects\FinTech\collenda-finance-listener\Collenda.Client.SftpUploader\CollendaSftpClient.cs:line 59
I did not experiment with every permutation of adding or not adding using to each of these, but I did try adding to all and some of them and received similar results.
Of course, the simple "solution" is just to leave them out and suppress this, but I'm concerned whether this is a reliable solution and whether there may be a better way to handle this situation (and potentially similiar situations in the future).
You're trying to read from the stream afterwards, and that's all. In that case, you don't want to dispose of the stream (because you still want to be able to read) but probably you do want to dispose of the StreamWriter. (It won't actually matter with MemoryStream, but I can understand wanting to do it.)
The simplest option here is to use the StreamWriter constructor that allows you to suppress it closing the underlying stream:
public static MemoryStream CreateCsvStream(IEnumerable<object> records)
{
// No using statement here, because we want the stream to stay open
MemoryStream memoryStream = new();
// We *do* want to dispose of the StreamWriter
using StreamWriter streamWriter = new(memoryStream, leaveOpen: true);
// I assume that CsvWriter implements IDisposable too
using CsvWriter csvWriter = new(streamWriter, CultureInfo.InvariantCulture);
// Write to csvWriter here
...
// No need to flush the StreamWriter - that'll happen when it's disposed
return memoryStream;
}
StreamWriter by default closes underlying stream on Dispose, you can use constructor accepting leaveOpen parameter and set it to true:
leaveOpenBoolean
trueto leave the stream open after theStreamWriterobject is disposed; otherwise,false.
MemoryStream memoryStream = new();
using StreamWriter streamWriter = new(memoryStream, leaveOpen: true);
Also when wrapping the StreamWriter into using there is no more need to call Flush - it will happen automatically on Dispose.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With