【问题标题】:EntityFramework extract to CSV but keeping Data AnnotationsEntityFramework 提取到 CSV 但保留数据注释
【发布时间】:2015-03-31 10:13:47
【问题描述】:

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

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

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

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

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

高度赞赏任何见解。

【问题讨论】:

  • 执行 SQL 语句以获取平面 DataTable,然后使用您喜欢的 Csv 工具将该表转换为 CSV 文件。就编码和维护所需的时间而言,它可能是最快的方法。
  • 这可能是一个解决方案,但 SQL 本身会相当复杂......列名也会有同样的问题。
  • SELECT column1 AS [Some Pretty Column Name] FROM theTable WHERE ...
  • DisplayAttribute 可以与列别名一样查看。我想你可以使用反射来获取DisplayAttribute,但是获取属性并输出到 CSV 是悬而未决的。似乎所有工具都有一些难以解决的依赖关系
  • 看我的回答。最后,我最终修改了 CsvHelper 源。我仍然不明白为什么这家伙在新版本中删除了属性......无论如何,如果我有足够的时间,这可能会成为一个很好的拉取请求:)

标签: c# entity-framework export-to-csv csvhelper


【解决方案1】:

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

您应该能够直接映射到您的 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 可能是其中的一部分。

【讨论】:

    【解决方案2】:

    最后,我找到了 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;
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-08-08
      • 2018-07-21
      • 1970-01-01
      • 2021-12-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多