6

データベーステーブルを表すクラスがいくつかあり、各テーブルの行をにロードするために、ループ内でそのテーブルからすべての行を取得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>

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これは、インターフェースを実装する必要があるメソッドに指示しますIDataFillFromReaderこれは、キャストせずにメソッドを呼び出すことができるようにするために必要です。データクラスにはデフォルトのコンストラクターが必要です(これはで指定されnew()ます。これにより、。でインスタンス化できますnew T()


接続、コマンド、およびデータリーダーを使用してコードをusingステートメントで囲みました。ステートメントはusing、ブロックの最後でリソースを自動的に閉じて解放し、例外がスローされたり、ステートメントブロックにreturnステートメントが時期尚早に残されたりした場合でも、これが確実に発生するようにします。

ステートメントの使用を参照してください(C#リファレンス)

于 2012-12-31T17:13:18.393 に答える
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();
    }
}

上記のコードにはいくつかの注意点があります。DBNullとNullable型は特殊なケースであり、それらを処理するにはカスタムコードが必要になります。私は通常、DBNullをnullに変換します。私は、2つの違いを区別する必要があるケースに遭遇したことはありません。Nullalbe型の場合は、Nullable型を検出し、それに応じてコードを処理するだけです。

ORMは、データアクセスを処理する際の頭痛の種の多くを取り除きます。欠点は、多くの場合、DTOとデータベーススキーマに結合されていることです。もちろん、この問題は抽象化を使用することで封じ込めることができます。多くの企業は依然として厳密にストアドプロシージャを使用しており、ほとんどのORMは、ストアドプロシージャの使用を余儀なくされると失敗します。これらは、ストアドプロシージャで動作するようには設計されていません。

「ハイパーソニック」というデータアクセスフレームワークを作成しました。これはGitHubにあり、ストアドプロシージャで動作するように特別に設計されています。上記のコードは、それを簡単に実装したものです。

于 2013-01-01T19:42:07.903 に答える