【问题标题】:Convert select new to DataTable?将 select new 转换为 DataTable?
【发布时间】:2012-04-13 19:40:30
【问题描述】:

我正在使用 .NET 3.5,需要将以下选择的新结果转换为 DataTable。是否有为此内置的东西或任何人知道可以做到这一点的方法?

var contentList = (from item in this.GetData().Cast<IContent>()
                  select new
                  {
                      Title = item.GetMetaData("Title"),
                      Street = item.GetMetaData("Street"),
                      City = item.GetMetaData("City"),
                      Country = item.GetMetaData("Country")
                  });

【问题讨论】:

  • 你在做什么需要一个 DataTable 作为结果?
  • 我正在尝试导出为 CSV,并且已经有可以将 DataTable/Set 导出为 CSV 的方法。
  • 我认为您正在寻找一种(主要是)静态类型的动态编程语言。如果您非常依赖那些 DataTable 到 csv 导出例程,那么恐怕您的道路可能只有您一人踏上一步。匿名对象是一种相当新的语言特性,而 DataRow/DataTable/DataSet 在设计时并未考虑到它们。
  • 更多答案在这里:stackoverflow.com/q/15213919/1180926

标签: c# asp.net .net linq dataset


【解决方案1】:

简单直接的做法是使用反射:

var records = (from item in this.GetData().Cast<IContent>()
                           select new
                           {
                               Title = "1",
                               Street = "2",
                               City = "3",
                               Country = "4"
                           });
var firstRecord = records.First();
if (firstRecord == null)
    return;

var infos = firstRecord.GetType().GetProperties();
DataTable table = new DataTable();
foreach (var info in infos) {
    DataColumn column = new DataColumn(info.Name, info.PropertyType);
    table.Columns.Add(column);
}

foreach (var record in records) {
    DataRow row = table.NewRow();
    for (int i = 0; i < table.Columns.Count; i++)
        row[i] = infos[i].GetValue(record);
    table.Rows.Add(row);
}

代码可能无法预先运行,但应该可以让您大致了解一下。首先,您从匿名类型获取 propertyInfos 并使用此元数据创建数据表模式(填充列)。然后您使用这些信息从每个对象中获取值。

【讨论】:

  • 但是每次代码运行时反射都会影响性能。我宁愿在代码中手动指定属性。
  • 好吧,由于 OP 有 30 多个属性并且可能不止一个用例,手动指定它会适得其反。此外,还有一些技巧可以提高反射效率(例如只使用一次反射并生成动态映射方法,保存在内存中以供将来的对象转换使用或快速反射库)。
  • 看看我的通用解决方案,不使用反射,而是使用表达式树。从您的解决方案中借用了一些代码
【解决方案2】:

这是一种没有反射属性的通用解决方案。有如下扩展方法

    public static DataTable ConvertToDataTable<TSource>(this IEnumerable<TSource>
                     records, params Expression<Func<TSource, object>>[] columns)
    {
        var firstRecord = records.First();
        if (firstRecord == null)
            return null;

        DataTable table = new DataTable();

        List<Func<TSource, object>> functions = new List<Func<TSource, object>>();
        foreach (var col in columns)
        {
            DataColumn column = new DataColumn();
            column.Caption = (col.Body as MemberExpression).Member.Name;
            var function = col.Compile();
            column.DataType = function(firstRecord).GetType();
            functions.Add(function);
            table.Columns.Add(column);
        }

        foreach (var record in records)
        {
            DataRow row = table.NewRow();
            int i = 0;
            foreach (var function in functions)
            {
                row[i++] = function((record));
            }
            table.Rows.Add(row);
        }
        return table;
    }

并使用 where 参数调用相同的列名,按您想要的顺序。

var table = records.ConvertToDataTable(
                                        item => item.Title, 
                                        item => item.Street, 
                                        item => item.City
                                      );

【讨论】:

  • 我似乎永远不会改变的变量?
【解决方案3】:

