如何动态指定List的类型<>功能?

时间:2012-12-31 17:00:27

标签: c# list types datagridview

我有一些表示数据库表的类,要在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();
}

对于其他表,还有其他一些函数:list_rows_table2,list_rows_table3 ... 我的问题是:如何创建唯一的List<>函数,我可以在其中动态指定返回的List<>的类型,或如何转换,例如List<object>到{{1}在返回之前。

2 个答案:

答案 0 :(得分:7)

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

public interface IData
{
    void FillFromReader(IDataReader dr);
}

然后像这样改变你的方法

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

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

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


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

请参阅using Statement (C# Reference)

答案 1 :(得分:1)

Olivier的实施很好。它使用泛型和接口为每个实体提供它自己的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();
    }
}

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

ORM将消除处理数据访问的许多麻烦。缺点是,您很多时候都与DTO和数据库架构相关联。当然,使用抽象可以包含这个问题。许多公司仍然使用严格存储的过程,大多数ORM在被迫使用存储过程时都会崩溃。它们不适用于存储过程。

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