【问题标题】:How To Change DataType of a DataColumn in a DataTable?如何更改数据表中数据列的数据类型?
【发布时间】:2012-02-20 03:09:16
【问题描述】:

我有:

DataTable Table = new DataTable;
SqlConnection = new System.Data.SqlClient.SqlConnection("Data Source=" + ServerName + ";Initial Catalog=" + DatabaseName + ";Integrated Security=SSPI; Connect Timeout=120");

SqlDataAdapter adapter = new SqlDataAdapter("Select * from " + TableName, Connection);
adapter.FillSchema(Table, SchemaType.Source);
adapter.Fill(Table);

DataColumn column = DataTable.Columns[0];

我想做的是:

假设当前 column.DataType.Name“Double”。我希望它变成 "Int32"

我如何做到这一点?

【问题讨论】:

    标签: c# types datatable datacolumn


    【解决方案1】:

    Puedes agregar una columna con tipo de dato distinto , luego copiar los datos y eliminar la columna anterior

    TB.Columns.Add("columna1", GetType(Integer))    
    TB.Select("id=id").ToList().ForEach(Sub(row) row("columna1") = row("columna2"))    
    TB.Columns.Remove("columna2")
    

    【讨论】:

    • 您应该在回答中添加一些上下文,仅代码回答没有价值,因为它们被视为低质量。
    • 我明白了。它是否有效是另一回事。
    【解决方案2】:

    如果您只想更改一个列。例如从字符串更改为 int32,您可以使用表达式属性:

    DataColumn col = new DataColumn("col_int" , typeof(int));
    table.Columns.Add(col);
    col.Expression = "table_exist_col_string"; // digit string convert to int  
    

    【讨论】:

    • 好答案!无需 foreach(...) 并检查空值。
    【解决方案3】:

    我结合了 Mark 解决方案的效率 - 所以我不必.Clone 整个 DataTable - 与泛型和可扩展性,所以 我可以定义自己的转换函数。这就是我最终得到的结果:

    /// <summary>
    ///     Converts a column in a DataTable to another type using a user-defined converter function.
    /// </summary>
    /// <param name="dt">The source table.</param>
    /// <param name="columnName">The name of the column to convert.</param>
    /// <param name="valueConverter">Converter function that converts existing values to the new type.</param>
    /// <typeparam name="TTargetType">The target column type.</typeparam>
    public static void ConvertColumnTypeTo<TTargetType>(this DataTable dt, string columnName, Func<object, TTargetType> valueConverter)
    {
        var newType = typeof(TTargetType);
    
        DataColumn dc = new DataColumn(columnName + "_new", newType);
    
        // Add the new column which has the new type, and move it to the ordinal of the old column
        int ordinal = dt.Columns[columnName].Ordinal;
        dt.Columns.Add(dc);
        dc.SetOrdinal(ordinal);
    
        // Get and convert the values of the old column, and insert them into the new
        foreach (DataRow dr in dt.Rows)
        {
            dr[dc.ColumnName] = valueConverter(dr[columnName]);
        }
    
        // Remove the old column
        dt.Columns.Remove(columnName);
    
        // Give the new column the old column's name
        dc.ColumnName = columnName;
    }
    

    这样,使用起来更简单,同时也可以自定义:

    DataTable someDt = CreateSomeDataTable();
    // Assume ColumnName is an int column which we want to convert to a string one.
    someDt.ConvertColumnTypeTo<string>('ColumnName', raw => raw.ToString());
    

    【讨论】:

      【解决方案4】:

      旧帖子,但我想我会权衡一下,使用可以一次将单个列转换为给定类型的 DataTable 扩展:

      public static class DataTableExt
      {
          public static void ConvertColumnType(this DataTable dt, string columnName, Type newType)
          {
              using (DataColumn dc = new DataColumn(columnName + "_new", newType))
              {
                  // Add the new column which has the new type, and move it to the ordinal of the old column
                  int ordinal = dt.Columns[columnName].Ordinal;
                  dt.Columns.Add(dc);
                  dc.SetOrdinal(ordinal);
      
                  // Get and convert the values of the old column, and insert them into the new
                  foreach (DataRow dr in dt.Rows)
                      dr[dc.ColumnName] = Convert.ChangeType(dr[columnName], newType);
      
                  // Remove the old column
                  dt.Columns.Remove(columnName);
      
                  // Give the new column the old column's name
                  dc.ColumnName = columnName;
              }
          }
      }
      

      然后可以这样调用:

      MyTable.ConvertColumnType("MyColumnName", typeof(int));
      

      当然可以使用你想要的任何类型,只要列中的每个值都可以实际转换为新类型。

      【讨论】:

      • 给出错误“对象必须实现 IConvertible。”同时将 Byte[] 转换为字符串类型列。
      • 只需添加一些泛型即可public static void ConvertColumnType&lt;T&gt;(this DataTable dt, string columnName, TnewType) where T : Type, IConvertible
      • 我认为这是最优雅的解决方案,只是避免转换 DBNulls,例如dr[dc.ColumnName] = dr[columnName] == DBNull.Value ? DBNull.Value : Convert.ChangeType(dr[columnName], newType);
      【解决方案5】:
      DataTable DT = ...
      // Rename column to OLD:
      DT.Columns["ID"].ColumnName = "ID_OLD";
      // Add column with new type:
      DT.Columns.Add( "ID", typeof(int) );
      // copy data from old column to new column with new type:
      foreach( DataRow DR in DT.Rows )
      { DR["ID"] = Convert.ToInt32( DR["ID_OLD"] ); }
      // remove "OLD" column
      DT.Columns.Remove( "ID_OLD" );
      

      【讨论】:

        【解决方案6】:

        我创建了一个扩展函数,它允许更改 DataTable 的列类型。它不是克隆整个表并导入所有数据,而是克隆列,解析值,然后删除原始数据。

            /// <summary>
            /// Changes the datatype of a column. More specifically it creates a new one and transfers the data to it
            /// </summary>
            /// <param name="column">The source column</param>
            /// <param name="type">The target type</param>
            /// <param name="parser">A lambda function for converting the value</param>
            public static void ChangeType(this DataColumn column, Type type, Func<object, object> parser)
            {
                //no table? just switch the type
                if (column.Table == null)
                {
                    column.DataType = type;
                    return;
                }
        
                //clone our table
                DataTable clonedtable = column.Table.Clone();
        
                //get our cloned column
                DataColumn clonedcolumn = clonedtable.Columns[column.ColumnName];
        
                //remove from our cloned table
                clonedtable.Columns.Remove(clonedcolumn);
        
                //change the data type
                clonedcolumn.DataType = type;
        
                //change our name
                clonedcolumn.ColumnName = Guid.NewGuid().ToString();
        
                //add our cloned column
                column.Table.Columns.Add(clonedcolumn);
        
                //interpret our rows
                foreach (DataRow drRow in column.Table.Rows)
                {
                    drRow[clonedcolumn] = parser(drRow[column]);
                }
        
                //remove our original column
                column.Table.Columns.Remove(column);
        
                //change our name
                clonedcolumn.ColumnName = column.ColumnName;
            }
        }
        

        你可以这样使用它:

        List<DataColumn> lsColumns = dtData.Columns
            .Cast<DataColumn>()
            .Where(i => i.DataType == typeof(decimal))
            .ToList()
        
        //loop through each of our decimal columns
        foreach(DataColumn column in lsColumns)
        {
            //change to double
            column.ChangeType(typeof(double),(value) =>
            {
                double output = 0;
                double.TryParse(value.ToString(), out output);
                return output;  
            });
        }
        

        以上代码将所有小数列改为双精度。

        【讨论】:

          【解决方案7】:

          我采取了一些不同的方法。 我需要从 OA 日期格式的 excel 导入中解析日期时间。这种方法很简单,可以从……本质上构建,

          1. 添加所需类型的列
          2. 翻阅转换值的行
          3. 删除原始列并重命名为新列以匹配旧列

            private void ChangeColumnType(System.Data.DataTable dt, string p, Type type){
                    dt.Columns.Add(p + "_new", type);
                    foreach (System.Data.DataRow dr in dt.Rows)
                    {   // Will need switch Case for others if Date is not the only one.
                        dr[p + "_new"] =DateTime.FromOADate(double.Parse(dr[p].ToString())); // dr[p].ToString();
                    }
                    dt.Columns.Remove(p);
                    dt.Columns[p + "_new"].ColumnName = p;
                }
            

          【讨论】:

          • 谢谢!正是我想要的。
          【解决方案8】:
          Dim tblReady1 As DataTable = tblReady.Clone()
          
          '' convert all the columns type to String 
          For Each col As DataColumn In tblReady1.Columns
            col.DataType = GetType(String)
          Next
          
          tblReady1.Load(tblReady.CreateDataReader)
          

          【讨论】:

            【解决方案9】:

            考虑同时改变返回类型:

            select cast(columnName as int) columnName from table
            

            【讨论】:

              【解决方案10】:

              一旦DataTable 被填满,您就无法更改列的类型。

              在这种情况下,您最好的选择是在填充之前将Int32 列添加到DataTable

              dataTable = new DataTable("Contact");
              dataColumn = new DataColumn("Id");
              dataColumn.DataType = typeof(Int32);
              dataTable.Columns.Add(dataColumn);
              

              然后您可以将原始表中的数据克隆到新表中:

              DataTable dataTableClone = dataTable.Clone();
              

              这是post with more details

              【讨论】:

                【解决方案11】:

                虽然在DataTable 被填满后,您确实无法更改列的类型,但您可以在调用FillSchema 之后,但在调用Fill 之前更改它。例如,假设第 3 列是您想要从 double 转换为 Int32 的那一列,您可以使用:

                adapter.FillSchema(table, SchemaType.Source);
                table.Columns[2].DataType = typeof (Int32);
                adapter.Fill(table);
                

                【讨论】:

                • 请注意,如果您的适配器的命令是存储过程,这似乎不起作用
                • 我已经在Oracle.MangedDataAccess.Client.OracleDataAdapter 上使用存储过程对此进行了测试。它正在工作。谢谢。
                【解决方案12】:

                Datatable 填满数据后,您无法更改 DataType。但是,您可以克隆数据表,更改列类型并将数据从以前的数据表加载到克隆的表中,如下所示。

                DataTable dtCloned = dt.Clone();
                dtCloned.Columns[0].DataType = typeof(Int32);
                foreach (DataRow row in dt.Rows) 
                {
                    dtCloned.ImportRow(row);
                }
                

                【讨论】:

                • 喜欢这个解决方案 - 优雅!但是 ImportRow() 似乎没有为我将字符串值转换为浮点值。我在这里错过了什么吗?
                • DataTable.ImportRow()(或者更确切地说是它的底层存储)确实使用 IConvertible 接口转换值。确保相应地设置属性 DataTable.Locale! (默认为 CultureInfo.CurrentCulture)
                • 这不起作用。我正在尝试创建一个 memorystream 列而不是 db 来字节数组列。它给出 System.ArgumentException: Type of value has a mismatch with column typeCouldn't store in DATA Column。预期类型是 MemoryStream
                • 这不起作用。我正在检索带有 'timestamp' 列的记录,这些列最终为 byte[] 类型。这反过来又无法在 DataGridView 组件中正确显示,该组件试图将 byte[] 类型的列呈现为图像。我有一种将 byte[] 转换为字符串的方法,但它肯定不会在 ImportRow 中自动发生。它最终将值转换为字符串文字“System.Byte[]”。
                猜你喜欢
                • 2014-03-05
                • 2014-01-20
                • 1970-01-01
                • 1970-01-01
                • 2013-09-12
                • 2023-03-06
                • 2019-04-29
                • 2011-11-02
                相关资源
                最近更新 更多