更改填充的DataTable列数据类型

时间:2010-03-29 14:09:51

标签: c# .net ado.net

我有一个System.Data.DataTable,它通过读取CSV文件来填充,该文件将每列的数据类型设置为字符串。

我想将DataTable的内容追加到现有的数据库表中 - 目前这是使用SqlBulkCopy并以DataTable作为源来完成的。

但是,需要更改DataTable的列数据类型以匹配目标数据库表的模式,处理空值。

我对ADO.NET不太熟悉,所以一直在寻找一种干净的方法吗?

感谢。

6 个答案:

答案 0 :(得分:10)

使用数据填充后,您无法更改DataType的{​​{1}}。它不是只读属性,但如果您在已有数据后尝试更改它,则会在运行时收到异常。

来自documentation

  

在列开始存储数据后更改此属性时会生成异常。

因此,您必须在开始时确保正确的列类型(如果可能),或者专门为从原始DataColumn导入和复制数据创建新的DataTable

您还可以编写一个自定义的DataTable课程,该课程从您的IDataReader读取,并执行即时转换并将其传递给DataTable - 这样会更有效率,但这显然不是一个快速修复。

答案 1 :(得分:9)

我写了这个通用函数来完成这项工作,它对我很有用:

public static bool ChangeColumnDataType(DataTable table, string columnname, Type newtype)
{
    if (table.Columns.Contains(columnname) == false)
        return false;

    DataColumn column= table.Columns[columnname];
    if (column.DataType == newtype)
        return true;

    try
    {
        DataColumn newcolumn = new DataColumn("temporary", newtype);
        table.Columns.Add(newcolumn);
        foreach (DataRow row in table.Rows)
        {
            try
            {
                row["temporary"] = Convert.ChangeType(row[columnname], newtype);
            }
            catch
            {
            }
        }
        table.Columns.Remove(columnname);
        newcolumn.ColumnName = columnname;
    }
    catch (Exception)
    {
        return false;
    }

    return true;
}

您可以复制代码并将其放在一个类(MyClass here)中,并以此为例使用它:

MyClass.ChangeColumnDataType(table, "GEOST", typeof (int));

答案 2 :(得分:3)

确保设置正在填充的数据表的数据类型。

E.g:

    DataTable table = new DataTable("countries");
    table.Columns.Add("country_code", typeof (string));
    table.Columns.Add("country_name", typeof (string));
    //...
    //Fill table

或者如果列类型兼容,您可以更改列类型:

table.Columns["country_code"].DataType = typeof(string);

答案 3 :(得分:1)

如果从csv文件填充,则先在datatable中读取schema,然后更改column的数据类型,然后填充表。 示例:我正在使用XML文件导入数据。

       DataSet dstemp = new DataSet();
       dstemp.ReadXmlSchema(@"D:\path of file\filename.xml");
       dstemp.Tables[0].Columns["Student_id"].DataType = typeof(Guid);
       dstemp.ReadXml(@"D:\path of file\filename.xml");

我认为它应该适合你。

答案 4 :(得分:0)

就像#Eddie Monge Jr"或" Gisway"无法得到它。

但使用正确的列顺序。

public static bool ChangeColumnDataType(DataTable table, string columnname, Type newtype){
    if (table.Columns.Contains(columnname) == false)
        return false;

    DataColumn column = table.Columns[columnname];
    if (column.DataType == newtype)
        return true;

    try{
        DataColumn newcolumn = new DataColumn("temporary", newtype);
        table.Columns.Add(newcolumn);

        foreach (DataRow row in table.Rows){
            try{
                row["temporary"] = Convert.ChangeType(row[columnname], newtype);
            }
            catch{}
        }
        newcolumn.SetOrdinal(column.Ordinal);
        table.Columns.Remove(columnname);
        newcolumn.ColumnName = columnname;
    }
    catch (Exception){
        return false;
    }

    return true;
}

答案 5 :(得分:0)

我创建了Gisway / Yuri Galanter解决方案的改进版本,解决了以下几点:

  • Don't eat exceptions,尽早失败
  • 保留原始列的可空性/ AllowDBNull
  • 直接使用列对象,无需将表对象作为参数
  • 将命名更改为典型的.Net约定
  • 改善文档
  • 在临时列名称中包含一个Guid,以真正避免冲突
  • 重构为扩展方法

由于我需要的项目在VB.Net中,所以我的解决方案也已在其中编写(并经过测试),对不起-转换并不难。

 
' following methods will be defined in a module, which is why they aren't Shared
' based on https://codecorner.galanter.net/2013/08/02/ado-net-datatable-change-column-datatype-after-table-is-populated-with-data/ 
' and https://stackoverflow.com/a/15692087/1200847 

''' <summary> 
''' Converts DataType of a DataTable's column to a new type by creating a copy of the column with the new type and removing the old one. 
''' </summary> 
''' <param name="table">DataTable containing the column</param> 
''' <param name="columnName">Name of the column</param> 
''' <param name="newType">New type of the column</param> 
<Extension()> 
Public Sub ChangeColumnDataType(table As DataTable, columnName As String, newType As Type) 
    If Not table.Columns.Contains(columnName) Then Throw New ArgumentException($"No column of the given table is named ""{columnName}"".") 
    Dim oldCol As DataColumn = table.Columns(columnName) 
    oldCol.ChangeDataType(newType) 
End Sub 

''' <summary> 
''' Converts DataType of a DataTable's column to a new type by creating a copy of the column with the new type and removing the old one. 
''' </summary> 
''' <param name="column">The column whichs type should be changed</param> 
''' <param name="newType">New type of the column</param> 
<Extension()> 
Public Sub ChangeDataType(column As DataColumn, newType As Type) 
    Dim table = column.Table 
    If column.DataType Is newType Then Return 

    Dim tempColName = "temporary-327b8efdb7984e4d82d514230b92a137" 
    Dim newCol As New DataColumn(tempColName, newType) 
    newCol.AllowDBNull = column.AllowDBNull 

    table.Columns.Add(newCol) 
    newCol.SetOrdinal(table.Columns.IndexOf(column)) 

    For Each row As DataRow In table.Rows 
        row(tempColName) = Convert.ChangeType(row(column), newType) 
    Next 
    table.Columns.Remove(column) 
    newCol.ColumnName = column.ColumnName 
End Sub

如果您有一个int列,而该列实际上应该是bool列,请像这样使用它:

 
table.Columns("TrueOrFalse").ChangeDataType(GetType(Boolean)) 

重要:由于这会更改DataTable,因此您可能希望在加载数据后立即执行此操作,然后接受更改。这样,更改跟踪,数据绑定等便可以正常工作:

 
table.AcceptChanges()

如果在加载数据时未正确配置列的不可为空性(例如我的Oracle NUMBER(1,0) NOT NULL列的情况),则可能要插入如下代码:

 
table.Columns("TrueOrFalse").AllowDBNull = False 
table.Columns("TrueOrFalse").DefaultValue = 0