您的位置:首頁(yè) > 軟件教程 > 教程 > 實(shí)現(xiàn)對(duì)象集合與DataTable的相互轉(zhuǎn)換

實(shí)現(xiàn)對(duì)象集合與DataTable的相互轉(zhuǎn)換

來源:好特整理 | 時(shí)間:2024-12-02 10:07:41 | 閱讀:84 |  標(biāo)簽: a T 對(duì)象 El C 開源 excel EA   | 分享到:

講解對(duì)象集合與DataTable相互轉(zhuǎn)換,包括表格轉(zhuǎn)對(duì)象集合需校驗(yàn)類型、列名對(duì)應(yīng),及解決結(jié)構(gòu)體賦值問題;對(duì)象集合轉(zhuǎn)表格、一維數(shù)組轉(zhuǎn)表格、行列轉(zhuǎn)置的方法及示例代碼,測(cè)試源碼已上傳。

書接上回,我們今天繼續(xù)講解實(shí)現(xiàn)對(duì)象集合與DataTable的相互轉(zhuǎn)換。

實(shí)現(xiàn)對(duì)象集合與DataTable的相互轉(zhuǎn)換

01、把表格轉(zhuǎn)換為對(duì)象集合

該方法是將表格的列名稱作為類的屬性名,將表格的行數(shù)據(jù)轉(zhuǎn)為類的對(duì)象。從而實(shí)現(xiàn)表格轉(zhuǎn)換為對(duì)象集合。同時(shí)我們約定如果類的屬性設(shè)置了DescriptionAttribute特性,則特性值和表格列名一一對(duì)應(yīng),如果沒有設(shè)置特性則取屬性名稱和列名一一對(duì)應(yīng)。

同時(shí)我們需要約束類只能為結(jié)構(gòu)體或類,而不能是枚舉、基礎(chǔ)類型、以及集合類型、委托、接口等。

類的類型校驗(yàn)成功后,我們還需要校驗(yàn)表格是否能轉(zhuǎn)換為對(duì)象,即判斷表格列名和類的屬性名稱或者Description特性值是否存在一致,如果沒有一個(gè)表格列名和類的屬性能對(duì)應(yīng)上,則報(bào)表格列名無法映射至對(duì)象屬性,無法完成轉(zhuǎn)換異常。

當(dāng)這些校驗(yàn)成功后,開始循環(huán)處理表格行記錄,把每一行都轉(zhuǎn)換為一個(gè)對(duì)象。

我們可以通過反射動(dòng)態(tài)實(shí)例化對(duì)象,再通過反射對(duì)對(duì)象的屬性動(dòng)態(tài)賦值。因?yàn)槲覀兊膶?duì)象即支持類也支持結(jié)構(gòu)體,因此這里面就會(huì)遇到一個(gè)技術(shù)問題,正常的property.SetValue方法并沒有辦法給結(jié)構(gòu)體動(dòng)態(tài)賦值。

這是因?yàn)榻Y(jié)構(gòu)體是值類型,而property.SetValue方法的參數(shù)都是object,因此這里面就涉及到裝箱拆箱,因此SetValue是設(shè)置了裝箱以后的對(duì)象,而并不能改變?cè)瓕?duì)象。

而解決辦法就是先把結(jié)構(gòu)體賦值給object變量,然后對(duì)object變量進(jìn)行SetValue設(shè)置值,最后再把object變量轉(zhuǎn)為結(jié)構(gòu)體。

下面我們一起看看具體實(shí)現(xiàn)代碼:

