web-dev-qa-db-fra.com

CsvHelper n'écrit rien dans le flux mémoire

J'ai la méthode suivante:

public byte[] WriteCsvWithHeaderToMemory<T>(IEnumerable<T> records) where T : class
{
    using (var memoryStream = new MemoryStream())
    using (var streamWriter = new StreamWriter(memoryStream))
    using (var csvWriter = new CsvWriter(streamWriter))
    {
        csvWriter.WriteRecords<T>(records);

        return memoryStream.ToArray();
    }
}

Qui est appelé avec une liste d'objets - éventuellement à partir d'une base de données, mais comme quelque chose ne fonctionne pas, je remplis simplement une collection statique. Les objets transmis sont les suivants:

using CsvHelper.Configuration;

namespace Application.Models.ViewModels
{
    public class Model
    {
        [CsvField(Name = "Field 1", Ignore = false)]
        public string Field1 { get; set; }

        [CsvField(Name = "Statistic 1", Ignore = false)]
        public int Stat1{ get; set; }

        [CsvField(Name = "Statistic 2", Ignore = false)]
        public int Stat2{ get; set; }

        [CsvField(Name = "Statistic 3", Ignore = false)]
        public int Stat3{ get; set; }

        [CsvField(Name = "Statistic 4", Ignore = false)]
        public int Stat4{ get; set; }
    }
}

Ce que j'essaie de faire, c'est d'écrire une collection sur un fichier csv pour le télécharger dans une application MVC. Chaque fois que j'essaie d'écrire dans la méthode, le MemoryStream revient avec une longueur nulle et rien ne lui est transmis. Je l'ai utilisé auparavant, mais pour une raison quelconque, cela ne fonctionne tout simplement pas - je suis quelque peu confus. Quelqu'un peut-il me signaler ce que j'ai fait de mal ici?

À votre santé

40
Ian Cotterill

Mettez csvWriter.Flush(); avant de retourner pour vider le scripteur/flux.

EDIT: Par la réponse de Jack. Ce doit être le flux qui est vidé, pas le csvWriter. streamWriter.Flush();. Quitter la solution d'origine, mais en ajoutant cette correction.

EDIT 2: Ma réponse préférée est: https://stackoverflow.com/a/22997765/179505 Laissez les instructions using faire le gros du travail pour vous

27
Eli Gassert

Vous avez déjà un bloc using ce qui est génial. Cela videra votre écrivain pour vous. Vous pouvez simplement modifier légèrement votre code pour qu'il fonctionne.

using (var memoryStream = new MemoryStream())
{
    using (var streamWriter = new StreamWriter(memoryStream))
    using (var csvWriter = new CsvWriter(streamWriter))
    {
        csvWriter.WriteRecords<T>(records);
    } // StreamWriter gets flushed here.

    return memoryStream.ToArray();
}

Si vous activez AutoFlush, vous devez être prudent. Cela videra après chaque écriture. Si votre flux est un flux réseau et sur le fil, il sera très lent.

47
Josh Close

En rassemblant tout cela (et les commentaires pour les corrections), y compris la réinitialisation de la position du flux de mémoire, la solution finale pour moi était;

        using (MemoryStream ms = new MemoryStream())
        {
            using (TextWriter tw = new StreamWriter(ms))
            using (CsvWriter csv = new CsvWriter(tw))
            {
                csv.WriteRecords(errors); // Converts error records to CSV

                tw.Flush(); // flush the buffered text to stream
                ms.Seek(0, SeekOrigin.Begin); // reset stream position

                Attachment a = new Attachment(ms, "errors.csv"); // Create attachment from the stream
                // I sent an email here with the csv attached.
            }
        }

Au cas où cela aiderait quelqu'un d'autre!

19
Josh

Il n'y a pas de vidage dans csvWriter, le vidage est dans streamWriter. Lorsque appelé

csvWriter.Dispose ();

cela videra le ruisseau. Une autre approche consiste à définir

streamWriter.AutoFlush = true;

qui purgera automatiquement le flux à chaque fois.

9
TuomasK

Voici un exemple de travail:

void Main()
{
    var records = new List<dynamic>{
       new { Id = 1, Name = "one" },
       new { Id = 2, Name = "two" },
    };

    Console.WriteLine(records.ToCsv());
}

public static class Extensions {
    public static string ToCsv<T>(this IEnumerable<T> collection)
    {
        using (var memoryStream = new MemoryStream())
        {
            using (var streamWriter = new StreamWriter(memoryStream))
            using (var csvWriter = new CsvWriter(streamWriter))
            {
                csvWriter.WriteRecords(collection);
            } // StreamWriter gets flushed here.

            return Encoding.ASCII.GetString(memoryStream.ToArray());
        }
    }
}

Basé sur cette réponse.

6
Amit