各列のデータ型を文字列に設定するCSVファイルを読み取ることで生成されるSystem.Data.DataTableがあります。
DataTableの内容を既存のデータベーステーブルに追加したい-現在、これは、DataTableをソースとしてSqlBulkCopyを使用して行われています。
ただし、DataTableの列のデータ型は、ターゲットデータベーステーブルのスキーマと一致するように変更して、null値を処理する必要があります。
私はADO.NETにあまり詳しくないので、これを行うクリーンな方法を探していましたか?
ありがとう。
データを入力した後は、DataType
のDataColumn
を変更できません。これは読み取り専用のプロパティではありませんが、すでにデータが入っている後で変更しようとすると、実行時に例外が発生します。
ドキュメント から:
列がデータの格納を開始した後でこのプロパティを変更すると、例外が生成されます。
したがって、最初に(可能な場合は)正しい列タイプを確認するか、インポート用に新しいDataTable
を作成し、元のDataTable
からデータをコピーする必要があります。
IDataReader
から読み取り、ジャストインタイム変換を実行し、それをDataTable
に渡すカスタムSqlBulkCopy
クラスを作成することもできます-より効率的です、しかしそれは明らかに迅速な修正ではありません。
私は仕事をするためにこの一般的な関数を書きました、それは私にとって非常にうまくいきます:
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)に配置するだけで、次のように使用できます。
MyClass.ChangeColumnDataType(table, "GEOST", typeof (int));
埋めるデータテーブルのデータ型を設定してください。
例えば。:
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);
Csvファイルからデータを入力する場合は、最初にデータテーブルでスキーマを読み取り、次に列のデータ型を変更してからテーブルに入力します。例:データのインポートに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");
うまくいくと思います。
"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;
}
私は Gisway 's/ 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
列の場合のように、データのロード時に列のnull可能性が正しく構成されていない場合は、次のようなコードを挿入できます。
_table.Columns("TrueOrFalse").AllowDBNull = False
table.Columns("TrueOrFalse").DefaultValue = 0
_