0

我正在尝试创建一个实用程序类,我可以在其中传递匿名类型 (AT) 的列表,它会生成一个 CSV 文件,其中 AT 的属性作为其列,属性值作为其各自的数据。

我有一个工作代码,但我觉得它可以改进(很多!)。我继承了一个类FileResult并用我的自定义实现来装饰它。这是我到目前为止所拥有的:

public class ExportCSVAnonymous : FileResult {
    public dynamic List {
        set;
        get;
    }

    public char Separator {
        set;
        get;
    }
    public ExportCSVAnonymous(dynamic list, string fileDownloadName, char separator = ',') : base("text/csv") {
        List             = list;
        Separator        = separator;
        FileDownloadName = fileDownloadName;
    }
        public ExportCSVAnonymous(dynamic list, string fileDownloadName, char separator = ',') : base("text/csv") {
        List             = list;
        Separator        = separator;
        FileDownloadName = fileDownloadName;
    }

    protected override void WriteFile(HttpResponseBase response) {
        var outputStream = response.OutputStream;
        using (var memoryStream = new MemoryStream()) {
            WriteList(memoryStream);
            outputStream.Write(memoryStream.GetBuffer(), 0, (int)memoryStream.Length);
        }
    }

    private void WriteList(Stream stream) {
        var streamWriter = new StreamWriter(stream, Encoding.Default);

        WriteHeaderLine(streamWriter);
        streamWriter.WriteLine();
        WriteDataLines(streamWriter);

        streamWriter.Flush();
    }

    //I wish this part could be improved
    private void WriteHeaderLine(StreamWriter streamWriter) {
        foreach (var line in List) {
            foreach (MemberInfo member in line.GetType().GetProperties()) {
                WriteValue(streamWriter, member.Name);
            }
            break;
        }
    }

    private void WriteValue(StreamWriter writer, String value) {
        writer.Write("\"");
        writer.Write(value.Replace("\"", "\"\""));
        writer.Write("\"" + Separator);
    }

    private void WriteDataLines(StreamWriter streamWriter) {
        foreach (var line in List) {
            foreach (MemberInfo member in line.GetType().GetProperties()) {
                WriteValue(streamWriter, GetPropertyValue(line, member.Name));
            }
            streamWriter.WriteLine();
        }
    }

    private static string GetPropertyValue(object src, string propName) {
        object obj = src.GetType().GetProperty(propName).GetValue(src, null);
        return (obj != null) ? obj.ToString() : "";
    }
}

我用来dynamic作为一种在课堂上通过我的 AT 的方法。有没有更好的方法来做到这一点?最后,我想改进WriteHeaderLine方法。由于我使用的是dynamic类型,因此我无法成功转换它来检查 AT 的属性。最好的方法是什么?

4

1 回答 1

0

在某种程度上,我觉得有点矫枉过正,为了编写 CSV,使用匿名类型(或任何类型。真的)纯粹传递信息,这样你也可以将标题作为命名属性传递。我明白了,但问问自己代码到底在做什么/你在解决什么问题?

您想将 N 个字符串写入文件。

差不多就是这样。所以你写了一些简单的方法:

void WriteCsvLine(path, string[] cells){
  File.AppendAllText(path, string.Join(",", cells) + Environment.NewLine);
}

你这样称呼它:

someContext.Employees.Select(e =>
  new [] { e.Name, e.Dept, e.Salary.ToString() }
).ToList().ForEach(x => WriteCsvLine("c:\\...", x) ;

啊,但是我们不想每次都传递路径..所以你将它升级为一个类,将路径作为构造函数arg..

啊,但我们需要转义逗号.. 所以你升级它来引用字段

啊,但我们需要提供一些变量分隔符.. 所以你升级它有另一个构造函数 arg

啊,但我们可以优化一次写多行..所以你升级它来取一个List<string[]>或什么

啊,但是我们需要写一个标题行..所以你只需将你传递的第一个字符串数组作为标题(你可以将你的数据 LINQ Concat 到 anew[]{"Name","Dept","Salary"}或使其成为构造函数参数..)

所以我们需要一些我们可能会像这样使用的东西:

 var x = new CsvWriter("c:\\...", ',', new[]{"EmployeeName","DepartmentName","Salary"});
x.WriteEnumerable(someContext.Employees.Select(e => new [] { 
  e.Name, 
  e.Dept, 
  e.Salary.ToString() 
}));

但这不是很酷 - 当然我们可以做得更酷。所以你决定你将传递一个KeyValuePair<string, string>[] (或 arecord或 a ValueTuple,其中键是标题,值是数据。你的调用代码会因为你而变大每次都用数据重新指定标题名称..

啊,但是所有这些字符串仍然不是很酷..所以你决定你将传递一个匿名类型,其中属性名称是标题,属性值是数据..

你的代码有一些更少的"字符,但现在接收端已成为将属性名称解压缩为字符串的痛苦噩梦,因此它们可以写为标题行..(我什至不知道你是否可以轻松控制顺序列的更多)


最后,问题很简单:“找到一种方法来传递列的标题应该是什么”,或者换句话说“将字符串传递给方法”

..不知何故,我们从:

void Print(string what){
  Console.WriteLine(what);
}

...

Print("Hello World");

类似于:

using System.Reflection; 

static void Print<T>(T what)
{
    PropertyInfo[] propertyInfos = what.GetType().GetProperties();
    Console.WriteLine(propertyInfos[0].Name.Replace("_", " "));
}

...

Print(new { Hello_World = 0 });

它会起作用,但是当您考虑它时,这是一种“将字符串传递给方法”的相当疯狂的方式。

..现在老板希望标题有百分比符号,所以我要研究如何将它们放入属性名称并添加另一个 bool 标志,这样我们有时可以跳过编写标题..

于 2021-09-02T09:05:15.717 回答