//把表格轉(zhuǎn)換為對(duì)象集合
//如果設(shè)置DescriptionAttribute,則將特性值作為表格的列名稱
//否則將屬性名作為表格的列名稱
public static IEnumerable ToModels(DataTable dataTable)
{
    //T必須是結(jié)構(gòu)體或類,并且不能是集合類型
    AssertTypeValid();
    if (0 == dataTable.Rows.Count)
    {
        return [];
    }
    //獲取T所有可寫入屬性
    var properties = typeof(T).GetProperties().Where(u => u.CanWrite);
    //校驗(yàn)表格是否能轉(zhuǎn)換為對(duì)象
    var isCanParse = IsCanMapDataTableToModel(dataTable, properties);
    if (!isCanParse)
    {
        throw new NotSupportedException("The column name of the table cannot be mapped to an object property, and the conversion cannot be completed.");
    }
    var models = new List();
    foreach (DataRow dr in dataTable.Rows)
    {
        //通過反射實(shí)例化T
        var model = Activator.CreateInstance();
        //把行數(shù)據(jù)映射到對(duì)象上
        if (typeof(T).IsClass)
        {
            //處理T為類的情況
            MapRowToModel(dr, model, properties);
        }
        else
        {
            //處理T為結(jié)構(gòu)體的情況
            object boxed = model!;
            MapRowToModel(dr, boxed, properties);
            model = (T)boxed;
        }
        models.Add(model);
    }
    return models;
}
//校驗(yàn)表格是否能轉(zhuǎn)換為對(duì)象
private static bool IsCanMapDataTableToModel(DataTable dataTable, IEnumerable properties)
{
    var isCanParse = false;
    foreach (var property in properties)
    {
        //根據(jù)屬性獲取列名
        var columnName = GetColumnName(property);
        if (!dataTable.Columns.Contains(columnName))
        {
            continue;
        }
        isCanParse = true;
    }
    return isCanParse;
}
//把行數(shù)據(jù)映射到對(duì)象上
private static void MapRowToModel(DataRow dataRow, T model, IEnumerable properties)
{
    foreach (var property in properties)
    {
        //根據(jù)屬性獲取列名
        var columnName = GetColumnName(property);
        if (!dataRow.Table.Columns.Contains(columnName))
        {
            continue;
        }
        //獲取單元格值
        var value = dataRow[columnName];
        if (value != DBNull.Value)
        {
            //給對(duì)象屬性賦值
            property.SetValue(model, Convert.ChangeType(value, property.PropertyType));
        }
    }
}

我們做個(gè)簡(jiǎn)單的單元測(cè)試:

[Fact]
public void ToModels()
{
    //驗(yàn)證正常情況
    var table = TableHelper.Create>();
    var row1 = table.NewRow();
    row1[0] = "Id-11";
    row1[1] = "名稱-12";
    row1[2] = 33.13;
    table.Rows.Add(row1);
    var row2 = table.NewRow();
    row2[0] = "Id-21";
    row2[1] = "名稱-22";
    row2[2] = 33.23;
    table.Rows.Add(row2);
    var students = TableHelper.ToModels>(table);
    Assert.Equal(2, students.Count());
    Assert.Equal("Id-11", students.ElementAt(0).Id);
    Assert.Equal("名稱-12", students.ElementAt(0).Name);
    Assert.Equal(33.13, students.ElementAt(0).Age);
    Assert.Equal("Id-21", students.ElementAt(1).Id);
    Assert.Equal("名稱-22", students.ElementAt(1).Name);
    Assert.Equal(33.23, students.ElementAt(1).Age);
}

02、把對(duì)象集合轉(zhuǎn)換為表格

該方法首先會(huì)調(diào)用根據(jù)對(duì)象創(chuàng)建表格方法得到一個(gè)空白表格,然后通過反射獲取對(duì)象的所有屬性,然后循環(huán)處理對(duì)象集合,把一個(gè)對(duì)象的所有屬性值一個(gè)一個(gè)添加行的所有列中,這樣就完成了一個(gè)對(duì)象映射成表的一行記錄,直至所有對(duì)象轉(zhuǎn)換完成即可得到一個(gè)表格。

代碼如下:

//把對(duì)象集合轉(zhuǎn)為表格
//如果設(shè)置DescriptionAttribute,則將特性值作為表格的列名稱
//否則將屬性名作為表格的列名稱
public static DataTable ToDataTable(IEnumerable models, string? tableName = null)
{
    //創(chuàng)建表格
    var dataTable = Create(tableName);
    if (models == null || !models.Any())
    {
        return dataTable;
    }
    //獲取所有屬性
    var properties = typeof(T).GetProperties().Where(u => u.CanRead);
    foreach (var model in models)
    {
        //創(chuàng)建行
        var dataRow = dataTable.NewRow();
        foreach (var property in properties)
        {
            //根據(jù)屬性獲取列名
            var columnName = GetColumnName(property);
            //填充行數(shù)據(jù)
            dataRow[columnName] = property.GetValue(model);
        }
        dataTable.Rows.Add(dataRow);
    }
    return dataTable;
}

