Bagaimana Cara Mengubah DataType dari DataColumn di DataTable?

120

Saya sudah:

DataTable Table = new DataTable;
SqlConnection = new System.Data.SqlClient.SqlConnection("Data Source=" + ServerName + ";Initial Catalog=" + DatabaseName + ";Integrated Security=SSPI; Connect Timeout=120");

SqlDataAdapter adapter = new SqlDataAdapter("Select * from " + TableName, Connection);
adapter.FillSchema(Table, SchemaType.Source);
adapter.Fill(Table);

DataColumn column = DataTable.Columns[0];

Yang ingin saya lakukan adalah:

Asumsikan saat ini kolom.DataType.Name adalah "Double" . Saya ingin ini menjadi "Int32" .

Bagaimana saya mencapai ini?

rofans91
sumber

Jawaban:

269

Anda tidak dapat mengubah DataType setelah Datatable diisi dengan data. Namun, Anda dapat mengkloning tabel Data, mengubah jenis kolom dan memuat data dari tabel data sebelumnya ke tabel kloning seperti yang ditunjukkan di bawah ini.

DataTable dtCloned = dt.Clone();
dtCloned.Columns[0].DataType = typeof(Int32);
foreach (DataRow row in dt.Rows) 
{
    dtCloned.ImportRow(row);
}
Akhil
sumber
Suka solusinya - Elegan! Namun ImportRow () tampaknya tidak mengubah nilai string menjadi nilai float bagi saya. Apakah saya melewatkan sesuatu di sini?
yangli.liy
1
DataTable.ImportRow () (atau lebih tepatnya penyimpanan yang mendasarinya) tidak mengonversi nilai dengan menggunakan antarmuka IConvertible. Pastikan untuk menyetel properti DataTable.Locale sesuai! (default adalah CultureInfo.CurrentCulture)
Sir Kill A Lot
Ini tidak bekerja. Saya mencoba untuk membuat kolom memorystream daripada kolom array byte db datang. Ini memberi System.ArgumentException: Jenis nilai memiliki ketidakcocokan dengan tipe kolomTidak bisa menyimpan <System.Byte []> di DATA Column. Jenis yang diharapkan adalah MemoryStream
Mert Serimer
28

Meskipun benar bahwa Anda tidak bisa mengubah tipe kolom setelah DataTablediisi, Anda bisa mengubahnya setelah Anda menelepon FillSchema, tapi sebelum Anda menelepon Fill. Misalnya, kolom ke-3 adalah kolom yang ingin Anda ubah doublemenjadi Int32, Anda dapat menggunakan:

adapter.FillSchema(table, SchemaType.Source);
table.Columns[2].DataType = typeof (Int32);
adapter.Fill(table);
rsbarro.dll
sumber
1
Perhatikan bahwa ini tampaknya tidak berfungsi jika perintah adaptor Anda adalah prosedur yang tersimpan
Mark Sowul
1
Saya telah menguji ini Oracle.MangedDataAccess.Client.OracleDataAdapterdengan prosedur tersimpan. Bekerja. Terima kasih.
3per
21

Posting lama, tapi saya pikir saya akan mempertimbangkannya, dengan ekstensi DataTable yang dapat mengonversi satu kolom pada satu waktu, ke jenis tertentu:

public static class DataTableExt
{
    public static void ConvertColumnType(this DataTable dt, string columnName, Type newType)
    {
        using (DataColumn dc = new DataColumn(columnName + "_new", newType))
        {
            // Add the new column which has the new type, and move it to the ordinal of the old column
            int ordinal = dt.Columns[columnName].Ordinal;
            dt.Columns.Add(dc);
            dc.SetOrdinal(ordinal);

            // Get and convert the values of the old column, and insert them into the new
            foreach (DataRow dr in dt.Rows)
                dr[dc.ColumnName] = Convert.ChangeType(dr[columnName], newType);

            // Remove the old column
            dt.Columns.Remove(columnName);

            // Give the new column the old column's name
            dc.ColumnName = columnName;
        }
    }
}

Kemudian bisa disebut seperti ini:

MyTable.ConvertColumnType("MyColumnName", typeof(int));

