我有一个System.Data.DataTable,它通过读取CSV文件来填充,该文件将每列的数据类型设置为字符串。
我想将DataTable的内容追加到现有的数据库表中 - 目前这是使用SqlBulkCopy并以DataTable作为源来完成的。
但是,需要更改DataTable的列数据类型以匹配目标数据库表的模式,处理空值。
我对ADO.NET不太熟悉,所以一直在寻找一种干净的方法吗?
感谢。
答案 0 :(得分:10)
使用数据填充后,您无法更改DataType
的{{1}}。它不是只读属性,但如果您在已有数据后尝试更改它,则会在运行时收到异常。
在列开始存储数据后更改此属性时会生成异常。
因此,您必须在开始时确保正确的列类型(如果可能),或者专门为从原始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解决方案的改进版本,解决了以下几点:
AllowDBNull
由于我需要的项目在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