您可以通过以下函数将列表结果转换为数据表

   public static DataTable ToDataTable<T>(IEnumerable<T> values)
    {
        DataTable table = new DataTable();

        foreach (T value in values)
        {
            if (table.Columns.Count == 0)
            {
                foreach (var p in value.GetType().GetProperties())
                {
                    table.Columns.Add(p.Name);
                }
            }

            DataRow dr = table.NewRow();
            foreach (var p in value.GetType().GetProperties())
            {
                dr[p.Name] = p.GetValue(value, null) + "";

            }
            table.Rows.Add(dr);
        }

        return table;
    }

【讨论】:

    【解决方案4】:

    有一个CopyToDataTable extension method 可以为您做到这一点。它位于 System.Data.DataSetExtensions.dll 中

    【讨论】:

    • 我希望我可以使用它,但我得到一个编译错误:错误 109 类型 'AnonymousType#1' 不能用作泛型类型或方法 'System. Data.DataTableExtensions.CopyToDataTable(System.Collections.Generic.IEnumerable)'。没有从“AnonymousType#1”到“System.Data.DataRow”的隐式引用转换。
    • 是的,这对于从匿名或其他方式的对象进行复制来说不是一个有用的方法,除非它们来自 DataRow。
    【解决方案5】:

    试试这个:

    // Create your datatable.
    
    DataTable dt = new DataTable();
    dt.Columns.Add("Title", typeof(string));
    dt.Columns.Add("Street", typeof(double));
    
    
    // get a list of object arrays corresponding
    // to the objects listed in the columns
    // in the datatable above.
    var result = from item in in this.GetData().Cast<IContent>()             
                 select dt.LoadDataRow(
                    new object[] { Title = item.GetMetaData("Title"),
                                  Street = item.GetMetaData("Street"),
                     },
                    false);
    
    
    // the end result will be a set of DataRow objects that have been
    // loaded into the DataTable. 
    

    代码示例原文:Converting Anonymous type generated by LINQ to a DataTable type

    编辑:通用伪代码:

    void LinqToDatatable(string[] columns, Type[] datatypes, linqSource)
    {
         for loop
         {
           dt.columns.add(columns[i], datatypes[i]);
         }
    
    //Still thinking how to make this generic.. 
    var result = from item in in this.GetData().Cast<IContent>()             
                 select dt.LoadDataRow(
                    new object[] { string[0] = item.GetMetaData[string[0]],
                                   string[1] = item.GetMetaData[srring[1]
                     },
                    false);
    
    
    }
    

    【讨论】:

    • 我实际上有超过 30 多个属性,并希望这是通用的。除了对列进行硬编码之外,还有其他方法吗?我还不如创建一个真正的类而不是使用匿名类。
    • 检查我的编辑。仍在思考如何使第二部分通用。
    【解决方案6】:
       public static DataTable ListToDataTable<T>(this IList<T> data)
            {
                DataTable dt = new DataTable();
                PropertyDescriptorCollection props = TypeDescriptor.GetProperties(typeof(T));
                for (int i = 0; i < props.Count; i++)
                {
                    PropertyDescriptor prop = props[i];
                    dt.Columns.Add(prop.Name, prop.PropertyType);
                }
                object[] values = new object[props.Count];
                foreach (T t in data)
                {
                    for (int i = 0; i < values.Length; i++)
                    {
                        values[i] = props[i].GetValue(t);
                    }
                    dt.Rows.Add(values);
                }
                return dt;
            }
    

    选择新后,您可以发送至.ToList().ListToDataTable()。这使用 ComponentModel 反射并且(理论上)比 System.Reflection 更快。

    【讨论】:

      猜你喜欢
      • 2019-06-02
      • 2013-02-20
      • 2016-05-14
      • 2014-07-22
      • 2017-01-20
      • 1970-01-01
      • 1970-01-01
      • 2011-01-17
      • 2015-11-19
      相关资源
      最近更新 更多