0

我正在尝试将一个非常复杂的模型(就层次结构而言)导出到 CSV 文件。我正在尝试各种方法,但它们都缺少一些东西或需要大量我想避免的编码。

结果,我想要的是值的 CSV 提取,CSV 标DisplayName头来自我在 EF 模型本身上使用的属性注释。

我尝试过 CSVHelper,但自动映射器功能有点混乱,无法再忽略具有属性的某些字段;此外它不支持从 EF 命名DisplayAttribute

我还尝试使用 AutoMapper 将模型映射到单独的 CSV 模型,然后使用 CsvHelper 转储该模型,但我最终还是编写了大量代码来设置列名。

你知道任何快速的解决方案来获得我需要的东西吗?我可能会进入 CsvHelper 源代码并重写采用属性名称的部分,但我不知道代码库会有多复杂。有人有什么主意吗?

任何见解都受到高度赞赏。

4

2 回答 2

2

属性已被删除,因为它们的功能非常有限。映射结构允许更多的可能性。

您应该能够直接映射到您的 EF 模型。这是映射更好的另一个原因。与属性不同,它允许您映射到无法将源更改为的类。

如果该类与您的 CSV 文件匹配,则您无需执行任何操作。

void Main()
{
    using( var stream = new MemoryStream() )
    using( var reader = new StreamReader( stream ) )
    using( var writer = new StreamWriter( stream ) )
    using( var csv = new CsvWriter( writer ) )
    {
        var people = new List<Person>
        {
            new Person
            {
                Name = "Joe User",
                Address = new Address
                {
                    Street = "123 4th Street",
                },
            },
        };

        csv.WriteRecords( people );
        writer.Flush();
        stream.Position = 0;

        reader.ReadToEnd().Dump();
    }
}

public class Person
{
    public string Name { get; set; }

    public Address Address { get; set; }
}

public class Address
{
    public string Street { get; set; }
}

输出:

Name,Street
Joe User,123 4th Street

如果要进行更改,可以创建一个映射文件来解释这些更改。你仍然可以AutoMap并且只是改变你想要的东西。

void Main()
{
    using( var stream = new MemoryStream() )
    using( var reader = new StreamReader( stream ) )
    using( var writer = new StreamWriter( stream ) )
    using( var csv = new CsvWriter( writer ) )
    {
        var people = new List<Person>
        {
            new Person
            {
                Name = "Joe User",
                Address = new Address
                {
                    Street = "123 4th Street",
                },
            },
        };

        csv.Configuration.RegisterClassMap<PersonMap>();
        csv.WriteRecords( people );
        writer.Flush();
        stream.Position = 0;

        reader.ReadToEnd().Dump();
    }
}

public class Person
{
    public string Name { get; set; }

    public Address Address { get; set; }
}

public class Address
{
    public string Street { get; set; }
}

public sealed class PersonMap : CsvClassMap<Person>
{
    public PersonMap()
    {
        AutoMap();
        Map( m => m.Name ).Name( "Full Name" );
    }
}

输出:

Full Name,Street
Joe User,123 4th Street

如果需要,您可以管理参考并完全控制。

老实说,我不认为属性会重新出现。虽然有些人真的想使用它们。我一直在考虑的一个选项是在其中一些我认为不应该成为主库一部分的其他东西上添加一个扩展,对于那些想要使用它的人来说仍然存在。或者更好的是,其他人应该启动 CsvHelperContrib 项目并包含类似的内容。Attribute映射,并DataAnnotations可能成为其中的一部分。

于 2015-04-01T15:18:12.943 回答
0

最后,我找到了 CsvHelper 源代码并在其中添加了一些代码。首先我为 Ignoring 创建了两个属性并指定了一个名称(我不知道为什么作者决定删除 v2 中的属性):

[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public class CsvIgnoreAttribute : Attribute
{
}

[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public class CsvHeaderAttribute : Attribute
{
    /// <summary>
    /// The name to use for the CSV column header
    /// </summary>
    public string Name { get; set; }
}

然后我修改了CsvPropertyMap构造函数以考虑属性的属性,从特定CsvHeader属性开始,然后回退到DisplayAttribute属性名称。潜在地,它可以扩展到考虑DisplayNameAttribute并最终使用像 Humanizer 这样的东西作为后备。

这是修改后的构造函数;注意CsvIgnore如果存在的话,它实际上是如何从基础属性中获取的。这有助于继承。

public CsvPropertyMap( PropertyInfo property )
{
    data = new CsvPropertyMapData( property )
    {
        // Set some defaults.
        TypeConverter = TypeConverterFactory.GetConverter( property.PropertyType )
    };

    var displayAttributes = property.GetCustomAttributes(typeof(CsvHeaderAttribute), inherit: false);
    if (displayAttributes.Any()) {
        var displayName = ((CsvHeaderAttribute)displayAttributes.First()).Name;
        data.Names.Add(displayName);
    }
    else {
        displayAttributes = property.GetCustomAttributes(typeof (DisplayAttribute), inherit: false);
        if (displayAttributes.Any()) {
            var displayName = ((DisplayAttribute) displayAttributes.First()).Name;
            data.Names.Add(displayName);
        }
        else {
            data.Names.Add(property.Name);
        }
    }

    var shouldIgnore = Attribute.GetCustomAttribute(property, typeof (CsvIgnoreAttribute), inherit: true);
    if (shouldIgnore != null)
        data.Ignore = true;
}
于 2015-04-01T11:17:06.817 回答