Xin lỗi vì chỉ nhận xét ở vị trí đầu tiên, nhưng hầu như ngày nào tôi cũng đăng một nhận xét tương tự vì nhiều người nghĩ rằng sẽ rất thông minh nếu gói chức năng ADO.NET vào một DB-Class (tôi cũng vậy 10 năm trước). Hầu hết họ quyết định sử dụng các đối tượng tĩnh / được chia sẻ vì nó có vẻ nhanh hơn so với việc tạo một đối tượng mới cho bất kỳ hành động nào.
Đó không phải là một ý tưởng hay về mặt hiệu suất cũng như về mặt an toàn khi thất bại.
Không săn trộm trên lãnh thổ của Connection-Pool
Có một lý do chính đáng tại sao ADO.NET quản lý nội bộ các Kết nối cơ bản với DBMS trong Nhóm kết nối ADO-NET:
Trong thực tế, hầu hết các ứng dụng chỉ sử dụng một hoặc một vài cấu hình khác nhau cho các kết nối. Điều này có nghĩa là trong quá trình thực thi ứng dụng, nhiều kết nối giống hệt nhau sẽ được mở và đóng lại nhiều lần. Để giảm thiểu chi phí mở kết nối, ADO.NET sử dụng kỹ thuật tối ưu hóa được gọi là gộp kết nối.
Việc gộp kết nối làm giảm số lần các kết nối mới phải được mở. Pooler duy trì quyền sở hữu kết nối vật lý. Nó quản lý các kết nối bằng cách duy trì một tập hợp các kết nối đang hoạt động cho mỗi cấu hình kết nối nhất định. Bất cứ khi nào một usercalls Mở trên một kết nối, pooler sẽ tìm kiếm một kết nối khả dụng trong pool. Nếu một kết nối gộp khả dụng, nó sẽ trả về cho người gọi thay vì mở một kết nối mới. Khi ứng dụng gọi Đóng trên kết nối, trình tổng hợp trả nó về tập hợp các kết nối hoạt động được gộp chung thay vì đóng nó. Sau khi kết nối được trả lại cho nhóm, nó đã sẵn sàng để được sử dụng lại trong cuộc gọi Mở tiếp theo.
Vì vậy, rõ ràng là không có lý do gì để tránh tạo, mở hoặc đóng các kết nối vì thực ra chúng không hề được tạo, mở và đóng. Đây là "chỉ" một cờ để nhóm kết nối biết khi nào một kết nối có thể được sử dụng lại hay không. Nhưng đó là một cờ rất quan trọng, bởi vì nếu một kết nối "đang được sử dụng" (nhóm kết nối giả định), thì một kết nối vật lý mới phải được hoạt động với DBMS, một kết nối rất tốn kém.
Vì vậy, bạn không cải thiện hiệu suất mà ngược lại. Nếu đạt đến kích thước nhóm tối đa được chỉ định (100 là mặc định), bạn thậm chí sẽ nhận được ngoại lệ (quá nhiều kết nối đang mở ...). Vì vậy, điều này sẽ không chỉ ảnh hưởng rất nhiều đến hiệu suất mà còn là nguồn gây ra các lỗi khó chịu và (không sử dụng Giao dịch) một khu vực kết xuất dữ liệu.
Nếu bạn thậm chí đang sử dụng kết nối tĩnh, bạn đang tạo khóa cho mọi luồng đang cố gắng truy cập đối tượng này. ASP.NET về bản chất là một môi trường đa luồng. Vì vậy, có một cơ hội lớn cho những khóa này gây ra các vấn đề về hiệu suất tốt nhất. Trên thực tế, sớm hay muộn bạn sẽ nhận được nhiều ngoại lệ khác nhau (như ExecuteReader của bạn yêu cầu một Kết nối mở và có sẵn ).
Kết luận :
- Không sử dụng lại các kết nối hoặc bất kỳ đối tượng ADO.NET nào.
- Không đặt chúng ở chế độ tĩnh / được chia sẻ (trong VB.NET)
- Luôn tạo, mở (trong trường hợp có Kết nối), sử dụng, đóng và xử lý chúng ở những nơi bạn cần (tức là trong một phương pháp)
- sử dụng
using-statement
để loại bỏ và đóng (trong trường hợp Kết nối) một cách ẩn ý
Điều đó đúng không chỉ đối với Kết nối (mặc dù đáng chú ý nhất). Mọi đối tượng triển khai IDisposable
nên được xử lý (đơn giản nhất bằng using-statement
), tất cả những thứ khác trong System.Data.SqlClient
không gian tên.
Tất cả những điều trên đều chống lại một Lớp DB tùy chỉnh đóng gói và sử dụng lại tất cả các đối tượng. Đó là lý do tại sao tôi bình luận để thùng rác nó. Đó chỉ là một nguồn có vấn đề.
Chỉnh sửa :Đây là cách triển khai có thể có của retrievePromotion
của bạn -method:
public Promotion retrievePromotion(int promotionID)
{
Promotion promo = null;
var connectionString = System.Configuration.ConfigurationManager.ConnectionStrings["MainConnStr"].ConnectionString;
using (SqlConnection connection = new SqlConnection(connectionString))
{
var queryString = "SELECT PromotionID, PromotionTitle, PromotionURL FROM Promotion WHERE [email protected]";
using (var da = new SqlDataAdapter(queryString, connection))
{
// you could also use a SqlDataReader instead
// note that a DataTable does not need to be disposed since it does not implement IDisposable
var tblPromotion = new DataTable();
// avoid SQL-Injection
da.SelectCommand.Parameters.Add("@PromotionID", SqlDbType.Int);
da.SelectCommand.Parameters["@PromotionID"].Value = promotionID;
try
{
connection.Open(); // not necessarily needed in this case because DataAdapter.Fill does it otherwise
da.Fill(tblPromotion);
if (tblPromotion.Rows.Count != 0)
{
var promoRow = tblPromotion.Rows[0];
promo = new Promotion()
{
promotionID = promotionID,
promotionTitle = promoRow.Field<String>("PromotionTitle"),
promotionUrl = promoRow.Field<String>("PromotionURL")
};
}
}
catch (Exception ex)
{
// log this exception or throw it up the StackTrace
// we do not need a finally-block to close the connection since it will be closed implicitely in an using-statement
throw;
}
}
}
return promo;
}