6

我有一些代表数据库表的类,要在 a 上加载每个表的行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>返回List<myClass>之前的a。

4

2 回答 2

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 语句(C# 参考)

于 2012-12-31T17:13:18.393 回答
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 上,专为使用存储过程而设计。上面的代码是它的轻量级实现。

于 2013-01-01T19:42:07.903 回答