進(jìn)行如下單元測(cè)試:

[Fact]
public void ToDataTable()
{
    //驗(yàn)證正常情況
    var students = new List>();
    var student1 = new Student
    {
        Id = "Id-11",
        Name = "名稱-12",
        Age = 33.13
    };
    students.Add(student1);
    var student2 = new Student
    {
        Id = "Id-21",
        Name = "名稱-22",
        Age = 33.23
    };
    students.Add(student2);
    var table = TableHelper.ToDataTable>(students, "學(xué)生表");
    Assert.Equal("學(xué)生表", table.TableName);
    Assert.Equal(2, table.Rows.Count);
    Assert.Equal("Id-11", table.Rows[0][0]);
    Assert.Equal("名稱-12", table.Rows[0][1]);
    Assert.Equal("33.13", table.Rows[0][2].ToString());
    Assert.Equal("Id-21", table.Rows[1][0]);
    Assert.Equal("名稱-22", table.Rows[1][1]);
    Assert.Equal("33.23", table.Rows[1][2].ToString());
}

03、把一維數(shù)組作為一列轉(zhuǎn)換為表格

該方法比較簡(jiǎn)單就是把一個(gè)一維數(shù)組作為一列數(shù)據(jù)創(chuàng)建一張表格,同時(shí)可以選擇是否填寫表名和列名。具體代碼如下:

//把一維數(shù)組作為一列轉(zhuǎn)換為表格
public static DataTable ToDataTableWithColumnArray(TColumn[] array, string? tableName = null, string? columnName = null)
{
    var dataTable = new DataTable(tableName);
    //創(chuàng)建列
    dataTable.Columns.Add(columnName, typeof(TColumn));
    //添加行數(shù)據(jù)
    foreach (var item in array)
    {
        var dataRow = dataTable.NewRow();
        dataRow[0] = item;
        dataTable.Rows.Add(dataRow);
    }
    return dataTable;
}

單元測(cè)試如下:

[Fact]
public void ToDataTableWithColumnArray()
{
    //驗(yàn)證正常情況
    var columns = new string[] { "A", "B" };
    var table = TableHelper.ToDataTableWithColumnArray(columns, "學(xué)生表");
    Assert.Equal("學(xué)生表", table.TableName);
    Assert.Equal("Column1", table.Columns[0].ColumnName);
    Assert.Equal(2, table.Rows.Count);
    Assert.Equal("A", table.Rows[0][0]);
    Assert.Equal("B", table.Rows[1][0]);
    table = TableHelper.ToDataTableWithColumnArray(columns, "學(xué)生表", "列");
    Assert.Equal("列", table.Columns[0].ColumnName);
}

04、把一維數(shù)組作為一行轉(zhuǎn)換為表格

該方法也比較簡(jiǎn)單就是把一個(gè)一維數(shù)組作為一行數(shù)據(jù)創(chuàng)建一張表格,同時(shí)可以選擇是否填寫表名。具體代碼如下:

//把一維數(shù)組作為一行轉(zhuǎn)換為表格
public static DataTable ToDataTableWithRowArray(TRow[] array, string? tableName = null)
{
    var dataTable = new DataTable(tableName);
    //創(chuàng)建列
    for (var i = 0; i < array.Length; i++)
    {
        dataTable.Columns.Add(null, typeof(TRow));
    }
    //添加行數(shù)據(jù)
    var dataRow = dataTable.NewRow();
    for (var i = 0; i < array.Length; i++)
    {
        dataRow[i] = array[i];
    }
    dataTable.Rows.Add(dataRow);
    return dataTable;
}

05、行列轉(zhuǎn)置

該方法是指把DataTable中的行和列互換,就是行的數(shù)據(jù)變成列,列的數(shù)據(jù)變成行。如下圖示例:

實(shí)現(xiàn)對(duì)象集合與DataTable的相互轉(zhuǎn)換

這個(gè)示例轉(zhuǎn)換,第一個(gè)表格中列名并沒有作為數(shù)據(jù)進(jìn)行轉(zhuǎn)換,因此我們會(huì)提供一個(gè)可選項(xiàng)參數(shù)用來指示要不要把類目作為數(shù)據(jù)進(jìn)行轉(zhuǎn)換。

