Entity Framework และ Connection Pooling


268

ฉันเพิ่งเริ่มใช้ Entity Framework 4.0 ในแอปพลิเคชัน. NET 4.0 ของฉันและอยากรู้เกี่ยวกับบางสิ่งที่เกี่ยวข้องกับการรวมกำไรกัน

  1. การเชื่อมต่อร่วมกันอย่างที่ฉันรู้ได้รับการจัดการโดยผู้ให้บริการข้อมูล ADO.NET ในกรณีของเซิร์ฟเวอร์ MS SQL สิ่งนี้มีผลบังคับใช้เมื่อคุณสร้างอินสแตนซ์บริบทใหม่ ( ObjectContext) เช่นไม่มีพารามิเตอร์new MyDatabaseModelEntities()หรือไม่

  2. อะไรคือข้อดีและข้อเสียของ a) การสร้างบริบทเอนทิตีระดับโลกสำหรับแอปพลิเคชัน (เช่นอินสแตนซ์แบบคงที่หนึ่ง) หรือ b) การสร้างและเปิดเผยบริบทเอนทิตีสำหรับแต่ละการดำเนินการ / วิธีที่usingกำหนด

  3. คำแนะนำอื่น ๆ แนวปฏิบัติที่ดีที่สุดหรือแนวทางทั่วไปสำหรับบางสถานการณ์ที่ฉันควรรู้

คำตอบ:


