Location>code7788 >text

Open Source - Ideal library - Excel help class, TableHelper implementation (III)

Popularity:932 ℃/2024-12-09 15:22:12

Book picks up last time, we continue today to explain the implementation of object collections and DataTable conversion each other.

01Converting a table into a collection of objects

This method converts the row data of a table into an object of a class by using the column names of the table as property names of the class. Thus, the table is converted into a collection of objects. At the same time, we agree that if the attribute of the class is set with the DescriptionAttribute feature, the value of the feature corresponds to the column name of the table, and if the feature is not set, then the name of the attribute is taken as a one-to-one correspondence with the column name.

At the same time we need to constrain classes to be only structures or classes, not enums, base types, and collection types, delegates, interfaces, etc.

After the class type verification is successful, we also need to verify whether the form can be converted to an object, that is, to determine whether there is consistency between the form column name and the class attribute name or Description feature value, if there is no form column name and class attribute can be corresponded to, then the form column name can not be mapped to the object attribute, and can not be completed to convert the exception.

When these checks are successful, the loop begins to process the table row records, converting each row into an object.

We can dynamically instantiate an object through reflection, and then dynamically assign values to the properties of the object through reflection. Because our objects support both classes and structures, there is a technical problem in that there is no way to dynamically assign values to structures in the normal way.

This is because the structure is a value type, and the parameters of the method are all objects, so it involves boxing and unboxing, so SetValue sets the object after boxing, and does not change the original object.

And the solution is to assign the structure to the object variable first, then set the value of SetValue on the object variable, and finally convert the object variable to a structure.

Let's take a look at the implementation code below:

//Converting a table into a collection of objects
//If you set theDescriptionAttribute,then the characteristic values are used as column names in the table
//Otherwise use the attribute name as the column name of the table
public static IEnumerable<T> ToModels<T>(DataTable dataTable)
{
    //TMust be a structure or class,and cannot be a collection type
    AssertTypeValid<T>();
    if (0 == )
    {
        return [];
    }
    //gainTAll Writable Properties
    var properties = typeof(T).GetProperties().Where(u => );
    //Verify that the form can be converted to an object
    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<T>();
    foreach (DataRow dr in )
    {
        //Instantiation via reflectionT
        var model = <T>();
        //Mapping row data to objects
        if (typeof(T).IsClass)
        {
            //deal withTcase
            MapRowToModel<T>(dr, model, properties);
        }
        else
        {
            //deal withTThe case of a structure
            object boxed = model!;
            MapRowToModel<object>(dr, boxed, properties);
            model = (T)boxed;
        }
        (model);
    }
    return models;
}
//Verify that the form can be converted to an object
private static bool IsCanMapDataTableToModel(DataTable dataTable, IEnumerable<PropertyInfo> properties)
{
    var isCanParse = false;
    foreach (var property in properties)
    {
        //根据属性gain列名
        var columnName = GetColumnName(property);
        if (!(columnName))
        {
            continue;
        }
        isCanParse = true;
    }
    return isCanParse;
}
//Mapping row data to objects
private static void MapRowToModel<T>(DataRow dataRow, T model, IEnumerable<PropertyInfo> properties)
{
    foreach (var property in properties)
    {
        //根据属性gain列名
        var columnName = GetColumnName(property);
        if (!(columnName))
        {
            continue;
        }
        //gain单元格值
        var value = dataRow[columnName];
        if (value != )
        {
            //Assigning values to object properties
            (model, (value, ));
        }
    }
}

Let's do a simple unit test:

[Fact]
public void ToModels()
{
    //Verification of normal conditions
    var table = <Student<double>>();
    var row1 = ();
    row1[0] = "Id-11";
    row1[1] = "name (of a thing)-12";
    row1[2] = 33.13;
    (row1);
    var row2 = ();
    row2[0] = "Id-21";
    row2[1] = "name (of a thing)-22";
    row2[2] = 33.23;
    (row2);
    var students = <Student<double>>(table);
    (2, ());
    ("Id-11", (0).Id);
    ("name (of a thing)-12", (0).Name);
    (33.13, (0).Age);
    ("Id-21", (1).Id);
    ("name (of a thing)-22", (1).Name);
    (33.23, (1).Age);
}

02Converting a collection of objects to a table

The method will first be called according to the object to create a table method to get a blank form, and then through reflection to get all the properties of the object, and then cycle through the collection of objects, the value of all the attributes of an object one by one to add a line in all the columns, so that the completion of an object mapping into a table of a row of records until the completion of the conversion of all the objects can be obtained a table.

The code is as follows:

//Converting a collection of objects to a table
//If you set theDescriptionAttribute,then the characteristic values are used as column names in the table
//Otherwise use the attribute name as the column name of the table
public static DataTable ToDataTable<T>(IEnumerable<T> models, string? tableName = null)
{
    //Creating Forms
    var dataTable = Create<T>(tableName);
    if (models == null || !())
    {
        return dataTable;
    }
    //Get all properties
    var properties = typeof(T).GetProperties().Where(u => );
    foreach (var model in models)
    {
        //Create Row
        var dataRow = ();
        foreach (var property in properties)
        {
            //Get column names based on attributes
            var columnName = GetColumnName(property);
            //Filling rows of data
            dataRow[columnName] = (model);
        }
        (dataRow);
    }
    return dataTable;
}