整個(gè)方法實(shí)現(xiàn)邏輯也很簡(jiǎn)單,就是以原表格行數(shù)為列數(shù)創(chuàng)建一個(gè)新表格,然后在循環(huán)處理原表格列,并把原表格一列數(shù)據(jù)填充至新表格的一行數(shù)據(jù)中,直至原表格所有列處理完成則完成行列轉(zhuǎn)置。具體代碼如下:

//行列轉(zhuǎn)置
public static DataTable Transpose(DataTable dataTable, bool isColumnNameAsData = true)
{
    var transposed = new DataTable(dataTable.TableName);
    //如果列名作為數(shù)據(jù),則需要多加一列
    if (isColumnNameAsData)
    {
        transposed.Columns.Add();
    }
    //轉(zhuǎn)置后,行數(shù)即為新的列數(shù)
    for (int i = 0; i < dataTable.Rows.Count; i++)
    {
        transposed.Columns.Add();
    }
    //以列為單位,一次處理一列數(shù)據(jù)
    for (var column = 0; column < dataTable.Columns.Count; column++)
    {
        //創(chuàng)建新行
        var newRow = transposed.NewRow();
        //如果列名作為數(shù)據(jù),則先把列名加入第一列
        if (isColumnNameAsData)
        {
            newRow[0] = dataTable.Columns[column].ColumnName;
        }
        //把一列數(shù)據(jù)轉(zhuǎn)為一行數(shù)據(jù)
        for (var row = 0; row < dataTable.Rows.Count; row++)
        {
            //如果列名作為數(shù)據(jù),則行數(shù)據(jù)從第二列開始填充
            var rowIndex = isColumnNameAsData ? row + 1 : row;
            newRow[rowIndex] = dataTable.Rows[row][column];
        }
        transposed.Rows.Add(newRow);
    }
    return transposed;
}

下面進(jìn)行簡(jiǎn)單的單元測(cè)試:

[Fact]
public void Transpose_ColumnNameAsData()
{
    DataTable originalTable = new DataTable("測(cè)試");
    originalTable.Columns.Add("A", typeof(string));
    originalTable.Columns.Add("B", typeof(int));
    originalTable.Columns.Add("C", typeof(int));
    originalTable.Rows.Add("D", 1, 2);
    //列名作為數(shù)據(jù)的情況
    var table = TableHelper.Transpose(originalTable, true);
    Assert.Equal(originalTable.TableName, table.TableName);
    Assert.Equal("Column1", table.Columns[0].ColumnName);
    Assert.Equal("Column2", table.Columns[1].ColumnName);
    Assert.Equal(3, table.Rows.Count);
    Assert.Equal("A", table.Rows[0][0]);
    Assert.Equal("D", table.Rows[0][1]);
    Assert.Equal("B", table.Rows[1][0]);
    Assert.Equal("1", table.Rows[1][1].ToString());
    Assert.Equal("C", table.Rows[2][0]);
    Assert.Equal("2", table.Rows[2][1].ToString());
}

:測(cè)試方法代碼以及示例源碼都已經(jīng)上傳至代碼庫(kù),有興趣的可以看看。 https://gitee.com/hugogoos/Ideal

小編推薦閱讀

好特網(wǎng)發(fā)布此文僅為傳遞信息,不代表好特網(wǎng)認(rèn)同期限觀點(diǎn)或證實(shí)其描述。

a 1.0
a 1.0
類型:休閑益智  運(yùn)營(yíng)狀態(tài):正式運(yùn)營(yíng)  語(yǔ)言:中文   

游戲攻略

游戲禮包

游戲視頻

游戲下載

游戲活動(dòng)

《alittletotheleft》官網(wǎng)正版是一款備受歡迎的休閑益智整理游戲。玩家的任務(wù)是對(duì)日常生活中的各種雜亂物

相關(guān)視頻攻略

更多

掃二維碼進(jìn)入好特網(wǎng)手機(jī)版本!

掃二維碼進(jìn)入好特網(wǎng)微信公眾號(hào)!

本站所有軟件,都由網(wǎng)友上傳,如有侵犯你的版權(quán),請(qǐng)發(fā)郵件[email protected]

湘ICP備2022002427號(hào)-10 湘公網(wǎng)安備:43070202000427號(hào)© 2013~2025 haote.com 好特網(wǎng)