Bir DataTable'daki DataColumn'un DataType'ı Nasıl Değiştirilir?


120

Sahibim:

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];

Yapmak istediğim şey:

Şu anda column.DataType.Name'in "Double" olduğunu varsayın . "Int32" olmasını istiyorum .

Bunu nasıl başarırım?

Yanıtlar:


269

Veri Tablosu verilerle doldurulduktan sonra Veri Tipini değiştiremezsiniz. Ancak, aşağıda gösterildiği gibi Veri tablosunu klonlayabilir, sütun türünü değiştirebilir ve önceki veri tablosundaki verileri klonlanmış tabloya yükleyebilirsiniz.

DataTable dtCloned = dt.Clone();
dtCloned.Columns[0].DataType = typeof(Int32);
foreach (DataRow row in dt.Rows) 
{
    dtCloned.ImportRow(row);
}

Çözümü sevin - Zarif! Ancak ImportRow () benim için dize değerlerini float değerlerine dönüştürmüyor gibi görünüyor. Burada bir şey mi kaçırıyorum?
yangli.liy

1
DataTable.ImportRow () (veya onun temelindeki depolama alanı), IConvertible arabirimini kullanarak değerleri dönüştürür. DataTable.Locale özelliğini buna göre ayarladığınızdan emin olun! (varsayılan, CultureInfo.CurrentCulture'dır)
Sir Kill A Lot

Bu çalışmıyor. Db gelen bayt dizisi sütunu yerine bir memorystream sütunu oluşturmaya çalışıyorum. System.ArgumentException verir: Değer türü sütun tipi ile uyuşmazlığa sahip <System.Byte []> VERİ Sütununda saklanamadı. Beklenen tür MemoryStream
Mert Serimer

28

Sütunun türünü DataTabledoldurduktan sonra değiştiremeyeceğiniz doğru olsa da, onu aradıktan sonra FillSchemaancak aramadan önce değiştirebilirsiniz Fill. Örneğin, 3 sütun dönüştürmek istediğiniz biridir söylemek doubleiçin Int32şunu kullanabilirsiniz:

adapter.FillSchema(table, SchemaType.Source);
table.Columns[2].DataType = typeof (Int32);
adapter.Fill(table);

1
Bağdaştırıcınızın komutu saklı bir yordamsa bunun işe yaramayacağını unutmayın
Mark Sowul

1
Bunu Oracle.MangedDataAccess.Client.OracleDataAdaptersaklı bir prosedürle test ettim . İşe yarıyor. Teşekkürler.
3

21

Eski gönderi, ancak bir seferde tek bir sütunu belirli bir türe dönüştürebilen bir DataTable uzantısı ile tartacağımı düşündüm:

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;
        }
    }
}

Daha sonra şu şekilde çağrılabilir:

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

Elbette, sütundaki her değer aslında yeni türe dönüştürülebildiği sürece, istediğiniz türü kullanın.


"Nesne, IConvertible uygulamalıdır." Hatası verir. Byte [] 'ı dize türü sütununa dönüştürürken.
Harshad Vekariya

Sadece ona bazı jenerikler ekleyin public static void ConvertColumnType<T>(this DataTable dt, string columnName, TnewType) where T : Type, IConvertible
TM

3
Bence bu en zarif çözüm, DBNull'ları dönüştürmekten kaçının, örneğin dr[dc.ColumnName] = dr[columnName] == DBNull.Value ? DBNull.Value : Convert.ChangeType(dr[columnName], newType);
miboper

8

Dönüş türünü de değiştirmeyi düşünün:

select cast(columnName as int) columnName from table

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)

8

Ben biraz farklı bir yaklaşım izledim. OA tarih biçiminde olan bir excel içe aktarımından bir tarih saati ayrıştırmam gerekiyordu. Bu metodoloji, temelde ... temelden inşa etmek için yeterince basittir,

  1. İstediğiniz türde sütun ekleyin
  2. Değeri dönüştürerek satırları yarın
  3. Orijinal sütunu silin ve eskiyle eşleşecek şekilde yenisiyle yeniden adlandırın

    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;
        }

Teşekkürler! Tam olarak aradığım şey.
Rahul Singh

4

A DataTabledoldurulduktan sonra , bir sütunun türünü değiştiremezsiniz.

Bu senaryodaki en iyi seçeneğiniz, doldurmadan önce öğesine bir Int32sütun eklemektir DataTable:

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

Ardından orijinal tablonuzdaki verileri yeni tabloya klonlayabilirsiniz:

DataTable dataTableClone = dataTable.Clone();

İşte daha fazla ayrıntı içeren bir gönderi .


4

yalnızca bir sütunu değiştirmek istiyorsanız, örneğin dizeden int32'ye, Expression özelliğini kullanabilirsiniz:

DataColumn col = new DataColumn("col_int" , typeof(int));
table.Columns.Add(col);
col.Expression = "table_exist_col_string"; // digit string convert to int  

iyi cevap! foreach (...) ve boş değerleri kontrol etmeye gerek yok .
Behzad Ebrahimi

2

Bir DataTable'ın sütun türünü değiştirmeye izin veren bir uzantı işlevi oluşturdum. Tüm tabloyu klonlamak ve tüm verileri içe aktarmak yerine, yalnızca sütunu klonlar, değeri ayrıştırır ve ardından orijinali siler.

    /// <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;
    }
}

Bunu şu şekilde kullanabilirsiniz:

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

Yukarıdaki kod, tüm ondalık sütunları ikiye katlar.


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

1

Mark'ın çözümünün verimliliğini - bu yüzden DataTable'ın tamamına ihtiyacım yok.Clone - jenerikler ve genişletilebilirlikle birleştirdim, böylece kendi dönüştürme fonksiyonumu tanımlayabilirim . Bu benim sonucum:

/// <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;
}

Bu şekilde, kullanım çok daha kolay ve aynı zamanda özelleştirilebilir:

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

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

1
Cevabınıza bir bağlam eklemelisiniz, sadece kod cevabının değeri yoktur, çünkü bunlar düşük kaliteli olarak görülür.
Nawed Nabi Zada
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.