Search code examples
c#idisposablestreamwritermemorystream

Why does StreamWriter need to be open to access my MemoryStream?


I have some test code that's preparing a MemoryStream that will eventually be read by an object. Here's how I want to write it:

        var manager = new LeaderboardImportManager(leaderboard);
        var columnNames = manager.ColumnNames;

        var stream = new MemoryStream();
        using (var writer = new StreamWriter(stream))
        {
            writer.WriteLine(string.Join(",", columnNames));
            foreach (var user in users)
            {
                var row = leaderboard.Metrics.Select(m => Faker.RandomNumber.Next().ToString()).ToList();
                row.Insert(0, user.UserName);
                writer.WriteLine(string.Join(",", row));
            }

            writer.Flush();
            stream.Position = 0;
        }

        return stream;

But when I do it that way, my stream object becomes unreadable and my test fails, so I have to do it like this:

        var manager = new LeaderboardImportManager(leaderboard);
        var columnNames = manager.ColumnNames;

        var stream = new MemoryStream();
        var writer = new StreamWriter(stream);

        writer.WriteLine(string.Join(",", columnNames));
        foreach (var user in users)
        {
            var row = leaderboard.Metrics.Select(m => Faker.RandomNumber.Next().ToString()).ToList();
            row.Insert(0, user.UserName);
            writer.WriteLine(string.Join(",", row));
        }

        writer.Flush();
        stream.Position = 0;
        return stream;

This, of course, prevents me from being able to dispose of my StreamWriter object, which as I understand it, should definitely be disposed of.

Why does the StreamWriter need to remain open if I've flushed its contents to the MemoryStream object already?

I can think of some very inconvenient ways to work around this, but I'd like to know why it doesn't work the way I want it, and whether or not there's something I can do to make it work that way. Any advice is appreciated, thanks!


Solution

  • The other answers indicate I should use the constructor that has a leaveOpen param and set it to true, but I dislike this because the constructor also requires a bufferSize argument.

    However, I realized that I can get away with this just as easily:

            // new method body, returns byte array
    
            var stream = new MemoryStream();
            using (var writer = new StreamWriter(stream))
            {
                writer.WriteLine(string.Join(",", columnNames));
                foreach (var user in users)
                {
                    var row = leaderboard.Metrics.Select(m => Faker.RandomNumber.Next().ToString()).ToList();
                    row.Insert(0, user.UserName);
                    writer.WriteLine(string.Join(",", row));
                }
    
                writer.Flush();
                stream.Position = 0;
            }
    
            return stream.ToArray();
    
            // consumer opens a new stream using the bytes
    
            using (var stream = new MemoryStream(this.GetCSVStream(leaderboard, users)))
            {
                mockFile.Setup(f => f.InputStream).Returns(stream);
                this.service.UpdateEntries(update.ID, mockFile.Object);
            }