我创建了Gisway's/Yuri Galanter's 解决方案的改进版本,它解决了以下几点:
-
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