web-dev-qa-db-ja.com

設定されたDataTable列のデータ型の変更

各列のデータ型を文字列に設定するCSVファイルを読み取ることで生成されるSystem.Data.DataTableがあります。

DataTableの内容を既存のデータベーステーブルに追加したい-現在、これは、DataTableをソースとしてSqlBulkCopyを使用して行われています。

ただし、DataTableの列のデータ型は、ターゲットデータベーステーブルのスキーマと一致するように変更して、null値を処理する必要があります。

私はADO.NETにあまり詳しくないので、これを行うクリーンな方法を探していましたか?

ありがとう。

12
TonE

データを入力した後は、DataTypeDataColumnを変更できません。これは読み取り専用のプロパティではありませんが、すでにデータが入っている後で変更しようとすると、実行時に例外が発生します。

ドキュメント から:

列がデータの格納を開始した後でこのプロパティを変更すると、例外が生成されます。

したがって、最初に(可能な場合は)正しい列タイプを確認するか、インポート用に新しいDataTableを作成し、元のDataTableからデータをコピーする必要があります。

IDataReaderから読み取り、ジャストインタイム変換を実行し、それをDataTableに渡すカスタムSqlBulkCopyクラスを作成することもできます-より効率的です、しかしそれは明らかに迅速な修正ではありません。

10
Aaronaught

私は仕事をするためにこの一般的な関数を書きました、それは私にとって非常にうまくいきます:

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));
10
Gisway

埋めるデータテーブルのデータ型を設定してください。

例えば。:

    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
Carra

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");

うまくいくと思います。

1
Mayur

"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;
}
0
BenjaminR

私は Gisway 's/ Yuri Galanter のソリューションの改良版を作成しました。これは次の点に対処します。

  • 例外を食べない 、早く失敗する
  • 元の列のnullability/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列の場合のように、データのロード時に列のnull可能性が正しく構成されていない場合は、次のようなコードを挿入できます。

_table.Columns("TrueOrFalse").AllowDBNull = False 
table.Columns("TrueOrFalse").DefaultValue = 0
_
0
Georg Jung