Tentu saja menggunakan tipe apa pun yang Anda inginkan, asalkan setiap nilai di kolom benar-benar dapat dikonversi ke tipe baru.

Marc Ferrold
sumber
Memberikan error "Objek harus menerapkan IConvertible." saat mengubah Byte [] menjadi kolom tipe string.
Harshad Vekariya
Cukup tambahkan beberapa obat generik ke dalamnya public static void ConvertColumnType<T>(this DataTable dt, string columnName, TnewType) where T : Type, IConvertible
TM
3
Saya pikir ini adalah solusi yang paling elegan, hindari mengonversi DBNulls, misalnya dr[dc.ColumnName] = dr[columnName] == DBNull.Value ? DBNull.Value : Convert.ChangeType(dr[columnName], newType);
miboper
8

Pertimbangkan juga untuk mengubah tipe pengembalian:

select cast(columnName as int) columnName from table
tsilb
sumber
8
Dim tblReady1 As DataTable = tblReady.Clone()

'' convert all the columns type to String 
For Each col As DataColumn In tblReady1.Columns
  col.DataType = GetType(String)
Next

tblReady1.Load(tblReady.CreateDataReader)
Superna Parajuli
sumber
8

Saya telah mengambil sedikit pendekatan yang berbeda. Saya perlu mengurai datetime dari impor excel yang ada dalam format tanggal OA. Metodologi ini cukup sederhana untuk dibangun dari ... pada dasarnya,

  1. Tambahkan kolom jenis yang Anda inginkan
  2. Merobek baris untuk mengubah nilai
  3. Hapus kolom asli dan ganti namanya ke yang baru agar sesuai dengan yang lama

    private void ChangeColumnType(System.Data.DataTable dt, string p, Type type){
            dt.Columns.Add(p + "_new", type);
            foreach (System.Data.DataRow dr in dt.Rows)
            {   // Will need switch Case for others if Date is not the only one.
                dr[p + "_new"] =DateTime.FromOADate(double.Parse(dr[p].ToString())); // dr[p].ToString();
            }
            dt.Columns.Remove(p);
            dt.Columns[p + "_new"].ColumnName = p;
        }
John Salewski
sumber
Terima kasih! Persis apa yang saya cari.
Rahul Singh
4

Setelah a DataTablediisi, Anda tidak dapat mengubah tipe kolom.

Pilihan terbaik Anda dalam skenario ini adalah menambahkan Int32kolom ke DataTablesebelum mengisinya:

dataTable = new DataTable("Contact");
dataColumn = new DataColumn("Id");
dataColumn.DataType = typeof(Int32);
dataTable.Columns.Add(dataColumn);

Kemudian Anda dapat mengkloning data dari tabel asli Anda ke tabel baru:

DataTable dataTableClone = dataTable.Clone();

Berikut postingan dengan detail lebih lanjut .

Josh Earl
sumber
4

jika Anda ingin mengubah hanya kolom. misalnya dari string ke int32 Anda dapat menggunakan properti Expression:

DataColumn col = new DataColumn("col_int" , typeof(int));
table.Columns.Add(col);
col.Expression = "table_exist_col_string"; // digit string convert to int  
mehdi
sumber
jawaban yang bagus! tidak perlu meneruskan (...) dan memeriksa nilai null.
Behzad Ebrahimi
2

Saya membuat fungsi ekstensi yang memungkinkan mengubah jenis kolom DataTable. Alih-alih mengkloning seluruh tabel dan mengimpor semua data, itu hanya mengkloning kolom, mengurai nilai dan kemudian menghapus aslinya.

    /// <summary>
    /// Changes the datatype of a column. More specifically it creates a new one and transfers the data to it
    /// </summary>
    /// <param name="column">The source column</param>
    /// <param name="type">The target type</param>
    /// <param name="parser">A lambda function for converting the value</param>
    public static void ChangeType(this DataColumn column, Type type, Func<object, object> parser)
    {
        //no table? just switch the type
        if (column.Table == null)
        {
            column.DataType = type;
            return;
        }

        //clone our table
        DataTable clonedtable = column.Table.Clone();

        //get our cloned column
        DataColumn clonedcolumn = clonedtable.Columns[column.ColumnName];

        //remove from our cloned table
        clonedtable.Columns.Remove(clonedcolumn);

        //change the data type
        clonedcolumn.DataType = type;

        //change our name
        clonedcolumn.ColumnName = Guid.NewGuid().ToString();

        //add our cloned column
        column.Table.Columns.Add(clonedcolumn);

        //interpret our rows
        foreach (DataRow drRow in column.Table.Rows)
        {
            drRow[clonedcolumn] = parser(drRow[column]);
        }

        //remove our original column
        column.Table.Columns.Remove(column);

        //change our name
        clonedcolumn.ColumnName = column.ColumnName;
    }
}

