Jawaban ini berfokus terutama pada operasi 'pilih' vs perbarui/buat/hapus. Saya pikir lebih jarang memperbarui lebih dari satu atau beberapa catatan sekaligus, jadi saya juga berpikir 'pilih' adalah tempat kemacetan cenderung terjadi. Yang mengatakan, Anda perlu mengetahui aplikasi Anda (profil). Tempat terbaik untuk memfokuskan waktu pengoptimalan Anda hampir selalu pada tingkat basis data dalam kueri itu sendiri, daripada kode klien. Semua kode klien hanyalah pipa ledeng:itu bukan kekuatan utama aplikasi Anda. Namun, karena pemipaan cenderung digunakan kembali di banyak aplikasi yang berbeda, saya bersimpati dengan keinginan untuk membuatnya seoptimal mungkin, dan oleh karena itu saya memiliki banyak hal untuk dikatakan tentang cara membuat kode tersebut.
Saya memiliki metode umum untuk memilih kueri/prosedur di lapisan data saya yang terlihat seperti ini:
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();
}
}
}
Dan itu memungkinkan saya menulis metode lapisan data publik yang menggunakan metode anonim untuk menambahkan parameter. Kode yang ditampilkan berfungsi dengan .Net 2.0+, tetapi dapat ditulis lebih pendek lagi menggunakan .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;
}
);
}
Saya akan berhenti di sini sehingga saya dapat mengarahkan Anda lagi ke kode tepat di atas yang menggunakan metode anonim untuk pembuatan parameter.
Ini adalah kode yang sangat bersih, karena menempatkan definisi kueri dan pembuatan parameter di tempat yang sama sambil tetap memungkinkan Anda untuk mengabstraksikan koneksi basis data boilerplate/kode panggilan ke tempat yang lebih dapat digunakan kembali. Saya tidak berpikir teknik ini tercakup oleh poin-poin dalam pertanyaan Anda, dan itu juga sangat cepat. Saya pikir ini tentang mencakup inti pertanyaan Anda.
Saya ingin melanjutkan, meskipun, untuk menjelaskan bagaimana semua ini cocok bersama. Sisanya cukup mudah, tetapi juga mudah untuk membuang ini ke daftar atau yang serupa dan melakukan kesalahan, yang pada akhirnya merusak kinerja. Selanjutnya, lapisan bisnis kemudian menggunakan pabrik untuk menerjemahkan hasil kueri ke objek (c# 3.0 atau yang lebih baru):
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]
//...
};
}
}
Daripada memiliki ini langsung di kelas mereka, Anda juga bisa mengelompokkan semuanya menjadi kelas statis yang secara khusus dimaksudkan untuk menampung metode pabrik.
Saya perlu membuat satu perubahan pada metode pengambilan asli. Metode itu "menghasilkan" objek yang sama berulang-ulang, dan ini tidak selalu bekerja dengan baik. Apa yang ingin kami lakukan secara berbeda untuk membuatnya berfungsi adalah memaksa salinan objek yang diwakili oleh catatan saat ini, sehingga ketika pembaca bermutasi untuk catatan berikutnya, kami bekerja dengan data bersih. Saya menunggu sampai setelah menunjukkan metode pabrik sehingga kami dapat menggunakannya dalam kode akhir. Metode Retrieve yang baru terlihat seperti ini:
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();
}
}
}
Dan sekarang kita akan memanggil metode Retrieve() baru seperti ini:
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;
}
);
}
Jelas metode terakhir ini dapat diperluas untuk memasukkan logika bisnis tambahan yang diperlukan. Ternyata kode ini juga sangat cepat, karena memanfaatkan fitur evaluasi malas dari IEnumerable. Kelemahannya adalah ia cenderung membuat banyak objek berumur pendek, dan itu dapat merusak kinerja transaksional yang Anda tanyakan. Untuk menyiasatinya, saya terkadang memecah n-tier yang baik dan meneruskan objek IDataRecord langsung ke tingkat presentasi dan menghindari pembuatan objek yang tidak perlu untuk rekaman yang langsung terikat ke kontrol grid.
Perbarui/Buat kode serupa, dengan perbedaan bahwa Anda biasanya hanya mengubah satu catatan pada satu waktu daripada banyak.
Atau, saya bisa menyelamatkan Anda membaca posting panjang ini dan hanya memberitahu Anda untuk menggunakan Entity Framework;)