ขออภัยสำหรับการแสดงความคิดเห็นในตอนแรก แต่ฉันโพสต์ความคิดเห็นที่คล้ายกันเกือบทุกวันเนื่องจากหลายคนคิดว่าการห่อหุ้มฟังก์ชัน ADO.NET ไว้ใน DB-Class จะเป็นการดี (ฉันเมื่อ 10 ปีก่อน) ส่วนใหญ่พวกเขาตัดสินใจที่จะใช้วัตถุคงที่ / ใช้ร่วมกันเนื่องจากดูเหมือนว่าจะเร็วกว่าการสร้างวัตถุใหม่สำหรับการกระทำใด ๆ
นั่นไม่ใช่ความคิดที่ดีในแง่ของความเหมาะสมหรือในแง่ของความล้มเหลว - ความปลอดภัย
อย่ารุกล้ำอาณาเขตของ Connection-Pool
มีเหตุผลที่ดีว่าทำไม ADO.NET จึงจัดการการเชื่อมต่อพื้นฐานไปยัง DBMS ภายในADO-NET Connection-Pool :
ในทางปฏิบัติแอปพลิเคชันส่วนใหญ่จะใช้การกำหนดค่าที่แตกต่างกันเพียงหนึ่งหรือสองสามอย่างสำหรับการเชื่อมต่อ ซึ่งหมายความว่าในระหว่างการเรียกใช้งานแอปพลิเคชันการเชื่อมต่อที่เหมือนกันหลาย ๆ ครั้งจะถูกเปิดและปิดซ้ำ ๆ เพื่อลดต้นทุนในการเปิดการเชื่อมต่อ ADO.NET ใช้เทคนิคการเพิ่มประสิทธิภาพที่เรียกว่าการรวมการเชื่อมต่อ
การรวมการเชื่อมต่อช่วยลดจำนวนครั้งที่ต้องเปิดการเชื่อมต่อใหม่ ตัวยึดจะรักษาความเป็นเจ้าของการเชื่อมต่อทางกายภาพ จัดการการเชื่อมต่อโดยการรักษาชุดการเชื่อมต่อที่ใช้งานอยู่สำหรับการกำหนดค่าการเชื่อมต่อแต่ละรายการ เมื่อใดก็ตามที่ผู้ใช้เรียกใช้ Open บนการเชื่อมต่อ pooler จะค้นหาการเชื่อมต่อที่พร้อมใช้งานในพูล หากมีการเชื่อมต่อแบบรวมระบบจะส่งกลับไปยังผู้โทรแทนที่จะเปิดการเชื่อมต่อใหม่ เมื่อแอปพลิเคชันเรียกปิดบนการเชื่อมต่อตัวควบคุมจะส่งกลับไปยังชุดการเชื่อมต่อที่ใช้งานร่วมกันแทนที่จะปิด เมื่อการเชื่อมต่อถูกส่งกลับไปที่พูลก็พร้อมที่จะใช้ซ้ำในการโทรแบบเปิดครั้งถัดไป
เห็นได้ชัดว่าไม่มีเหตุผลใดที่จะหลีกเลี่ยงการสร้างเปิดหรือปิดการเชื่อมต่อเนื่องจากจริงๆแล้วพวกเขาไม่ได้สร้างเปิดและปิดเลย นี่เป็นแฟล็ก "เท่านั้น" สำหรับพูลการเชื่อมต่อเพื่อให้ทราบเมื่อสามารถใช้การเชื่อมต่อซ้ำได้หรือไม่ แต่เป็นแฟล็กที่สำคัญมากเพราะหากการเชื่อมต่อ "ใช้งานอยู่" (พูลการเชื่อมต่อจะถือว่า) การเชื่อมต่อทางกายภาพใหม่จะต้องเปิดขึ้นกับ DBMS ซึ่งมีราคาแพงมาก
ดังนั้นคุณจึงไม่ได้รับการปรับปรุงประสิทธิภาพ แต่ตรงกันข้าม หากถึงขนาดพูลสูงสุดที่ระบุ (100 คือค่าเริ่มต้น) คุณจะได้รับข้อยกเว้น (การเชื่อมต่อที่เปิดมากเกินไป ... ) ดังนั้นสิ่งนี้จะไม่เพียงส่งผลกระทบต่อประสิทธิภาพอย่างมาก แต่ยังเป็นแหล่งที่มาของข้อผิดพลาดที่น่ารังเกียจและ (โดยไม่ใช้ธุรกรรม) พื้นที่ทิ้งข้อมูล
หากคุณใช้การเชื่อมต่อแบบคงที่คุณกำลังสร้างล็อกสำหรับทุกเธรดที่พยายามเข้าถึงวัตถุนี้ ASP.NET เป็นสภาพแวดล้อมแบบมัลติเธรดโดยธรรมชาติ ดังนั้นจึงมีโอกาสที่ดีสำหรับการล็อกเหล่านี้ซึ่งทำให้เกิดปัญหาด้านประสิทธิภาพที่ดีที่สุด ไม่ช้าก็เร็วคุณจะได้รับข้อยกเว้นต่างๆมากมาย (เช่นExecuteReaderของคุณต้องการการเชื่อมต่อแบบเปิดและพร้อมใช้งาน )
สรุป :
- อย่าใช้การเชื่อมต่อซ้ำหรือวัตถุ ADO.NET ใด ๆ เลย
- อย่าทำให้เป็นแบบคงที่ / แชร์ (ใน VB.NET)
- สร้างเปิด (ในกรณีของ Connections) ใช้ปิดและกำจัดทิ้งในที่ที่คุณต้องการเสมอ (fe ในวิธีการ)
- ใช้
using-statement
เพื่อกำจัดและปิด (ในกรณีของ Connections) โดยปริยาย
นั่นเป็นความจริงไม่เพียง แต่สำหรับ Connections (แม้ว่าจะสังเกตเห็นได้ชัดเจนที่สุด) ทุกออบเจ็กต์ที่นำไปใช้IDisposable
ควรถูกกำจัด (ง่ายที่สุดโดยusing-statement
) ยิ่งมีมากขึ้นในSystem.Data.SqlClient
เนมสเปซ
ทั้งหมดข้างต้นพูดถึง DB-Class ที่กำหนดเองซึ่งห่อหุ้มและนำออบเจ็กต์ทั้งหมดกลับมาใช้ใหม่ นั่นคือเหตุผลที่ฉันแสดงความคิดเห็นทิ้งขยะ นั่นเป็นเพียงแหล่งที่มาของปัญหาเท่านั้น
แก้ไข : นี่คือการใช้งานretrievePromotion
-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 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;
}