如何动态指定List <>函数的类型?

Shi*_*hin 6 c# types datagridview list

我有一些表示数据库表的类,要加载每个表的行DataGridView,我有一个List<>函数,在循环中获取该表中的所有行.

public List<class_Table1> list_rows_table1()
{
    // class_Table1 contains each column of table as public property
    List<class_Table1> myList = new List<class_Table1>();

    // sp_List_Rows: stored procedure that lists data
    //   from Table1 with some conditions or filters
    Connection cnx = new Connection;
    Command cmd = new Command(sp_List_Rows, cnx);

    cnx.Open;
    IDataReader dr = cmd.ExecuteReader();

    while (dr.Read())
    {
        class_Table1 ct = new class_Table1();

        ct.ID   = Convert.ToInt32(dr[ID_table1]);
        ct.Name = dr[name_table1].ToString();
        //... all others wanted columns follow here

        myList.Add(ct);
    }
    dr.Close();
    cnx.Close();

    // myList contains all wanted rows; from a Form fills a dataGridView
    return myList();
}
Run Code Online (Sandbox Code Playgroud)

而对于其他表,其他一些功能:list_rows_table2,list_rows_table3 ...我的问题是:如何创建一个唯一的List<>功能,在那里我可以动态指定类型List<>返回,或者如何转换,例如List<object>List<myClass>返回之前.

Oli*_*bes 7

您可以拥有一个所有数据类必须实现的接口

public interface IData
{
    void FillFromReader(IDataReader dr);
}
Run Code Online (Sandbox Code Playgroud)

然后像这样改变你的方法

public List<T> GetList<T>(string sqlText)
    where T : IData, new()
{
    List<T> myList = new List<T>();

    using (Connection cnx = new Connection(connString))
    using (Command cmd = new Command(sqlText, cnx)) {
        cnx.Open();
        using (IDataReader dr = cmd.ExecuteReader()) {
            while (dr.Read())
            {
                T item = new T();
                item.FillFromReader(dr);
                myList.Add(item);
            }
        }
    }
    return myList();
}
Run Code Online (Sandbox Code Playgroud)

所以基本上每个班级都要负责填写自己的领域.

where T : IData, new()泛型类型参数的约束是至关重要的.它告诉方法,T必须实现接口IData.这对于能够在FillFromReader不进行强制转换的情况下调用方法是必要的.数据类必须具有默认构造函数(由此指定new().这使您可以实例化一个new T().


我使用连接,命令和带有using语句的数据读取器包围了代码.该using语句在块结束时自动关闭并释放资源,并确保发生这种情况,即使应该抛出异常或者语句块应该过早地使用return语句.

请参阅使用Statement(C#参考)


Chu*_*way 1

奥利维尔的实施很好。它使用泛型和接口,为每个实体提供自己的 FillFromDataReader() 实现。

你可以走得更远。通过使用约定,所有数据水合代码都可以集中并抽象出来。

我假设您的类属性名称和列名称相同。如果不是,则可以扩展以下代码以将别名属性添加到属性名称中。有时,某个属性是根据对象中的其他值计算得出的,该属性无法进行水合。可以在下面的类中创建和实现 Ignore 属性。

public class DataAccess
{
    /// <summary>
    /// Hydrates the collection of the type passes in.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="sql">The SQL.</param>
    /// <param name="connection">The connection.</param>
    /// <returns>List{``0}.</returns>
    public List<T> List<T>(string sql, string connection) where T: new()
    {
        List<T> items = new List<T>();

        using (SqlCommand command = new SqlCommand(sql, new SqlConnection(connection)))
        {
            string[] columns = GetColumnsNames<T>();
            var reader = command.ExecuteReader(CommandBehavior.CloseConnection);

            while (reader.Read())
            {
                T item = new T();

                foreach (var column in columns)
                {
                    object val = reader.GetValue(reader.GetOrdinal(column));
                    SetValue(item, val, column);
                }

                items.Add(item);
            }

            command.Connection.Close();

        }

        return items;
    }

    /// <summary>
    /// Sets the value.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="item">The item.</param>
    /// <param name="value">The value.</param>
    /// <param name="column">The column.</param>
    private void SetValue<T>(T item, object value, string column)
    {
        var property = item.GetType().GetProperty(column);
        property.SetValue(item, value, null);
    }

    /// <summary>
    /// Gets the columns names.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <returns>System.String[][].</returns>
    private string[] GetColumnsNames<T>() where T : new()
    {
        T item = new T();

        return (from i in item.GetType().GetProperties()
                select i.Name).ToArray();
    }
}
Run Code Online (Sandbox Code Playgroud)

上面的代码中有一些注意事项。DBNull 和 Nullable 类型是特殊情况,需要自定义代码来处理它们。我通常将 DBNull 转换为 null。我从来没有遇到过需要区分两者之间差异的情况。对于 Nullalbe 类型,只需检测 Nullable 类型并相应地处理代码即可。

ORM 将消除处理数据访问的大部分麻烦。缺点是很多时候您会耦合到 DTO 和数据库模式。当然这个问题可以通过使用抽象来解决。许多公司仍然严格使用存储过程,大多数 ORM 在被迫使用存储过程时就会崩溃。它们只是不适合与存储过程一起使用。

我编写了一个名为“ Hysonic ”的数据访问框架。它位于 GitHub 上,专门设计用于处理存储过程。上面的代码是它的一个轻量级实现。