0

I'm trying to create a CSV file (using the popular CsvHelper library) in memory and attach it to an email. See the following code:

public async Task<IActionResult> Send() {
    using var memoryStream = new MemoryStream();

    await CreateAsync(memoryStream, new List<Foo>() {
        new Foo()
    });

    await SendAsync(message => {
        message.Attachments.Add(new Attachment(memoryStream), "file.csv", "text/csv");
    });

    return Content("Sent!");
}

private async Task CreateAsync<T>(Stream stream, IEnumerable<T> records) {
    using var writer = new StreamWriter(stream);
    using var csv = new CsvWriter(writer, CultureInfo.InvariantCulture);
    await csv.WriteRecordsAsync(records);
}

private async Task SendAsync(Action<MailMessage>? options = null) {
    using var client = new SmtpClient("xxx", 25);
    using var mailMessage = new MailMessage("from@somewhere.com", "to@somewhere.com");
    options?.Invoke(mailMessage);
    await client.SendMailAsync(mailMessage);
}

This throws the error:

NotSupportedException: Stream does not support reading.

However if I try the following code to attach the file instead, then it works fine:

message.Attachments.Add(new Attachment(new MemoryStream(memoryStream.ToArray()), "file.csv", "text/csv"));

I'd appreciate if someone could help explain why the first example did not work, and how I can update it so that I don't have to create two MemoryStreams.

nfplee
  • 7,643
  • 12
  • 63
  • 124
  • I think problem can with the memory stream. Can help to you this answer ? https://stackoverflow.com/a/29917025/8810311 – Ramil Aliyev 007 Feb 28 '23 at 17:47
  • try using memoryStream.Position = 0; before sending... – pcalkins Feb 28 '23 at 19:37
  • one thing that jumps out is that in the one case it's got a "using" statement. This would, methinks, make it read only. (and if it goes out of scope its disposed...) You probably won't need that for the memory stream. It'll be disposed of when the Stream disposes. – pcalkins Feb 28 '23 at 19:52

1 Answers1

1

You are disposing the StreamWriter and this disposes the underlying stream, from the docs - The StreamWriter object calls Dispose() on the provided Stream object when StreamWriter.Dispose is called.

You can stop this from happening by using the leaveOpen option.

using var writer = new StreamWriter(stream, leaveOpen: true);
Angel Yordanov
  • 3,112
  • 1
  • 22
  • 19
  • Thanks, this worked a treat. I also had to set the position of the stream back to the start, which also didn't work previously due to the stream being closed. – nfplee Mar 01 '23 at 10:31