この回答は、主に「選択」操作と更新/作成/削除操作に焦点を当てています。一度に複数または数個のレコードを更新することはめったにないと思うので、「選択」もボトルネックが発生しやすい場所だと思います。そうは言っても、アプリケーション(プロファイル)を知る必要があります。最適化時間を集中するのに最適な場所は、ほとんどの場合、クライアントコードではなく、クエリ自体のデータベースレベルです。クライアントコードはすべて単なる配管であり、アプリの主な力ではありません。ただし、配管はさまざまなアプリで再利用される傾向があるため、可能な限り最適に近づけたいという願望に共感します。そのため、そのコードの作成方法については十分に説明します。
データレイヤーでクエリ/プロシージャを選択するための一般的な方法があります。これは次のようになります。
private static IEnumerable<IDataRecord> Retrieve(string sql, Action<SqlParameterCollection> addParameters)
{
//ConnectionString is a private static property in the data layer
// You can implement it to read from a config file or elsewhere
using (var cn = new SqlConnection(ConnectionString))
using (var cmd = new SqlCommand(sql, cn))
{
addParameters(cmd.Parameters);
cn.Open();
using (var rdr = cmd.ExecuteReader())
{
while (rdr.Read())
yield return rdr;
rdr.Close();
}
}
}
これにより、匿名メソッドを使用してパラメーターを追加するパブリックデータレイヤーメソッドを作成できます。示されているコードは.Net2.0以降で機能しますが、.Net 3.5を使用してさらに短く記述できます:
public IEnumerable<IDataRecord> GetFooChildrenByParentID(int ParentID)
{
//I could easily use a stored procedure name instead of a full sql query
return Retrieve(
@"SELECT c.*
FROM [ParentTable] p
INNER JOIN [ChildTable] c ON c.ParentID = f.ID
WHERE f.ID= @ParentID", delegate(SqlParameterCollection p)
{
p.Add("@ParentID", SqlDbType.Int).Value = ParentID;
}
);
}
ここで停止します。パラメータの作成に匿名メソッドを使用する上記のコードをもう一度紹介します。
これは非常にクリーンなコードであり、クエリ定義とパラメータの作成を同じ場所に配置すると同時に、定型的なデータベース接続/呼び出しコードをより再利用可能な場所に抽象化できます。この手法は、あなたの質問の箇条書きのいずれにも当てはまらないと思います。また、たまたまかなり高速です。これはあなたの質問の推力をカバーしていると思います。
ただし、これがどのように組み合わされるかを説明し続けたいと思います。残りはかなり簡単ですが、これをリストなどに入れて間違ってしまうことも簡単で、最終的にはパフォーマンスが低下します。次に、ビジネスレイヤーはファクトリを使用してクエリ結果をオブジェクトに変換します(c#3.0以降):
public class Foo
{
//various normal properties and methods go here
public static Foo FooFactory(IDataRecord record)
{
return new Foo
{
Property1 = record[0],
Property2 = record[1]
//...
};
}
}
これらをクラスに配置するのではなく、ファクトリメソッドを保持することを特に目的とした静的クラスにすべてグループ化することもできます。
元のretrieveメソッドに1つの変更を加える必要があります。そのメソッドは同じオブジェクトを何度も「生成」しますが、これは必ずしもうまく機能するとは限りません。それを機能させるために別の方法で実行したいのは、現在のレコードで表されるオブジェクトのコピーを強制することです。これにより、リーダーが次のレコードに変更したときに、クリーンなデータで作業します。最終的なコードで使用できるように、ファクトリメソッドを表示するまで待ちました。新しいRetrieveメソッドは次のようになります:
private static IEnumerable<T> Retrieve(Func<IDataRecord, T> factory,
string sql, Action<SqlParameterCollection> addParameters)
{
//ConnectionString is a private static property in the data layer
// You can implement it to read from a config file or elsewhere
using (var cn = new SqlConnection(ConnectionString))
using (var cmd = new SqlCommand(sql, cn))
{
addParameters(cmd.Parameters);
cn.Open();
using (var rdr = cmd.ExecuteReader())
{
while (rdr.Read())
yield return factory(rdr);
rdr.Close();
}
}
}
そして今、私たちはその新しいRetrieve()メソッドを次のように呼び出します:
public IEnumerable<Foo> GetFooChildrenByParentID(int ParentID)
{
//I could easily use a stored procedure name instead of a full sql query
return Retrieve(Foo.FooFactory,
@"SELECT c.*
FROM [ParentTable] p
INNER JOIN [ChildTable] c ON c.ParentID = f.ID
WHERE f.ID= @ParentID", delegate(SqlParameterCollection p)
{
p.Add("@ParentID", SqlDbType.Int).Value = ParentID;
}
);
}
明らかに、この最後の方法は、必要な追加のビジネスロジックを含めるように拡張できます。また、このコードはIEnumerableの遅延評価機能を利用しているため、非常に高速であることがわかります。欠点は、短命のオブジェクトを多数作成する傾向があり、それがあなたが尋ねたトランザクションパフォーマンスを損なう可能性があることです。これを回避するために、適切なn層を壊して、IDataRecordオブジェクトをプレゼンテーション層に直接渡し、グリッドコントロールにすぐにバインドされるレコードの不要なオブジェクト作成を回避することがあります。
コードの更新/作成も同様ですが、通常、一度に変更するのは多数ではなく1つのレコードのみであるという違いがあります。
または、この長い投稿を読む手間を省いて、EntityFrameworkを使用するように指示することもできます;)