Perform the following unit test:

[Fact]
public void ToDataTable()
{
    //Verification of normal conditions
    var students = new List<Student<double>>();
    var student1 = new Student<double>
    {
        Id = "Id-11",
        Name = "name (of a thing)-12",
        Age = 33.13
    };
    (student1);
    var student2 = new Student<double>
    {
        Id = "Id-21",
        Name = "name (of a thing)-22",
        Age = 33.23
    };
    (student2);
    var table = <Student<double>>(students, "student chart");
    ("student chart", );
    (2, );
    ("Id-11", [0][0]);
    ("name (of a thing)-12", [0][1]);
    ("33.13", [0][2].ToString());
    ("Id-21", [1][0]);
    ("name (of a thing)-22", [1][1]);
    ("33.23", [1][2].ToString());
}

03, convert a one-dimensional array as a column to a table

The method is relatively simple is a one-dimensional array as a column of data to create a table, while you can choose whether to fill in the table name and column name. Specific code is as follows:

//Converting a one-dimensional array into a table as a column
public static DataTable ToDataTableWithColumnArray<TColumn>(TColumn[] array, string? tableName = null, string? columnName = null)
{
    var dataTable = new DataTable(tableName);
    //Creating Columns
    (columnName, typeof(TColumn));
    //Adding Row Data
    foreach (var item in array)
    {
        var dataRow = ();
        dataRow[0] = item;
        (dataRow);
    }
    return dataTable;
}

The unit tests are as follows:

[Fact]
public void ToDataTableWithColumnArray()
{
    //Verification of normal conditions
    var columns = new string[] { "A", "B" };
    var table = <string>(columns, "student chart");
    ("student chart", );
    ("Column1", [0].ColumnName);
    (2, );
    ("A", [0][0]);
    ("B", [1][0]);
    table = <string>(columns, "student chart", "columns");
    ("columns", [0].ColumnName);
}

04, convert a one-dimensional array as a row to a table

The method is also relatively simple is a one-dimensional array as a line of data to create a table, while you can choose whether to fill in the table name. Specific code is as follows:

//Converting a one-dimensional array as a row to a table
public static DataTable ToDataTableWithRowArray<TRow>(TRow[] array, string? tableName = null)
{
    var dataTable = new DataTable(tableName);
    //Creating Columns
    for (var i = 0; i < ; i++)
    {
        (null, typeof(TRow));
    }
    //Adding Row Data
    var dataRow = ();
    for (var i = 0; i < ; i++)
    {
        dataRow[i] = array[i];
    }
    (dataRow);
    return dataTable;
}

05The rows and columns are transposed.

This method refers to the DataTable rows and columns are interchanged, that is, the rows of data into columns, columns of data into rows. The following figure is an example:

For this example conversion, the column names in the first table are not converted as data, so we will provide an optional parameter to indicate whether or not to convert the categories as data.

The logic of the entire method is also very simple, that is, the number of rows of the original form for the number of columns to create a new form, and then in the cycle of processing the original form of columns, and the original form of a column of data to fill in the new form of a line of data until the completion of the original form of all the columns of the completion of processing is completed rows and columns of the transposition. Specific code is as follows:

// rows and columns transpose
public static DataTable Transpose(DataTable dataTable, bool isColumnNameAsData = true)
{
    var transposed = new DataTable();
    // If the column name is used as data, then an extra column is needed
    if (isColumnNameAsData)
    {
        ();
    }
    // After transposition, the number of rows is the new number of columns
    for (int i = 0; i < ; i++)
    {
        ();
    }
    // Process the data one column at a time, column by column
    for (var column = 0; column < ; column++)
    {
        // Create a new row
        var newRow = ();
        //If the column name is used as data, add the column name to the first column first
        if (isColumnNameAsData)
        {
            newRow[0] = [column].ColumnName;
        }
        // Convert a column to a row
        for (var row = 0; row < ; row++)
        {
            //If column name is used as data, the row data is filled from the second column onwards
            var rowIndex = isColumnNameAsData ? row + 1 : row;
            newRow[rowIndex] = [row][column];
        }
        (newRow);
    }
    return transposed.
}

The following is a simple unit test:

[Fact]
public void Transpose_ColumnNameAsData()
{
    DataTable originalTable = new DataTable("beta (software)");
    ("A", typeof(string));
    ("B", typeof(int));
    ("C", typeof(int));
    ("D", 1, 2);
    //Column names as data
    var table = (originalTable, true);
    (, );
    ("Column1", [0].ColumnName);
    ("Column2", [1].ColumnName);
    (3, );
    ("A", [0][0]);
    ("D", [0][1]);
    ("B", [1][0]);
    ("1", [1][1].ToString());
    ("C", [2][0]);
    ("2", [2][1].ToString());
}

classifier for sums of money: The test method code as well as the sample source code have been uploaded to the code repository for those who are interested./hugogoos/Ideal