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 tốt về mặt hiệu suất cũng như về mặt an toàn thất bại.
Đừ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 nhau sẽ được mở và đóng liên tục. Để giảm thiểu chi phí mở kết nối, ADO.NET sử dụng một 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 người dùng gọi Mở trên một kết nối, trình tổng hợp sẽ tìm kiếm một kết nối khả dụng trong nhóm. Nếu một kết nối tổng hợ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 quay trở lại nhóm, nó đã sẵn sàng để được sử dụng lại trong lần 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 sự chúng không đượ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, điều 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 lớn đến hiệu suất mà còn là nguồn gốc cho 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 các kết nối tĩnh, bạn đang tạo một khóa cho mọi luồng đang cố gắng truy cập đối tượng này. Bản chất ASP.NET 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.
- Đừng làm cho chúng 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à hủy chúng ở nơi bạn cần (fe trong một phương thức)
- sử dụng
using-statement
để hủy và đóng (trong trường hợp Kết nối) một cách ẩn ý
Điều đó đúng không chỉ với Connections (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 là 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 phản ánh 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à một triển khai có thể có của retrievePromotion
-method của bạn :
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 PromotionID=@PromotionID";
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;
}