Anda dapat menggunakannya seperti ini:

List<DataColumn> lsColumns = dtData.Columns
    .Cast<DataColumn>()
    .Where(i => i.DataType == typeof(decimal))
    .ToList()

//loop through each of our decimal columns
foreach(DataColumn column in lsColumns)
{
    //change to double
    column.ChangeType(typeof(double),(value) =>
    {
        double output = 0;
        double.TryParse(value.ToString(), out output);
        return output;  
    });
}

Kode di atas mengubah semua kolom desimal menjadi dua kali lipat.

Wes Hanney
sumber
1
DataTable DT = ...
// Rename column to OLD:
DT.Columns["ID"].ColumnName = "ID_OLD";
// Add column with new type:
DT.Columns.Add( "ID", typeof(int) );
// copy data from old column to new column with new type:
foreach( DataRow DR in DT.Rows )
{ DR["ID"] = Convert.ToInt32( DR["ID_OLD"] ); }
// remove "OLD" column
DT.Columns.Remove( "ID_OLD" );
Altivo
sumber
1

Saya menggabungkan efisiensi solusi Mark - jadi saya tidak harus ke .Cloneseluruh DataTable - dengan generik dan ekstensibilitas, jadi saya dapat menentukan fungsi konversi saya sendiri . Inilah yang akhirnya saya dapatkan:

/// <summary>
///     Converts a column in a DataTable to another type using a user-defined converter function.
/// </summary>
/// <param name="dt">The source table.</param>
/// <param name="columnName">The name of the column to convert.</param>
/// <param name="valueConverter">Converter function that converts existing values to the new type.</param>
/// <typeparam name="TTargetType">The target column type.</typeparam>
public static void ConvertColumnTypeTo<TTargetType>(this DataTable dt, string columnName, Func<object, TTargetType> valueConverter)
{
    var newType = typeof(TTargetType);

    DataColumn dc = new DataColumn(columnName + "_new", newType);

    // Add the new column which has the new type, and move it to the ordinal of the old column
    int ordinal = dt.Columns[columnName].Ordinal;
    dt.Columns.Add(dc);
    dc.SetOrdinal(ordinal);

    // Get and convert the values of the old column, and insert them into the new
    foreach (DataRow dr in dt.Rows)
    {
        dr[dc.ColumnName] = valueConverter(dr[columnName]);
    }

    // Remove the old column
    dt.Columns.Remove(columnName);

    // Give the new column the old column's name
    dc.ColumnName = columnName;
}

Dengan cara ini, penggunaan jauh lebih mudah, sekaligus dapat disesuaikan:

DataTable someDt = CreateSomeDataTable();
// Assume ColumnName is an int column which we want to convert to a string one.
someDt.ConvertColumnTypeTo<string>('ColumnName', raw => raw.ToString());
Marcell Toth
sumber
0

Puedes agregar una columna con tipo de dato distinto, luego copiar los datos y eliminar la columna anterior

TB.Columns.Add("columna1", GetType(Integer))    
TB.Select("id=id").ToList().ForEach(Sub(row) row("columna1") = row("columna2"))    
TB.Columns.Remove("columna2")
Jhonny Espinoza
sumber
1
Anda harus menambahkan beberapa konteks pada jawaban Anda, jawaban hanya kode tidak memiliki nilai karena dianggap berkualitas rendah.
Nawed Nabi Zada