369
  1. การรวมการเชื่อมต่อได้รับการจัดการเช่นเดียวกับในแอปพลิเคชัน ADO.NET อื่น ๆ การเชื่อมต่อเอนทิตียังคงใช้การเชื่อมต่อฐานข้อมูลดั้งเดิมกับสตริงการเชื่อมต่อดั้งเดิม ฉันเชื่อว่าคุณสามารถปิดการเชื่อมต่อร่วมกันในสตริงการเชื่อมต่อหากคุณไม่ต้องการใช้ (อ่านเพิ่มเติมเกี่ยวกับSQL Server Connection Pooling (ADO.NET )
  2. ไม่เคยใช้บริบททั่วโลก ObjectContext ใช้รูปแบบหลายอย่างภายในรวมถึง Identity Map และ Unit of Work ผลกระทบของการใช้บริบทโลกแตกต่างกันตามประเภทของแอปพลิเคชัน
  3. สำหรับเว็บแอปพลิเคชันให้ใช้บริบทเดียวต่อคำขอ สำหรับบริการเว็บใช้บริบทเดียวต่อการโทร ใน WinForms หรือแอปพลิเคชัน WPF ใช้บริบทเดียวต่อแบบฟอร์มหรือต่อผู้นำเสนอ อาจมีข้อกำหนดพิเศษบางอย่างที่ไม่อนุญาตให้ใช้วิธีนี้ แต่ในสถานการณ์ส่วนใหญ่ก็เพียงพอแล้ว

หากคุณต้องการที่จะรู้ว่าผลกระทบมีบริบทวัตถุเดียวสำหรับการประยุกต์ใช้ WPF / WinForm ตรวจสอบบทความ มันเกี่ยวกับ NHibernate Session แต่ความคิดก็เหมือนกัน

แก้ไข:

เมื่อคุณใช้ EF โดยค่าเริ่มต้นจะโหลดแต่ละเอนทิตีเพียงครั้งเดียวต่อบริบท แบบสอบถามแรกสร้างเอนทิตี้ของและเก็บไว้ภายใน คิวรีใด ๆ ที่ตามมาซึ่งต้องการเอนทิตีที่มีคีย์เดียวกันจะส่งคืนอินสแตนซ์ที่เก็บไว้นี้ หากค่าในแหล่งข้อมูลเปลี่ยนไปคุณยังคงได้รับเอนทิตีที่มีค่าจากคิวรีเริ่มต้น นี้เรียกว่าเอกลักษณ์รูปแบบแผนที่ คุณสามารถบังคับให้บริบทวัตถุโหลดเอนทิตี้ได้ แต่จะโหลดซ้ำอินสแตนซ์ที่แชร์เพียงครั้งเดียว

การเปลี่ยนแปลงใด ๆ ที่เกิดขึ้นกับเอนทิตีจะไม่คงอยู่จนกว่าคุณจะโทรหาSaveChangesบริบท คุณสามารถทำการเปลี่ยนแปลงในหลายเอนทิตีและจัดเก็บได้ทันที นี้เรียกว่าหน่วยของรูปแบบการทำงาน คุณไม่สามารถเลือกได้ว่าต้องการแนบไฟล์เอนทิตีที่แก้ไขหรือไม่

รวมสองรูปแบบเหล่านี้แล้วคุณจะเห็นเอฟเฟกต์ที่น่าสนใจ คุณมีเอนทิตี้ของอินสแตนซ์เดียวเท่านั้นสำหรับแอปพลิเคชันทั้งหมด การเปลี่ยนแปลงใด ๆ ต่อเอนทิตีจะมีผลกับแอปพลิเคชั่นทั้งหมดแม้ว่าจะยังไม่มีการเปลี่ยนแปลง (ยืนยัน) ในเวลาส่วนใหญ่นี่ไม่ใช่สิ่งที่คุณต้องการ สมมติว่าคุณมีแบบฟอร์มแก้ไขในแอ็พพลิเคชัน WPF คุณกำลังทำงานกับเอนทิตีและคุณตัดสินใจที่จะยกเลิกการแก้ไขที่ซับซ้อน (การเปลี่ยนแปลงค่าเพิ่มเอนทิตีที่เกี่ยวข้องลบเอนทิตีที่เกี่ยวข้องอื่น ๆ ฯลฯ ) แต่เอนทิตีถูกแก้ไขแล้วในบริบทที่ใช้ร่วมกัน คุณจะทำอะไร? คำแนะนำ: ผมไม่ทราบเกี่ยวกับ CancelChanges ใด ๆ หรือ UndoChanges ObjectContextบน

ฉันคิดว่าเราไม่ต้องพูดคุยเรื่องเซิร์ฟเวอร์ เพียงแบ่งปันเอนทิตีเดียวระหว่างการร้องขอ HTTP หลายครั้งหรือการเรียกบริการบนเว็บทำให้แอปพลิเคชันของคุณไร้ประโยชน์ คำขอใด ๆ ก็สามารถทริกเกอร์SaveChangesและบันทึกข้อมูลบางส่วนจากคำขออื่นได้เนื่องจากคุณแบ่งปันหน่วยงานเดียวในหมู่พวกเขาทั้งหมด สิ่งนี้จะมีปัญหาอื่น - บริบทและการจัดการใด ๆ กับเอนทิตีในบริบทหรือการเชื่อมต่อฐานข้อมูลที่ใช้โดยบริบทนั้นไม่ปลอดภัยเธรด

แม้แต่แอปพลิเคชันแบบอ่านอย่างเดียวบริบททั่วโลกไม่ใช่ตัวเลือกที่ดีเพราะคุณอาจต้องการข้อมูลใหม่ทุกครั้งที่คุณค้นหาแอปพลิเคชัน


ขอบคุณสำหรับการตอบกลับของคุณ. บางทีคุณอาจอธิบายอย่างละเอียดว่าทำไมการใช้บริบทเดียวทั่วโลกจึงไม่ดี มันทำให้การเข้าถึงแบบขนานยากขึ้นแน่นอน แต่มีอะไรอีกบ้าง ...
Noldorin

ตกลงตอนนี้ชัดเจนมากขึ้นขอบคุณ เพียงเพื่อยืนยันแม้ว่าบริบททั่วโลกจะไม่เหมาะสมจริง ๆ บริบทเดียวสำหรับ "กล่องโต้ตอบแก้ไข" หรือสิ่งนั้นอาจเป็นวิธีที่ถูกต้อง? ในสถานการณ์อื่น ๆ เช่นเว็บเซอร์วิสและ ASP.NET บริบทภายในเมธอดจะเหมาะสมกว่าเท่านั้น เกี่ยวกับถูกต้องหรือไม่
Noldorin

ฉันเอาคำแนะนำของคุณและลบ Singelton ตอนนี้ฉันได้รับข้อผิดพลาดอื่น: stackoverflow.com/questions/14795899/ …
Elad Benda

ฉันเข้าใจว่าการใช้หน่วยรูปแบบการทำงานและการห่อหุ้ม DbContext ควรแยกตรรกะทางธุรกิจและการดำเนินการฐานข้อมูล ฉันไม่สามารถเข้าใจวิธีการใช้รูปแบบหน่วยการทำงานและใช้ TransactionScope สำหรับการดำเนินการบางอย่างเท่านั้น
Rudolf Dvoracek

4
@RudolfDvoracek: ได้อย่างง่ายดาย TransactionScopeไม่ได้อยู่ในหน่วยงาน แต่เป็นของตรรกะทางธุรกิจของคุณเพราะตรรกะนั้นกำหนดธุรกรรม หน่วยของงานจะกำหนดสิ่งที่ควรจะคงอยู่ด้วยกันเท่านั้นในขณะที่ขอบเขตการทำธุรกรรมช่วยให้คุณใช้หน่วยการคงอยู่ของการทำงานหลายครั้งภายในธุรกรรมเดียวกัน
Ladislav Mrnka

70

อ้างอิงจาก Daniel Simmons:

สร้างอินสแตนซ์ ObjectContext ใหม่ในการใช้คำสั่งสำหรับแต่ละวิธีการบริการเพื่อที่จะถูกกำจัดก่อนที่วิธีการส่งกลับ ขั้นตอนนี้มีความสำคัญต่อความยืดหยุ่นในการบริการของคุณ ตรวจสอบให้แน่ใจว่าการเชื่อมต่อฐานข้อมูลไม่ได้ถูกเปิดไว้ระหว่างการเรียกใช้บริการและสถานะชั่วคราวที่ใช้โดยการดำเนินการเฉพาะอย่างคือการรวบรวมขยะเมื่อการดำเนินการนั้นสิ้นสุดลง Entity Framework จะแคชข้อมูลเมตาและข้อมูลอื่น ๆ ที่ต้องการในโดเมนแอพและการเชื่อมต่อฐานข้อมูลพูลกลุ่ม ADO.NET โดยอัตโนมัติดังนั้นการสร้างบริบทใหม่ทุกครั้งคือการดำเนินการอย่างรวดเร็ว

นี่คือจากบทความที่ครอบคลุมของเขาที่นี่:

http://msdn.microsoft.com/en-us/magazine/ee335715.aspx

ฉันเชื่อว่าคำแนะนำนี้ครอบคลุมไปถึงคำขอ HTTP ดังนั้นจะใช้ได้สำหรับ ASP.NET แอปพลิเคชันไคลเอนต์แบบ fat-state เช่นแอปพลิเคชัน WPF อาจเป็นกรณีเดียวสำหรับบริบท "ที่ใช้ร่วมกัน"


ขอบคุณนั่นเป็นคำพูดที่ให้ข้อมูลมาก อย่างไรก็ตามฉันยังคงสงสัยว่าบริบทที่ใช้ร่วมกัน (ทั่วโลก) จะเหมาะสมแม้สำหรับแอป WPF ของลูกค้าหรือไม่ มีใด ๆแม้จะได้เปรียบในกรณีนี้หรือไม่?
Noldorin

จะไม่มีประโยชน์กับบริบทส่วนกลางในแอป WPF แต่อาจไม่มีความเสียหายอย่างมีนัยสำคัญ หากคุณใช้บริบททั่วโลกคุณอาจต้องทำการจัดการการเชื่อมต่อฐานข้อมูลด้วยตนเอง (การปิดการเชื่อมต่ออย่างชัดเจน) ในกรณีที่มีอัตราการร้องขอสูง
Dave Swersky

1
ขวา; ดังนั้นโดยพื้นฐานแล้วฉันไม่สามารถผิดพลาดได้จริงๆโดยใช้บริบทชั่วคราวหลายรายการ (เนื่องจากฉันรู้ว่ามีการรวมการเชื่อมต่อเกิดขึ้น) ... หากคุณใช้บริบททั่วโลกเพียงจุดเดียวการเชื่อมต่อในทฤษฎีจะไม่สามารถลดลงตามเวลาแบบสุ่มได้หรือไม่?
Noldorin

1
@Nododrin: ฉันไม่คิดว่าการเชื่อมต่อจะลดลง "แบบสุ่ม" ... ความเสี่ยงคือการเชื่อมต่ออาจจะเปิดนานเกินไปและทำให้อิ่มตัวการเชื่อมต่อ
Dave Swersky

1
ObjectContext / DbContext นำไปใช้IDisposableดังนั้นควรเปิดในเวลาที่สั้นที่สุดคือมุมมองของฉัน
nicodemus13

12

ไปที่เอกสารประกอบของ EF6 (4,5 ด้วย): https://msdn.microsoft.com/en-us/data/hh949853#9

9.3 บริบทต่อคำขอ

บริบทของ Entity Framework มีเจตนาที่จะนำมาใช้เป็นกรณีสั้น ๆ เพื่อที่จะให้ประสบการณ์ประสิทธิภาพที่ดีที่สุดมากที่สุด คาดว่าบริบทจะสั้นและถูกทอดทิ้งและมีการนำไปใช้เพื่อให้มีน้ำหนักเบาและเมตาดาต้าที่ reutilize เมื่อใดก็ตามที่เป็นไปได้ ในสถานการณ์สมมติทางเว็บสิ่งสำคัญคือต้องคำนึงถึงสิ่งนี้และไม่มีบริบทนานเกินกว่าระยะเวลาของคำขอเดียว ในทำนองเดียวกันในสถานการณ์ที่ไม่ใช่เว็บบริบทควรถูกลบทิ้งตามความเข้าใจของคุณเกี่ยวกับระดับการแคชต่าง ๆ ใน Entity Framework โดยทั่วไปแล้วเราควรหลีกเลี่ยงการใช้อินสแตนซ์บริบทตลอดอายุการใช้งานของแอปพลิเคชันรวมถึงบริบทต่อเธรดและบริบทคงที่


2
ฉันรู้ว่าคำตอบนี้ได้รับที่นี่ในขณะที่ แต่ฉันต้องบอกว่านี้ช่วยให้ฉันปวดหัวตัน ได้รับข้อผิดพลาด "การเชื่อมต่อพูล" เมื่อใช้ EF กับ Oracle และไม่สามารถหาสาเหตุได้ ฉันได้ตั้งค่า dbContext เป็นตัวแปรคลาสให้สร้างอินสแตนซ์ที่สร้าง การเปลี่ยนเป็นการสร้างบริบทตามที่ต้องการแก้ไขความเจ็บป่วยของโลกทั้งหมดของฉัน ขอบคุณ!
Fletchius

1

โค้ดด้านล่างช่วยให้วัตถุของฉันได้รับการฟื้นฟูด้วยค่าฐานข้อมูลใหม่ คำสั่ง Entry (object) .Reload () บังคับให้วัตถุเรียกคืนค่าฐานข้อมูล

GM_MEMBERS member = DatabaseObjectContext.GM_MEMBERS.FirstOrDefault(p => p.Username == username && p.ApplicationName == this.ApplicationName);
DatabaseObjectContext.Entry(member).Reload();

เช่นนี้สำหรับคอลเลกชัน (รหัส VB):CType(myContext, IObjectContextAdapter).ObjectContext.Refresh(RefreshMode.StoreWins,myCustomers)
Ivan Ferrer Villa
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.