Entity Framework - คู่มือฉบับย่อ
Entity Framework คืออะไร?
Entity Framework เปิดตัวครั้งแรกในปี 2008 ซึ่งเป็นวิธีการหลักของ Microsoft ในการโต้ตอบระหว่างแอปพลิเคชัน. NET และฐานข้อมูลเชิงสัมพันธ์ Entity Framework คือ Object Relational Mapper (ORM) ซึ่งเป็นเครื่องมือประเภทหนึ่งที่ช่วยลดความซับซ้อนในการแมประหว่างอ็อบเจ็กต์ในซอฟต์แวร์ของคุณไปยังตารางและคอลัมน์ของฐานข้อมูลเชิงสัมพันธ์
Entity Framework (EF) เป็นกรอบโอเพนซอร์ส ORM สำหรับ ADO.NET ซึ่งเป็นส่วนหนึ่งของ. NET Framework
ORM ดูแลการสร้างการเชื่อมต่อฐานข้อมูลและดำเนินการคำสั่งตลอดจนรับผลการสืบค้นและกำหนดผลลัพธ์เหล่านั้นเป็นวัตถุแอปพลิเคชันของคุณโดยอัตโนมัติ
ORM ยังช่วยติดตามการเปลี่ยนแปลงของออบเจ็กต์เหล่านั้นและเมื่อได้รับคำแนะนำก็จะยังคงมีการเปลี่ยนแปลงเหล่านั้นกลับไปยังฐานข้อมูลให้คุณ
ทำไมต้องเป็น Entity Framework
Entity Framework คือ ORM และ ORM มีวัตถุประสงค์เพื่อเพิ่มประสิทธิภาพการทำงานของนักพัฒนาโดยลดงานที่ซ้ำซ้อนในการคงข้อมูลที่ใช้ในแอปพลิเคชัน
Entity Framework สามารถสร้างคำสั่งฐานข้อมูลที่จำเป็นสำหรับการอ่านหรือเขียนข้อมูลในฐานข้อมูลและดำเนินการให้คุณ
หากคุณกำลังค้นหาคุณสามารถแสดงคำค้นหาของคุณเทียบกับวัตถุโดเมนของคุณโดยใช้ LINQ กับเอนทิตี
Entity Framework จะดำเนินการค้นหาที่เกี่ยวข้องในฐานข้อมูลจากนั้นสร้างผลลัพธ์เป็นอินสแตนซ์ของอ็อบเจ็กต์โดเมนของคุณเพื่อให้คุณทำงานภายในแอพของคุณ
มี ORM อื่น ๆ ในตลาดเช่น NHibernate และ LLBLGen Pro โดยทั่วไป ORM ส่วนใหญ่จะแมปประเภทโดเมนกับสคีมาฐานข้อมูลโดยตรง
Entity Framework มีเลเยอร์การแมปที่ละเอียดขึ้นเพื่อให้คุณสามารถปรับแต่งการแม็พได้เองตัวอย่างเช่นโดยการแม็พเอนทิตีเดียวกับตารางฐานข้อมูลหลายตารางหรือแม้แต่เอนทิตีหลายรายการกับตารางเดียว
Entity Framework เป็นเทคโนโลยีการเข้าถึงข้อมูลที่แนะนำสำหรับแอปพลิเคชันใหม่ของ Microsoft
ADO.NET ดูเหมือนจะอ้างถึงเทคโนโลยีสำหรับชุดข้อมูลและตารางข้อมูลโดยตรง
Entity Framework คือที่ที่มีการลงทุนแบบก้าวไปข้างหน้าทั้งหมดซึ่งเป็นกรณีนี้มาหลายปีแล้ว
Microsoft แนะนำให้คุณใช้ Entity Framework บน ADO.NET หรือ LINQ กับ SQL สำหรับการพัฒนาใหม่ทั้งหมด
รูปแบบความคิด
สำหรับนักพัฒนาที่คุ้นเคยกับการพัฒนาที่เน้นฐานข้อมูลการเปลี่ยนแปลงที่ยิ่งใหญ่ที่สุดของ Entity Framework คือการช่วยให้คุณมุ่งเน้นไปที่โดเมนธุรกิจของคุณ คุณต้องการให้แอปพลิเคชันของคุณทำอะไรโดยไม่ถูก จำกัด โดยสิ่งที่ฐานข้อมูลสามารถทำได้?
ด้วย Entity Framework จุดโฟกัสจะเรียกว่าแบบจำลองแนวคิด เป็นแบบจำลองของวัตถุในแอปพลิเคชันของคุณไม่ใช่แบบจำลองของฐานข้อมูลที่คุณใช้เพื่อคงข้อมูลแอปพลิเคชันของคุณ
แบบจำลองแนวคิดของคุณอาจเกิดขึ้นเพื่อให้สอดคล้องกับสคีมาฐานข้อมูลของคุณหรืออาจแตกต่างกันมาก
คุณสามารถใช้ Visual Designer เพื่อกำหนดแบบจำลองแนวคิดของคุณซึ่งจะสามารถสร้างคลาสที่คุณจะใช้ในแอปพลิเคชันของคุณในท้ายที่สุด
คุณสามารถกำหนดคลาสของคุณและใช้คุณสมบัติของ Entity Framework ที่เรียกว่า Code First จากนั้น Entity Framework จะเข้าใจรูปแบบแนวคิด
ไม่ว่าจะด้วยวิธีใด Entity Framework จะหาวิธีย้ายจากแบบจำลองแนวคิดของคุณไปยังฐานข้อมูลของคุณ ดังนั้นคุณสามารถสอบถามกับวัตถุโมเดลแนวความคิดของคุณและทำงานกับวัตถุเหล่านั้นได้โดยตรง
คุณสมบัติ
ต่อไปนี้เป็นคุณสมบัติพื้นฐานของ Entity Framework รายการนี้สร้างขึ้นตามคุณสมบัติที่โดดเด่นที่สุดและจากคำถามที่พบบ่อยเกี่ยวกับ Entity Framework
- Entity Framework เป็นเครื่องมือของ Microsoft
- Entity Framework กำลังได้รับการพัฒนาเป็นผลิตภัณฑ์ Open Source
- Entity Framework ไม่เชื่อมโยงหรือขึ้นอยู่กับรอบการเผยแพร่. NET อีกต่อไป
- ทำงานร่วมกับฐานข้อมูลเชิงสัมพันธ์กับผู้ให้บริการ Entity Framework ที่ถูกต้อง
- การสร้างคำสั่ง SQL จาก LINQ ไปยังเอนทิตี
- Entity Framework จะสร้างแบบสอบถามที่กำหนดพารามิเตอร์
- ติดตามการเปลี่ยนแปลงวัตถุในหน่วยความจำ
- อนุญาตให้แทรกอัปเดตและลบการสร้างคำสั่ง
- ทำงานร่วมกับโมเดลภาพหรือกับชั้นเรียนของคุณเอง
- Entity Framework ได้จัดเก็บ Procedure Support
สถาปัตยกรรมของ Entity Framework จากล่างขึ้นบนประกอบด้วยสิ่งต่อไปนี้ -
ผู้ให้บริการข้อมูล
สิ่งเหล่านี้เป็นผู้ให้บริการเฉพาะต้นทางซึ่งเป็นนามธรรมของอินเทอร์เฟซ ADO.NET เพื่อเชื่อมต่อกับฐานข้อมูลเมื่อเขียนโปรแกรมกับสคีมาแนวความคิด
จะแปลภาษา SQL ทั่วไปเช่น LINQ ผ่านทางทรีคำสั่งเป็นนิพจน์ SQL ดั้งเดิมและดำเนินการกับระบบ DBMS เฉพาะ
ไคลเอนต์เอนทิตี
เลเยอร์นี้แสดงเลเยอร์เอนทิตีไปที่เลเยอร์บน ไคลเอนต์เอนทิตีให้ความสามารถสำหรับนักพัฒนาในการทำงานกับเอนทิตีในรูปแบบของแถวและคอลัมน์โดยใช้เคียวรี SQL เอนทิตีโดยไม่จำเป็นต้องสร้างคลาสเพื่อแสดงสคีมาแนวคิด ไคลเอนต์เอนทิตีแสดงเลเยอร์เฟรมเวิร์กเอนทิตีซึ่งเป็นฟังก์ชันหลัก เลเยอร์เหล่านี้เรียกว่าแบบจำลองข้อมูลเอนทิตี
Storage Layer มีสกีมาฐานข้อมูลทั้งหมดในรูปแบบ XML
Entity Layer ซึ่งเป็นไฟล์ XML กำหนดเอนทิตีและความสัมพันธ์
Mapping layer เป็นไฟล์ XML ที่แมปเอนทิตีและความสัมพันธ์ที่กำหนดไว้ที่เลเยอร์แนวคิดด้วยความสัมพันธ์จริงและตารางที่กำหนดไว้ที่เลเยอร์ตรรกะ
Metadata services ซึ่งแสดงใน Entity Client ยังให้ API แบบรวมศูนย์เพื่อเข้าถึงเมทาดาทาที่จัดเก็บเลเยอร์เอนทิตีการแมปและที่เก็บข้อมูล
บริการวัตถุ
เลเยอร์ Object Services คือ Object Context ซึ่งแสดงถึงเซสชันของการโต้ตอบระหว่างแอ็พพลิเคชันและแหล่งข้อมูล
การใช้ Object Context หลักคือการดำเนินการต่างๆเช่นเพิ่มลบอินสแตนซ์ของเอนทิตีและบันทึกสถานะที่เปลี่ยนแปลงกลับไปยังฐานข้อมูลด้วยความช่วยเหลือของแบบสอบถาม
เป็นเลเยอร์ ORM ของ Entity Framework ซึ่งแสดงถึงผลลัพธ์ข้อมูลไปยังอินสแตนซ์ออบเจ็กต์ของเอนทิตี
บริการนี้ช่วยให้นักพัฒนาสามารถใช้คุณสมบัติ ORM ที่สมบูรณ์เช่นการแมปคีย์หลักการติดตามการเปลี่ยนแปลง ฯลฯ โดยการเขียนแบบสอบถามโดยใช้ LINQ และ Entity SQL
มีอะไรใหม่ใน Entity Framework 6
Framework มี API ที่ซับซ้อนซึ่งช่วยให้คุณสามารถควบคุมทุกอย่างได้อย่างละเอียดตั้งแต่การสร้างแบบจำลองไปจนถึงพฤติกรรมรันไทม์ ส่วนหนึ่งของ Entity Framework 5 อยู่ภายใน. NET และอีกส่วนหนึ่งอาศัยอยู่ในชุดประกอบเพิ่มเติมที่แจกจ่ายโดยใช้ NuGet
ฟังก์ชันหลักของ Entity Framework ถูกสร้างขึ้นใน. NET Framework
การสนับสนุน Code First นั่นคือสิ่งที่ช่วยให้ Entity Framework ใช้คลาสแทนโมเดลภาพและ API วิธีที่เบากว่าสำหรับการโต้ตอบกับ EF อยู่ในแพ็คเกจ NuGet
แกนหลักคือสิ่งที่ให้การสืบค้นการติดตามการเปลี่ยนแปลงและการเปลี่ยนแปลงทั้งหมดจากการสืบค้นของคุณไปเป็นแบบสอบถาม SQL รวมทั้งจากข้อมูลที่ส่งกลับไปยังวัตถุ
คุณสามารถใช้แพ็คเกจ EF 5 NuGet กับทั้ง. NET 4 และ. NET 4.5
จุดสำคัญจุดหนึ่งของความสับสน -. NET 4.5 ได้เพิ่มการรองรับ enums และข้อมูลเชิงพื้นที่ให้กับ Entity Framework API หลักซึ่งหมายความว่าหากคุณใช้ EF 5 กับ. NET 4 คุณจะไม่ได้รับคุณสมบัติใหม่เหล่านี้ คุณจะได้รับเมื่อรวม EF5 กับ. NET 4.5 เท่านั้น
ตอนนี้เรามาดู Entity Framework 6 กันดีกว่าตอนนี้ API หลักที่อยู่ใน. NET ใน Entity Framework 6 เป็นส่วนหนึ่งของแพ็คเกจ NuGet
มันหมายความว่า -
Entity Framework ทั้งหมดอยู่ภายในชุดประกอบนี้ซึ่งจัดจำหน่ายโดย NuGet
คุณจะไม่ต้องพึ่งพา. NET ในการจัดหาคุณลักษณะเฉพาะเช่นการสนับสนุน Entity Framework enum และการสนับสนุนข้อมูลพิเศษ
คุณจะเห็นว่าคุณสมบัติอย่างหนึ่งของ EF6 คือรองรับ enums และข้อมูลเชิงพื้นที่สำหรับ. NET 4
ในการเริ่มทำงานกับ Entity Framework คุณต้องติดตั้งเครื่องมือการพัฒนาต่อไปนี้ -
- Visual Studio 2013 ขึ้นไป
- SQL Server 2012 ขึ้นไป
- การอัปเดต Entity Framework จาก NuGet Package
ไมโครซอฟท์ยังมีรุ่นฟรีของสตูดิโอภาพซึ่งยังมี SQL Server และสามารถดาวน์โหลดได้จากwww.visualstudio.com
การติดตั้ง
Step 1- เมื่อดาวน์โหลดเสร็จแล้วให้เรียกใช้โปรแกรมติดตั้ง กล่องโต้ตอบต่อไปนี้จะปรากฏขึ้น
Step 2 - คลิกที่ปุ่มติดตั้งและจะเริ่มกระบวนการติดตั้ง
Step 3- เมื่อขั้นตอนการติดตั้งเสร็จสมบูรณ์คุณจะเห็นกล่องโต้ตอบต่อไปนี้ ปิดกล่องโต้ตอบนี้และรีสตาร์ทคอมพิวเตอร์หากจำเป็น
Step 4- เปิด Visual Studio จากเมนูเริ่มซึ่งจะเปิดกล่องโต้ตอบต่อไปนี้ จะเป็นครั้งแรกสำหรับการเตรียมการ
Step 5 - เมื่อเสร็จแล้วคุณจะเห็นหน้าต่างหลักของ Visual studio
มาสร้างโปรเจ็กต์ใหม่จากไฟล์→ใหม่→โปรเจ็กต์
Step 1 - เลือก Console Application แล้วคลิกปุ่ม OK
Step 2 - ในโซลูชัน Explorer ให้คลิกขวาที่โครงการของคุณ
Step 3 - เลือก Manage NuGet Packages ตามที่แสดงในภาพด้านบนซึ่งจะเปิดหน้าต่างต่อไปนี้ใน Visual Studio
Step 4 - ค้นหา Entity Framework และติดตั้งเวอร์ชันล่าสุดโดยกดปุ่มติดตั้ง
Step 5- คลิกตกลง เมื่อติดตั้งเสร็จแล้วคุณจะเห็นข้อความต่อไปนี้ในหน้าต่างผลลัพธ์ของคุณ
ตอนนี้คุณพร้อมที่จะเริ่มแอปพลิเคชันของคุณ
ในบทช่วยสอนนี้เราจะใช้ฐานข้อมูลมหาวิทยาลัยอย่างง่าย ฐานข้อมูลของมหาวิทยาลัยอาจมีความซับซ้อนมากขึ้นโดยรวม แต่เพื่อการสาธิตและการเรียนรู้เรากำลังใช้รูปแบบที่ง่ายที่สุดของฐานข้อมูลนี้ แผนภาพต่อไปนี้ประกอบด้วยสามตาราง
- Student
- Course
- Enrollment
เมื่อใดก็ตามที่มีการใช้ฐานข้อมูลคำสิ่งหนึ่งที่เข้ามาในใจของเราโดยตรงและนั่นก็คือตารางประเภทต่างๆที่มีความสัมพันธ์กัน มีความสัมพันธ์สามประเภทระหว่างตารางและความสัมพันธ์ระหว่างตารางต่างๆขึ้นอยู่กับวิธีกำหนดคอลัมน์ที่เกี่ยวข้อง
- ความสัมพันธ์แบบหนึ่งต่อกลุ่ม
- ความสัมพันธ์แบบกลุ่มต่อกลุ่ม
- ความสัมพันธ์แบบหนึ่งต่อหนึ่ง
ความสัมพันธ์แบบหนึ่งต่อกลุ่ม
ความสัมพันธ์แบบหนึ่งต่อกลุ่มเป็นประเภทความสัมพันธ์ที่พบบ่อยที่สุด ในความสัมพันธ์ประเภทนี้แถวในตาราง A สามารถมีแถวที่ตรงกันได้หลายแถวในตาราง B แต่แถวในตาราง B สามารถมีแถวที่ตรงกันเพียงแถวเดียวในตาราง A ตัวอย่างเช่นในแผนภาพด้านบนตารางนักเรียนและตารางการลงทะเบียนมีหนึ่งแถว - ความสัมพันธ์ต่อหลายคนนักเรียนแต่ละคนอาจมีการลงทะเบียนหลายครั้ง แต่การลงทะเบียนแต่ละครั้งเป็นของนักเรียนเพียงคนเดียว
ความสัมพันธ์แบบกลุ่มต่อกลุ่ม
ในความสัมพันธ์แบบกลุ่มต่อกลุ่มแถวในตาราง A สามารถมีแถวที่ตรงกันได้หลายแถวในตาราง B และในทางกลับกัน คุณสร้างความสัมพันธ์ดังกล่าวโดยการกำหนดตารางที่สามเรียกว่าตารางทางแยกซึ่งคีย์หลักประกอบด้วยคีย์ต่างประเทศจากทั้งตาราง A และตาราง B ตัวอย่างเช่นตารางนักเรียนและหลักสูตรมีความสัมพันธ์แบบกลุ่มต่อกลุ่มที่กำหนดโดย ความสัมพันธ์แบบหนึ่งต่อกลุ่มจากแต่ละตารางเหล่านี้ไปยังตารางการลงทะเบียน
ความสัมพันธ์แบบหนึ่งต่อหนึ่ง
ในความสัมพันธ์แบบหนึ่งต่อหนึ่งแถวในตาราง A สามารถมีแถวที่ตรงกันได้ไม่เกินหนึ่งแถวในตาราง B และในทางกลับกัน ความสัมพันธ์แบบหนึ่งต่อหนึ่งจะถูกสร้างขึ้นหากทั้งสองคอลัมน์ที่เกี่ยวข้องเป็นคีย์หลักหรือมีข้อ จำกัด เฉพาะ
ความสัมพันธ์ประเภทนี้ไม่ได้เกิดขึ้นบ่อยนักเนื่องจากข้อมูลส่วนใหญ่ที่เกี่ยวข้องในลักษณะนี้จะเป็นแบบ all-in-one table คุณอาจใช้ความสัมพันธ์แบบหนึ่งต่อกลุ่มเพื่อ -
- แบ่งตารางที่มีหลายคอลัมน์
- แยกส่วนของตารางด้วยเหตุผลด้านความปลอดภัย
- จัดเก็บข้อมูลที่มีอายุการใช้งานสั้นและสามารถลบได้อย่างง่ายดายเพียงแค่ลบตาราง
- จัดเก็บข้อมูลที่ใช้กับส่วนย่อยของตารางหลักเท่านั้น
Entity Data Model (EDM) เป็นเวอร์ชันขยายของแบบจำลองเอนทิตี - ความสัมพันธ์ซึ่งระบุรูปแบบแนวคิดของข้อมูลโดยใช้เทคนิคการสร้างแบบจำลองต่างๆ นอกจากนี้ยังหมายถึงชุดของแนวคิดที่อธิบายโครงสร้างข้อมูลโดยไม่คำนึงถึงรูปแบบที่จัดเก็บไว้
EDM สนับสนุนชุดของชนิดข้อมูลดั้งเดิมที่กำหนดคุณสมบัติในแบบจำลองแนวคิด เราจำเป็นต้องพิจารณา 3 ส่วนหลักซึ่งเป็นพื้นฐานสำหรับ Entity Framework และเรียกรวมกันว่า Entity Data Model ต่อไปนี้เป็นสามส่วนหลักของ EDM
- รูปแบบสคีมาที่เก็บข้อมูล
- แบบจำลองแนวคิด
- แบบจำลองการทำแผนที่
รูปแบบสคีมาที่เก็บข้อมูล
Storage Model เรียกอีกอย่างว่า Storage Schema Definition Layer (SSDL) แสดงถึงการแสดงแผนผังของที่เก็บข้อมูลแบ็กเอนด์
แบบจำลองแนวคิด
Conceptual Model เรียกอีกอย่างว่า Conceptual Schema Definition Layer (CSDL) เป็นแบบจำลองเอนทิตีจริงซึ่งเราเขียนแบบสอบถามของเรา
แบบจำลองการทำแผนที่
Mapping Layer เป็นเพียงการแมประหว่างโมเดลแนวคิดและโมเดลที่เก็บข้อมูล
สคีมาเชิงตรรกะและการแมปกับสคีมาทางกายภาพจะแสดงเป็น EDM
Visual Studio ยังมี Entity Designer สำหรับการสร้างภาพของ EDM และข้อกำหนดการแม็ป
ผลลัพธ์ของเครื่องมือคือไฟล์ XML (* .edmx) ที่ระบุสคีมาและการแมป
ไฟล์ Edmx มีส่วนข้อมูลเมตาของ Entity Framework
ภาษานิยามของสคีมา
ADO.NET Entity Framework ใช้ภาษานิยามข้อมูลตาม XML ที่เรียกว่า Schema Definition Language (SDL) เพื่อกำหนด EDM Schema
SDL กำหนดประเภทแบบง่ายที่คล้ายกับประเภทดั้งเดิมอื่น ๆ ได้แก่ String, Int32, Double, Decimal และ DateTime เป็นต้น
การแจงนับซึ่งกำหนดแผนผังของค่าดั้งเดิมและชื่อถือเป็นประเภทที่เรียบง่ายเช่นกัน
การแจงนับได้รับการสนับสนุนจากเฟรมเวิร์กเวอร์ชัน 5.0 เป็นต้นไปเท่านั้น
ประเภทที่ซับซ้อนถูกสร้างขึ้นจากการรวมประเภทอื่น ๆ การรวบรวมคุณสมบัติของประเภทเหล่านี้กำหนดประเภทเอนทิตี
โมเดลข้อมูลมีแนวคิดหลักสามประการในการอธิบายโครงสร้างข้อมูล -
- ประเภทเอนทิตี
- ประเภทการเชื่อมโยง
- Property
ประเภทเอนทิตี
ประเภทเอนทิตีเป็นส่วนประกอบพื้นฐานสำหรับการอธิบายโครงสร้างของข้อมูลใน EDM
ในแบบจำลองแนวคิดประเภทเอนทิตีถูกสร้างขึ้นจากคุณสมบัติและอธิบายโครงสร้างของแนวคิดระดับบนสุดเช่นนักเรียนและการลงทะเบียนในแอปพลิเคชันทางธุรกิจ
เอนทิตีแสดงถึงออบเจ็กต์เฉพาะเช่นนักเรียนหรือการลงทะเบียนเฉพาะ
แต่ละเอนทิตีต้องมีคีย์เอนทิตีเฉพาะภายในชุดเอนทิตี ชุดเอนทิตีคือชุดของอินสแตนซ์ของประเภทเอนทิตีเฉพาะ ชุดเอนทิตี (และชุดการเชื่อมโยง) ถูกจัดกลุ่มอย่างมีเหตุผลในคอนเทนเนอร์เอนทิตี
การสืบทอดได้รับการสนับสนุนกับประเภทเอนทิตีนั่นคือประเภทเอนทิตีหนึ่งสามารถได้รับมาจากอีกประเภทหนึ่ง
ประเภทการเชื่อมโยง
เป็นโครงสร้างพื้นฐานอีกอย่างหนึ่งสำหรับการอธิบายความสัมพันธ์ใน EDM ในแบบจำลองแนวความคิดการเชื่อมโยงแสดงถึงความสัมพันธ์ระหว่างเอนทิตีสองประเภทเช่นนักศึกษาและการลงทะเบียน
ทุกการเชื่อมโยงจะมีจุดสิ้นสุดการเชื่อมโยงสองแบบซึ่งระบุประเภทเอนทิตีที่เกี่ยวข้องในการเชื่อมโยง
การเชื่อมโยงแต่ละจุดสิ้นสุดยังระบุการสิ้นสุดการเชื่อมโยงหลายหลากที่ระบุจำนวนของเอนทิตีที่สามารถอยู่ที่ส่วนท้ายของการเชื่อมโยง
การสิ้นสุดการเชื่อมโยงหลายหลากสามารถมีค่าหนึ่ง (1) ศูนย์หรือหนึ่ง (0..1) หรือหลายค่า (*)
เอนทิตีที่ปลายด้านหนึ่งของการเชื่อมโยงสามารถเข้าถึงได้ผ่านคุณสมบัติการนำทางหรือผ่านคีย์ต่างประเทศหากมีการเปิดเผยในประเภทเอนทิตี
ทรัพย์สิน
ประเภทเอนทิตีประกอบด้วยคุณสมบัติที่กำหนดโครงสร้างและลักษณะ ตัวอย่างเช่นประเภทเอนทิตี Student อาจมีคุณสมบัติเช่น Student Id, Name เป็นต้น
คุณสมบัติสามารถมีข้อมูลพื้นฐาน (เช่นสตริงจำนวนเต็มหรือค่าบูลีน) หรือข้อมูลที่มีโครงสร้าง (เช่นประเภทที่ซับซ้อน)
Entity Framework ช่วยให้คุณสามารถสอบถามแทรกอัพเดตและลบข้อมูลโดยใช้อ็อบเจ็กต์ Common Language Runtime (CLR) ซึ่งเรียกว่าเอนทิตี Entity Framework จะแมปเอนทิตีและความสัมพันธ์ที่กำหนดไว้ในโมเดลของคุณกับฐานข้อมูล นอกจากนี้ยังมีสิ่งอำนวยความสะดวกให้ -
- ทำให้ข้อมูลที่ส่งคืนจากฐานข้อมูลเป็นวัตถุเอนทิตี
- ติดตามการเปลี่ยนแปลงที่เกิดขึ้นกับวัตถุ
- จัดการการทำงานพร้อมกัน
- แพร่กระจายวัตถุจะเปลี่ยนกลับไปที่ฐานข้อมูล
- ผูกวัตถุเข้ากับตัวควบคุม
คลาสหลักที่รับผิดชอบในการโต้ตอบกับข้อมูลเป็นอ็อบเจ็กต์คือ System.Data.Entity.DbContext DbContext API ไม่ถูกนำออกใช้เป็นส่วนหนึ่งของ. NET Framework เพื่อให้มีความยืดหยุ่นและบ่อยขึ้นด้วยการเผยแพร่คุณลักษณะใหม่ ๆ ใน Code First และ DbContext API ทีม Entity Framework จะแจกจ่าย EntityFramework.dll ผ่านคุณลักษณะการแจกจ่าย NuGet ของ Microsoft
NuGet ช่วยให้คุณสามารถเพิ่มการอ้างอิงไปยังโครงการ. NET ของคุณได้โดยดึง DLL ที่เกี่ยวข้องไปยังโปรเจ็กต์ของคุณโดยตรงจากเว็บ
ส่วนขยาย Visual Studio ที่เรียกว่า Library Package Manager เป็นวิธีง่ายๆในการดึงแอสเซมบลีที่เหมาะสมจากเว็บไปยังโปรเจ็กต์ของคุณ
DbContext API ส่วนใหญ่มีเป้าหมายเพื่อลดความซับซ้อนของการโต้ตอบกับ Entity Framework
นอกจากนี้ยังลดจำนวนวิธีการและคุณสมบัติที่คุณต้องการเพื่อเข้าถึงงานที่ใช้กันทั่วไป
ใน Entity Framework เวอร์ชันก่อนหน้างานเหล่านี้มักจะซับซ้อนในการค้นหาและเขียนโค้ด
คลาสบริบทจัดการเอนทิตีอ็อบเจ็กต์ระหว่างรันไทม์ซึ่งรวมถึงการเติมอ็อบเจ็กต์ด้วยข้อมูลจากฐานข้อมูลการติดตามการเปลี่ยนแปลงและข้อมูลที่มีอยู่ไปยังฐานข้อมูล
การกำหนดคลาสที่ได้รับ DbContext
วิธีที่แนะนำในการทำงานกับบริบทคือการกำหนดคลาสที่มาจาก DbContext และแสดงคุณสมบัติ DbSet ที่แสดงถึงคอลเลกชันของเอนทิตีที่ระบุในบริบท หากคุณกำลังทำงานกับ EF Designer บริบทจะถูกสร้างขึ้นให้คุณ หากคุณกำลังทำงานกับ Code First โดยทั่วไปคุณจะเขียนบริบทด้วยตัวเอง
โค้ดต่อไปนี้เป็นตัวอย่างง่ายๆที่แสดงว่า UniContext มาจาก DbContext
คุณสามารถใช้คุณสมบัติอัตโนมัติกับ DbSet เช่น getter และ setter
นอกจากนี้ยังสร้างรหัสที่สะอาดกว่ามาก แต่คุณไม่จำเป็นต้องใช้เพื่อจุดประสงค์ในการสร้าง DbSet เมื่อคุณไม่มีตรรกะอื่นที่จะใช้
public class UniContext : DbContext {
public UniContext() : base("UniContext") { }
public DbSet<Student> Students { get; set; }
public DbSet<Enrollment> Enrollments { get; set; }
public DbSet<Course> Courses { get; set; }
}
ก่อนหน้านี้ EDM ใช้เพื่อสร้างคลาสบริบทที่ได้มาจากคลาส ObjectContext
การทำงานกับ ObjectContext มีความซับซ้อนเล็กน้อย
DbContext เป็น wrapper รอบ ๆ ObjectContext ซึ่งจริงๆแล้วคล้ายกับ ObjectContext และมีประโยชน์และง่ายในรูปแบบการพัฒนาทั้งหมดเช่น Code First, Model First และ Database First
แบบสอบถาม
มีคำค้นหาสามประเภทที่คุณสามารถใช้ได้เช่น -
- การเพิ่มเอนทิตีใหม่
- การเปลี่ยนแปลงหรืออัปเดตค่าคุณสมบัติของเอนทิตีที่มีอยู่
- การลบเอนทิตีที่มีอยู่
การเพิ่มเอนทิตีใหม่
การเพิ่มอ็อบเจ็กต์ใหม่ด้วย Entity Framework ทำได้ง่ายเพียงแค่สร้างอินสแตนซ์ใหม่ของอ็อบเจ็กต์ของคุณและลงทะเบียนโดยใช้เมธอด Add บน DbSet รหัสต่อไปนี้ใช้สำหรับเมื่อคุณต้องการเพิ่มนักเรียนใหม่ในฐานข้อมูล
private static void AddStudent() {
using (var context = new UniContext()) {
var student = new Student {
LastName = "Khan",
FirstMidName = "Ali",
EnrollmentDate = DateTime.Parse("2005-09-01")
};
context.Students.Add(student);
context.SaveChanges();
}
}
การเปลี่ยนเอนทิตีที่มีอยู่
การเปลี่ยนออบเจ็กต์ที่มีอยู่ทำได้ง่ายเพียงแค่อัปเดตค่าที่กำหนดให้กับคุณสมบัติที่คุณต้องการเปลี่ยนแปลงและเรียกใช้ SaveChanges ในรหัสต่อไปนี้นามสกุลของ Ali เปลี่ยนจาก Khan เป็น Aslam
private static void AddStudent() {
private static void ChangeStudent() {
using (var context = new UniContext()) {
var student = (from d in context.Students
where d.FirstMidName == "Ali" select d).Single();
student.LastName = "Aslam";
context.SaveChanges();
}
}
}
การลบเอนทิตีที่มีอยู่
ในการลบเอนทิตีโดยใช้ Entity Framework คุณใช้เมธอด Remove บน DbSet ลบงานสำหรับทั้งเอนทิตีที่มีอยู่และเพิ่มใหม่ การเรียกลบในเอนทิตีที่ถูกเพิ่ม แต่ยังไม่ได้บันทึกลงในฐานข้อมูลจะยกเลิกการเพิ่มเอนทิตี เอนทิตีจะถูกลบออกจากตัวติดตามการเปลี่ยนแปลงและไม่ถูกติดตามโดย DbContext อีกต่อไป การเรียกลบในเอนทิตีที่มีอยู่ซึ่งกำลังถูกติดตามการเปลี่ยนแปลงจะลงทะเบียนเอนทิตีสำหรับการลบในครั้งถัดไปที่เรียก SaveChanges ตัวอย่างต่อไปนี้แสดงกรณีที่นักเรียนถูกลบออกจากฐานข้อมูลที่มีชื่อแรกคือ Ali
private static void DeleteStudent() {
using (var context = new UniContext()) {
var bay = (from d in context.Students where d.FirstMidName == "Ali" select d).Single();
context.Students.Remove(bay);
context.SaveChanges();
}
}
ใน Entity Framework มีเอนทิตีสองประเภทที่อนุญาตให้นักพัฒนาใช้คลาสข้อมูลที่กำหนดเองร่วมกับโมเดลข้อมูลโดยไม่ต้องทำการปรับเปลี่ยนคลาสข้อมูลด้วยตนเอง
- หน่วยงาน POCO
- พร็อกซีแบบไดนามิก
หน่วยงาน POCO
POCO ย่อมาจากอ็อบเจ็กต์ CLR "ธรรมดา" ซึ่งสามารถใช้เป็นอ็อบเจ็กต์โดเมนที่มีอยู่กับโมเดลข้อมูลของคุณ
คลาสข้อมูล POCO ที่แมปกับเอนทิตีถูกกำหนดไว้ในแบบจำลองข้อมูล
นอกจากนี้ยังสนับสนุนแบบสอบถามแทรกอัปเดตและลบลักษณะการทำงานส่วนใหญ่เป็นประเภทเอนทิตีที่สร้างโดยเครื่องมือตัวแบบข้อมูลเอนทิตี
คุณสามารถใช้เทมเพลต POCO เพื่อสร้างประเภทเอนทิตีที่คงอยู่โดยไม่รู้ตัวจากแบบจำลองแนวคิด
ลองดูตัวอย่าง Conceptual Entity Data Model ต่อไปนี้
ในการสร้างเอนทิตี POCO สำหรับโมเดลเอนทิตีด้านบน -
Step 1- คลิกขวาที่หน้าต่างนักออกแบบ จะแสดงกล่องโต้ตอบต่อไปนี้
Step 2 - เลือกรายการเพิ่มรหัสสร้าง ...
Step 3 - เลือก EF 6.x DbContext Generator เขียนชื่อจากนั้นคลิกปุ่ม Add
คุณจะเห็นในตัวสำรวจโซลูชันของคุณว่ามีการสร้างเทมเพลต POCODemo.Context.tt และ POCODemo.tt
POCODemo.Context สร้าง DbContext และชุดออบเจ็กต์ที่คุณสามารถส่งคืนและใช้สำหรับการสืบค้นพูดสำหรับบริบทนักเรียนและหลักสูตร ฯลฯ
แม่แบบอื่น ๆ เกี่ยวข้องกับประเภทนักเรียนหลักสูตรและอื่น ๆ ทั้งหมดต่อไปนี้เป็นรหัสสำหรับชั้นเรียนนักเรียนซึ่งสร้างขึ้นโดยอัตโนมัติจากแบบจำลองเอนทิตี
namespace ConsoleApplication1 {
using System;
using System.Collections.Generic;
public partial class Student {
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage",
"CA2214:DoNotCallOverridableMethodsInConstructors")]
public Student() {
this.Enrollments = new HashSet<Enrollment>();
}
public int ID { get; set; }
public string LastName { get; set; }
public string FirstMidName { get; set; }
public System.DateTime EnrollmentDate { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage",
CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<Enrollment> Enrollments { get; set; }
}
}
ชั้นเรียนที่คล้ายกันถูกสร้างขึ้นสำหรับตารางหลักสูตรและการลงทะเบียนจากโมเดลเอนทิตี
พร็อกซีแบบไดนามิก
เมื่อสร้างอินสแตนซ์ของประเภทเอนทิตี POCO Entity Framework มักจะสร้างอินสแตนซ์ของชนิดที่ได้รับที่สร้างขึ้นแบบไดนามิกซึ่งทำหน้าที่เป็นพร็อกซีสำหรับเอนทิตี IT ยังสามารถกล่าวได้ว่าเป็นคลาสพร็อกซีรันไทม์เช่นคลาส wrapper ของเอนทิตี POCO
คุณสามารถแทนที่คุณสมบัติบางอย่างของเอนทิตีเพื่อดำเนินการโดยอัตโนมัติเมื่อมีการเข้าถึงคุณสมบัติ
กลไกนี้ใช้เพื่อสนับสนุนการโหลดความสัมพันธ์แบบเกียจคร้านและการติดตามการเปลี่ยนแปลงอัตโนมัติ
เทคนิคนี้ยังใช้กับโมเดลที่สร้างด้วย Code First และ EF Designer
หากคุณต้องการให้ Entity Framework รองรับการโหลดอ็อบเจ็กต์ที่เกี่ยวข้องและติดตามการเปลี่ยนแปลงในคลาส POCO คลาส POCO จะต้องเป็นไปตามข้อกำหนดต่อไปนี้ -
ต้องประกาศคลาสข้อมูลที่กำหนดเองด้วยการเข้าถึงแบบสาธารณะ
ต้องไม่ปิดผนึกคลาสข้อมูลที่กำหนดเอง
คลาสข้อมูลที่กำหนดเองต้องไม่เป็นนามธรรม
คลาสข้อมูลที่กำหนดเองต้องมีตัวสร้างสาธารณะหรือที่ได้รับการป้องกันที่ไม่มีพารามิเตอร์
ใช้ตัวสร้างที่ได้รับการป้องกันโดยไม่มีพารามิเตอร์หากคุณต้องการใช้เมธอด CreateObject เพื่อสร้างพร็อกซีสำหรับเอนทิตี POCO
การเรียกใช้เมธอด CreateObject ไม่รับประกันการสร้างพร็อกซี: คลาส POCO ต้องเป็นไปตามข้อกำหนดอื่น ๆ ที่อธิบายไว้ในหัวข้อนี้
คลาสไม่สามารถใช้อินเตอร์เฟส IEntityWithChangeTracker หรือ IEntityWithRelationships ได้เนื่องจากคลาสพร็อกซีใช้อินเทอร์เฟซเหล่านี้
ต้องตั้งค่าตัวเลือก ProxyCreationEnabled เป็น true
ตัวอย่างต่อไปนี้เป็นคลาสเอนทิตีพร็อกซีแบบไดนามิก
public partial class Course {
public Course() {
this.Enrollments = new HashSet<Enrollment>();
}
public int CourseID { get; set; }
public string Title { get; set; }
public int Credits { get; set; }
public virtual ICollection<Enrollment> Enrollments { get; set; }
}
หากต้องการปิดใช้งานการสร้างวัตถุพร็อกซีให้ตั้งค่าคุณสมบัติ ProxyCreationEnabled เป็น false
ในฐานข้อมูลเชิงสัมพันธ์ความสัมพันธ์คือสถานการณ์ที่มีอยู่ระหว่างตารางฐานข้อมูลเชิงสัมพันธ์ผ่านคีย์ต่างประเทศ Foreign Key (FK) คือคอลัมน์หรือชุดของคอลัมน์ที่ใช้เพื่อสร้างและบังคับใช้การเชื่อมโยงระหว่างข้อมูลในสองตาราง แผนภาพต่อไปนี้ประกอบด้วยสามตาราง
- Student
- Course
- Enrollment
ในแผนภาพด้านบนคุณสามารถเห็นการเชื่อมโยง / ความสัมพันธ์ระหว่างตาราง มีความสัมพันธ์สามประเภทระหว่างตารางและความสัมพันธ์ระหว่างตารางต่างๆขึ้นอยู่กับวิธีกำหนดคอลัมน์ที่เกี่ยวข้อง
- ความสัมพันธ์แบบหนึ่งต่อกลุ่ม
- ความสัมพันธ์แบบกลุ่มต่อกลุ่ม
- ความสัมพันธ์แบบหนึ่งต่อหนึ่ง
ความสัมพันธ์แบบหนึ่งต่อกลุ่ม
ความสัมพันธ์แบบหนึ่งต่อกลุ่มเป็นประเภทของความสัมพันธ์ที่พบบ่อยที่สุด
ในความสัมพันธ์ประเภทนี้แถวในตาราง A สามารถมีแถวที่ตรงกันได้หลายแถวในตาราง B แต่แถวในตาราง B สามารถมีแถวที่ตรงกันเพียงแถวเดียวในตาราง A
คีย์ต่างประเทศถูกกำหนดไว้ในตารางที่แสดงถึงจุดสิ้นสุดของความสัมพันธ์
ตัวอย่างเช่นในแผนภาพด้านบนตารางนักเรียนและการลงทะเบียนมีความสัมพันธ์แบบเดียวกันนักเรียนแต่ละคนอาจมีการลงทะเบียนหลายครั้ง แต่การลงทะเบียนแต่ละครั้งเป็นของนักเรียนเพียงคนเดียว
ในกรอบงานเอนทิตีสามารถสร้างความสัมพันธ์เหล่านี้ด้วยรหัสได้เช่นกัน ต่อไปนี้เป็นตัวอย่างของชั้นเรียนนักเรียนและการลงทะเบียนที่เชื่อมโยงกับความสัมพันธ์แบบหนึ่งต่อหลาย
public class Student {
public int ID { get; set; }
public string LastName { get; set; }
public string FirstMidName { get; set; }
public DateTime EnrollmentDate { get; set; }
public virtual ICollection<Enrollment> Enrollments { get; set; }
}
public class Enrollment {
public int EnrollmentID { get; set; }
public int CourseID { get; set; }
public int StudentID { get; set; }
public Grade? Grade { get; set; }
public virtual Course Course { get; set; }
public virtual Student Student { get; set; }
}
ในโค้ดด้านบนคุณจะเห็นว่าคลาสนักเรียนมีคอลเล็กชันการลงทะเบียน แต่คลาสการลงทะเบียนมี Student Object เดียว
ความสัมพันธ์แบบกลุ่มต่อกลุ่ม
ในความสัมพันธ์แบบกลุ่มต่อกลุ่มแถวในตาราง A สามารถมีแถวที่ตรงกันได้หลายแถวในตาราง B และในทางกลับกัน
คุณสามารถสร้างความสัมพันธ์ดังกล่าวได้โดยการกำหนดตารางที่สามเรียกว่าตารางทางแยกซึ่งคีย์หลักประกอบด้วยคีย์ต่างประเทศจากทั้งตาราง A และตาราง B
ตัวอย่างเช่นตารางนักเรียนและหลักสูตรมีความสัมพันธ์แบบกลุ่มต่อกลุ่มที่กำหนดโดยความสัมพันธ์แบบหนึ่งต่อกลุ่มจากแต่ละตารางเหล่านี้ไปยังตารางการลงทะเบียน
รหัสต่อไปนี้ประกอบด้วยชั้นเรียนหลักสูตรและสองชั้นเรียนข้างต้นกล่าวคือ Student และ Enrollment.
public class Course {
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int CourseID { get; set; }
public string Title { get; set; }
public int Credits { get; set; }
public virtual ICollection<Enrollment> Enrollments { get; set; }
}
คุณจะเห็นได้ว่าทั้งคลาสหลักสูตรและคลาสนักเรียนมีคอลเล็กชันของอ็อบเจ็กต์การลงทะเบียนซึ่งสร้างความสัมพันธ์แบบกลุ่มต่อกลุ่มผ่านการลงทะเบียนคลาสทางแยก
ความสัมพันธ์แบบหนึ่งต่อหนึ่ง
ในความสัมพันธ์แบบหนึ่งต่อหนึ่งแถวในตาราง A สามารถมีแถวที่ตรงกันได้ไม่เกินหนึ่งแถวในตาราง B และในทางกลับกัน
ความสัมพันธ์แบบหนึ่งต่อหนึ่งจะถูกสร้างขึ้นหากทั้งสองคอลัมน์ที่เกี่ยวข้องเป็นคีย์หลักหรือมีข้อ จำกัด เฉพาะ
ในความสัมพันธ์แบบหนึ่งต่อกลุ่มคีย์หลักจะทำหน้าที่เป็นคีย์ต่างประเทศเพิ่มเติมและไม่มีคอลัมน์คีย์ต่างประเทศแยกสำหรับตารางใดตารางหนึ่ง
ความสัมพันธ์ประเภทนี้ไม่เกิดขึ้นบ่อยนักเนื่องจากข้อมูลส่วนใหญ่ที่เกี่ยวข้องในลักษณะนี้จะรวมอยู่ในตารางเดียว คุณอาจใช้ความสัมพันธ์แบบหนึ่งต่อกลุ่มเพื่อ -
- แบ่งตารางที่มีหลายคอลัมน์
- แยกส่วนของตารางด้วยเหตุผลด้านความปลอดภัย
- จัดเก็บข้อมูลที่มีอายุการใช้งานสั้นและสามารถลบได้อย่างง่ายดายเพียงแค่ลบตาราง
- จัดเก็บข้อมูลที่ใช้กับส่วนย่อยของตารางหลักเท่านั้น
รหัสต่อไปนี้คือการเพิ่มชื่อคลาส StudentProfile อื่นซึ่งมีรหัสอีเมลและรหัสผ่านของนักเรียน
public class Student {
public int ID { get; set; }
public string LastName { get; set; }
public string FirstMidName { get; set; }
public DateTime EnrollmentDate { get; set; }
public virtual ICollection<Enrollment> Enrollments { get; set; }
public virtual StudentProfile StudentProfile { get; set; }
}
public class StudentProfile {
public StudentProfile() {}
public int ID { get; set; }
public string Email { get; set; }
public string Password { get; set; }
public virtual Student Student { get; set; }
}
คุณสามารถดูได้ว่าคลาสเอนทิตี Student มีคุณสมบัติการนำทาง StudentProfile และ StudentProfile มีคุณสมบัติการนำทางของนักเรียน
นักเรียนแต่ละคนมีอีเมลและรหัสผ่านเพียงชุดเดียวเพื่อเข้าสู่ระบบในโดเมนของมหาวิทยาลัย ข้อมูลเหล่านี้สามารถเพิ่มลงในตารางนักเรียนได้ แต่เพื่อความปลอดภัยจะถูกแยกไปยังตารางอื่น
อายุการใช้งาน
อายุการใช้งานของบริบทเริ่มต้นเมื่ออินสแตนซ์ถูกสร้างขึ้นและสิ้นสุดลงเมื่ออินสแตนซ์ถูกกำจัดหรือรวบรวมขยะ
อายุการใช้งานตามบริบทเป็นการตัดสินใจที่สำคัญมากเมื่อเราใช้ ORM
บริบททำงานเหมือนเอนทิตีแคชดังนั้นจึงหมายถึงมีการอ้างอิงถึงเอนทิตีที่โหลดทั้งหมดซึ่งอาจเติบโตเร็วมากในการใช้หน่วยความจำและยังทำให้หน่วยความจำรั่วไหล
ในแผนภาพด้านล่างคุณสามารถดูระดับบนของเวิร์กโฟลว์ข้อมูลจากแอปพลิเคชันไปยังฐานข้อมูลผ่านทางบริบทและในทางกลับกัน
วงจรชีวิตของเอนทิตี
Entity Lifecycle อธิบายกระบวนการที่เอนทิตีถูกสร้างเพิ่มแก้ไขลบ ฯลฯ เอนทิตีมีหลายสถานะในช่วงอายุการใช้งาน ก่อนที่จะดูวิธีการดึงสถานะเอนทิตีมาดูกันว่าสถานะเอนทิตีคืออะไร สถานะเป็น enum ประเภทSystem.Data.EntityState ที่ประกาศค่าต่อไปนี้ -
Added: นิติบุคคลถูกทำเครื่องหมายว่าเพิ่มแล้ว
Deleted: นิติบุคคลถูกทำเครื่องหมายว่าลบแล้ว
Modified: มีการแก้ไขเอนทิตี
Unchanged: ไม่ได้แก้ไขเอนทิตี
Detached: ไม่ได้ติดตามเอนทิตี
การเปลี่ยนแปลงสถานะในวงจรชีวิตของเอนทิตี
บางครั้งสถานะของเอนทิตีจะถูกกำหนดโดยบริบทโดยอัตโนมัติ แต่นักพัฒนายังสามารถแก้ไขได้ด้วยตนเอง แม้ว่าการรวมกันทั้งหมดของสวิตช์จากสถานะหนึ่งไปยังอีกสถานะหนึ่งจะเป็นไปได้ แต่บางส่วนก็ไม่มีความหมาย ตัวอย่างเช่น,Added นิติบุคคลของ Deleted รัฐหรือในทางกลับกัน
ขอหารือเกี่ยวกับสถานะต่างๆ
สถานะไม่เปลี่ยนแปลง
เมื่อเอนทิตีไม่มีการเปลี่ยนแปลงจะถูกผูกไว้กับบริบท แต่ไม่ได้รับการแก้ไข
โดยค่าเริ่มต้นเอนทิตีที่ดึงมาจากฐานข้อมูลจะอยู่ในสถานะนี้
เมื่อเอนทิตีแนบกับบริบท (ด้วยวิธีการแนบ) เอนทิตีจะอยู่ในสถานะไม่เปลี่ยนแปลงในทำนองเดียวกัน
บริบทไม่สามารถติดตามการเปลี่ยนแปลงของออบเจ็กต์ที่ไม่ได้อ้างอิงดังนั้นเมื่อแนบมาแล้วจะถือว่าไม่มีการเปลี่ยนแปลง
สถานะแยก
แยกออกเป็นสถานะเริ่มต้นของเอนทิตีที่สร้างขึ้นใหม่เนื่องจากบริบทไม่สามารถติดตามการสร้างวัตถุใด ๆ ในโค้ดของคุณ
สิ่งนี้เป็นจริงแม้ว่าคุณจะสร้างอินสแตนซ์เอนทิตีภายในบล็อกโดยใช้บริบท
แยกออกแม้กระทั่งสถานะของเอนทิตีที่ดึงมาจากฐานข้อมูลเมื่อปิดใช้งานการติดตาม
เมื่อเอนทิตีถูกแยกออกจะไม่ถูกผูกไว้กับบริบทดังนั้นสถานะจึงไม่ถูกติดตาม
สามารถกำจัดแก้ไขใช้ร่วมกับคลาสอื่น ๆ หรือใช้ในลักษณะอื่นที่คุณอาจต้องการ
เนื่องจากไม่มีการติดตามบริบทจึงไม่มีความหมายกับ Entity Framework
เพิ่มรัฐ
เมื่อเอนทิตีอยู่ในสถานะเพิ่มคุณมีตัวเลือกน้อย ในความเป็นจริงคุณสามารถแยกออกจากบริบทเท่านั้น
โดยปกติแล้วแม้ว่าคุณจะปรับเปลี่ยนคุณสมบัติบางอย่างสถานะจะยังคงถูกเพิ่มเนื่องจากการย้ายไปที่ Modified, Unchanged หรือ Deleted นั้นไม่มีเหตุผล
เป็นเอนทิตีใหม่และไม่มีความสอดคล้องกับแถวในฐานข้อมูล
นี่เป็นข้อกำหนดเบื้องต้นพื้นฐานสำหรับการอยู่ในสถานะใดสถานะหนึ่ง (แต่บริบทนี้ไม่ได้บังคับใช้)
สถานะที่แก้ไข
เมื่อเอนทิตีถูกแก้ไขนั่นหมายความว่าอยู่ในสถานะไม่เปลี่ยนแปลงจากนั้นคุณสมบัติบางอย่างก็เปลี่ยนไป
หลังจากที่เอนทิตีเข้าสู่สถานะ Modified เอนทิตีสามารถย้ายไปยังสถานะ Detached หรือ Deleted ได้ แต่จะไม่สามารถย้อนกลับไปยังสถานะไม่เปลี่ยนแปลงแม้ว่าคุณจะกู้คืนค่าดั้งเดิมด้วยตนเองก็ตาม
ไม่สามารถเปลี่ยนเป็นเพิ่มได้เว้นแต่คุณจะแยกและเพิ่มเอนทิตีลงในบริบทเนื่องจากมีแถวที่มี ID นี้อยู่แล้วในฐานข้อมูลและคุณจะได้รับข้อยกเว้นรันไทม์เมื่อยังคงมีอยู่
สถานะที่ถูกลบ
เอนทิตีเข้าสู่สถานะที่ถูกลบเนื่องจากไม่มีการเปลี่ยนแปลงหรือแก้ไขจากนั้นจึงใช้เมธอด DeleteObject
นี่เป็นสถานะที่ จำกัด มากที่สุดเนื่องจากไม่มีจุดหมายเปลี่ยนจากสถานะนี้เป็นค่าอื่น แต่แยกออก
usingคำสั่งหากคุณต้องการให้ทรัพยากรทั้งหมดที่บริบทควบคุมถูกกำจัดเมื่อสิ้นสุดบล็อก เมื่อคุณใช้using จากนั้นคอมไพเลอร์จะสร้าง try / ปิดกั้นโดยอัตโนมัติและเรียกว่า dispose ในบล็อกสุดท้าย
using (var context = new UniContext()) {
var student = new Student {
LastName = "Khan",
FirstMidName = "Ali",
EnrollmentDate = DateTime.Parse("2005-09-01")
};
context.Students.Add(student);
context.SaveChanges();
}
เมื่อทำงานกับบริบทที่ใช้งานมายาวนานให้พิจารณาสิ่งต่อไปนี้ -
เมื่อคุณโหลดอ็อบเจ็กต์และการอ้างอิงลงในหน่วยความจำมากขึ้นการใช้หน่วยความจำของบริบทอาจเพิ่มขึ้นอย่างรวดเร็ว ซึ่งอาจทำให้เกิดปัญหาด้านประสิทธิภาพ
อย่าลืมทิ้งบริบทเมื่อไม่จำเป็นต้องใช้อีกต่อไป
หากข้อยกเว้นทำให้บริบทอยู่ในสถานะที่ไม่สามารถกู้คืนได้แอปพลิเคชันทั้งหมดอาจยุติลง
โอกาสที่จะพบปัญหาที่เกี่ยวข้องกับภาวะพร้อมกันจะเพิ่มขึ้นเมื่อช่องว่างระหว่างเวลาที่ข้อมูลถูกสอบถามและอัปเดตเพิ่มขึ้น
เมื่อทำงานกับเว็บแอปพลิเคชันให้ใช้อินสแตนซ์บริบทต่อคำขอ
เมื่อทำงานกับ Windows Presentation Foundation (WPF) หรือ Windows Forms ให้ใช้อินสแตนซ์บริบทต่อฟอร์ม ซึ่งช่วยให้คุณใช้ฟังก์ชันการติดตามการเปลี่ยนแปลงที่บริบทมีให้
กฎของ Thumb
Web Applications
ตอนนี้ถือเป็นแนวทางปฏิบัติทั่วไปและเป็นแนวทางปฏิบัติที่ดีที่สุดสำหรับการใช้งานเว็บบริบทจะถูกใช้ต่อคำขอ
ในเว็บแอปพลิเคชันเราจัดการกับคำขอที่สั้นมาก แต่มีธุรกรรมเซิร์ฟเวอร์ทั้งหมดดังนั้นจึงเป็นระยะเวลาที่เหมาะสมสำหรับบริบทที่จะดำเนินการ
Desktop Applications
สำหรับแอปพลิเคชันเดสก์ท็อปเช่น Win Forms / WPF เป็นต้นบริบทจะถูกใช้ต่อแบบฟอร์ม / โต้ตอบ / เพจ
เนื่องจากเราไม่ต้องการให้บริบทเป็นซิงเกิลตันสำหรับแอปพลิเคชันของเราเราจะกำจัดทิ้งเมื่อเราย้ายจากรูปแบบหนึ่งไปยังอีกรูปแบบหนึ่ง
ด้วยวิธีนี้เราจะได้รับความสามารถมากมายของบริบทและจะไม่ต้องทนทุกข์ทรมานจากผลกระทบของบริบทที่ใช้งานมายาวนาน
Entity Framework มีสามแนวทางในการสร้างแบบจำลองเอนทิตีและแต่ละวิธีมีข้อดีและข้อเสียของตนเอง
- รหัสแรก
- ฐานข้อมูลก่อน
- รุ่นแรก
ในบทนี้เราจะอธิบายสั้น ๆ เกี่ยวกับแนวทางแรกของรหัส นักพัฒนาบางคนชอบที่จะทำงานร่วมกับ Designer ใน Code ในขณะที่บางคนต้องการทำงานกับ Code ของตนเท่านั้น สำหรับนักพัฒนาเหล่านั้น Entity Framework มีเวิร์กโฟลว์การสร้างแบบจำลองที่เรียกว่า Code First
เวิร์กโฟลว์การสร้างโมเดล Code First กำหนดเป้าหมายฐานข้อมูลที่ไม่มีอยู่และ Code First จะสร้างขึ้น
นอกจากนี้ยังสามารถใช้หากคุณมีฐานข้อมูลว่างจากนั้น Code First จะเพิ่มตารางใหม่ด้วย
Code First ช่วยให้คุณกำหนดโมเดลของคุณโดยใช้คลาส C # หรือ VB.Net
การกำหนดค่าเพิ่มเติมสามารถเลือกดำเนินการได้โดยใช้แอตทริบิวต์ในคลาสและคุณสมบัติของคุณหรือใช้ API ที่คล่องแคล่ว
ทำไมต้องเป็นรหัสแรก
Code First สร้างขึ้นจากชุดตัวต่อ อันดับแรกคือคลาสโดเมนของคุณ
คลาสโดเมนไม่มีส่วนเกี่ยวข้องกับ Entity Framework เป็นเพียงรายการในโดเมนธุรกิจของคุณ
ดังนั้น Entity Framework จะมีบริบทที่จัดการปฏิสัมพันธ์ระหว่างคลาสเหล่านั้นกับฐานข้อมูลของคุณ
บริบทไม่เฉพาะเจาะจงสำหรับ Code First เป็นคุณสมบัติของ Entity Framework
Code First เพิ่มตัวสร้างโมเดลที่ตรวจสอบคลาสของคุณว่าบริบทกำลังจัดการจากนั้นใช้ชุดของกฎหรืออนุสัญญาเพื่อกำหนดว่าคลาสเหล่านั้นและความสัมพันธ์อธิบายโมเดลอย่างไรและโมเดลนั้นควรแมปกับฐานข้อมูลของคุณอย่างไร
ทั้งหมดนี้เกิดขึ้นที่รันไทม์ คุณจะไม่เห็นโมเดลนี้ แต่อยู่ในความทรงจำ
Code First มีความสามารถในการใช้โมเดลนั้นเพื่อสร้างฐานข้อมูลหากต้องการ
นอกจากนี้ยังสามารถอัปเดตฐานข้อมูลหากโมเดลเปลี่ยนแปลงโดยใช้คุณลักษณะที่เรียกว่า Code First Migrations
ในบทนี้ให้เราเรียนรู้วิธีการสร้างโมเดลข้อมูลเอนทิตีในตัวออกแบบโดยใช้เวิร์กโฟลว์ที่เรียกว่า Model First
Model First เหมาะอย่างยิ่งสำหรับเมื่อคุณเริ่มโครงการใหม่ที่ยังไม่มีฐานข้อมูล
โมเดลถูกเก็บไว้ในไฟล์ EDMX และสามารถดูและแก้ไขได้ใน Entity Framework Designer
ใน Model First คุณกำหนดโมเดลของคุณในตัวออกแบบ Entity Framework จากนั้นสร้าง SQL ซึ่งจะสร้างสคีมาฐานข้อมูลให้ตรงกับโมเดลของคุณจากนั้นคุณดำเนินการ SQL เพื่อสร้างสคีมาในฐานข้อมูลของคุณ
คลาสที่คุณโต้ตอบด้วยในแอปพลิเคชันของคุณจะสร้างขึ้นโดยอัตโนมัติจากไฟล์ EDMX
ต่อไปนี้เป็นตัวอย่างง่ายๆของการสร้างโครงการคอนโซลใหม่โดยใช้แนวทาง Model First
Step 1 - เปิด Visual Studio แล้วเลือก File → New → Project
Step 2 - เลือกติดตั้ง→เทมเพลต→ Visual C # → Windows จากบานหน้าต่างด้านซ้ายจากนั้นในบานหน้าต่างตรงกลางให้เลือกแอปพลิเคชันคอนโซล
Step 3 - ป้อน EFModelFirstDemo ในฟิลด์ชื่อ
Step 4 - ในการสร้างโมเดลให้คลิกขวาที่โปรเจ็กต์คอนโซลของคุณก่อนในตัวสำรวจโซลูชันแล้วเลือกเพิ่ม→รายการใหม่ ...
กล่องโต้ตอบต่อไปนี้จะเปิดขึ้น
Step 5 - เลือก ADO.NET Entity Data Model จากบานหน้าต่างตรงกลางและป้อนชื่อ ModelFirstDemoDB ในฟิลด์ Name
Step 6 - คลิกปุ่มเพิ่มซึ่งจะเปิดกล่องโต้ตอบตัวช่วยสร้างโมเดลข้อมูลเอนทิตี
Step 7- เลือก Empty EF Designer model แล้วคลิกปุ่ม Next Entity Framework Designer จะเปิดขึ้นด้วยโมเดลเปล่า ตอนนี้เราสามารถเริ่มเพิ่มเอนทิตีคุณสมบัติและการเชื่อมโยงให้กับโมเดลได้
Step 8- คลิกขวาบนพื้นผิวการออกแบบและเลือกคุณสมบัติ ในหน้าต่างคุณสมบัติเปลี่ยนชื่อคอนเทนเนอร์เอนทิตีเป็น ModelFirstDemoDBContext
Step 9 - คลิกขวาที่พื้นผิวการออกแบบแล้วเลือกเพิ่มใหม่→เอนทิตี ...
กล่องโต้ตอบเพิ่มเอนทิตีจะเปิดขึ้นดังที่แสดงในภาพต่อไปนี้
Step 10 - ป้อน Student เป็นชื่อนิติบุคคลและ Student Id เป็นชื่อคุณสมบัติแล้วคลิก Ok
Step 11 - คลิกขวาที่เอนทิตีใหม่บนพื้นผิวการออกแบบและเลือกเพิ่มใหม่→คุณสมบัติสเกลาร์ป้อนชื่อเป็นชื่อของคุณสมบัติ
Step 12 - ป้อน FirstName จากนั้นเพิ่มคุณสมบัติสเกลาร์อีกสองคุณสมบัติเช่น LastName และ EnrollmentDate
Step 13 - เพิ่มหลักสูตรเอนทิตีและการลงทะเบียนอีกสองรายการโดยทำตามขั้นตอนทั้งหมดที่กล่าวข้างต้นและเพิ่มคุณสมบัติสเกลาร์ตามที่แสดงในขั้นตอนต่อไปนี้
Step 14 - เรามีสามเอนทิตีใน Visual Designer มาเพิ่มการเชื่อมโยงหรือความสัมพันธ์ระหว่างกัน
Step 15 - คลิกขวาที่พื้นผิวการออกแบบแล้วเลือกเพิ่มใหม่→การเชื่อมโยง ...
Step 16 - ทำให้ปลายด้านหนึ่งของความสัมพันธ์ชี้ไปที่นักเรียนด้วยความหลายหลากของหนึ่งและอีกจุดสิ้นสุดไปที่การลงทะเบียนที่มีหลายหลากของจำนวนมาก
Step 17 - หมายความว่านักเรียนมีการลงทะเบียนและการลงทะเบียนจำนวนมากเป็นของนักเรียนหนึ่งคน
Step 18 - ตรวจสอบให้แน่ใจว่าได้เลือกช่องเพิ่มคุณสมบัติคีย์ต่างประเทศในเอนทิตี 'โพสต์' แล้วคลิกตกลง
Step 19 - ในทำนองเดียวกันให้เพิ่มความสัมพันธ์ระหว่างหลักสูตรและการลงทะเบียนอีกหนึ่งรายการ
Step 20 - โมเดลข้อมูลของคุณจะมีลักษณะเหมือนหน้าจอต่อไปนี้หลังจากเพิ่มการเชื่อมโยงระหว่างเอนทิตี
ตอนนี้เรามีโมเดลง่ายๆที่เราสามารถสร้างฐานข้อมูลและใช้ในการอ่านและเขียนข้อมูลได้ มาสร้างฐานข้อมูลกันเลย
Step 1 - คลิกขวาที่พื้นผิวการออกแบบและเลือกสร้างฐานข้อมูลจากแบบจำลอง ...
Step 2 - คุณสามารถเลือกฐานข้อมูลที่มีอยู่หรือสร้างการเชื่อมต่อใหม่โดยคลิกที่การเชื่อมต่อใหม่ ...
Step 3 - ในการสร้างฐานข้อมูลใหม่ให้คลิกที่การเชื่อมต่อใหม่ ...
Step 4 - ป้อนชื่อเซิร์ฟเวอร์และชื่อฐานข้อมูล
Step 5 - คลิกถัดไป
Step 6- คลิกเสร็จสิ้น สิ่งนี้จะเพิ่มไฟล์ * .edmx.sql ในโปรเจ็กต์ คุณสามารถรันสคริปต์ DDL ใน Visual Studio ได้โดยเปิดไฟล์. sql จากนั้นคลิกขวาและเลือก Execute
Step 7 - กล่องโต้ตอบต่อไปนี้จะปรากฏขึ้นเพื่อเชื่อมต่อกับฐานข้อมูล
Step 8 - เมื่อดำเนินการสำเร็จคุณจะเห็นข้อความต่อไปนี้
Step 9 - ไปที่ server explorer คุณจะเห็นว่าฐานข้อมูลถูกสร้างขึ้นด้วยตารางสามตารางที่ระบุไว้
ต่อไปเราต้องสลับโมเดลของเราเพื่อสร้างโค้ดที่ใช้ DbContext API
Step 1 - คลิกขวาที่จุดว่างของโมเดลของคุณใน EF Designer แล้วเลือก Add Code Generation Item ...
คุณจะเห็นว่ากล่องโต้ตอบเพิ่มรายการใหม่ต่อไปนี้เปิดขึ้น
Step 2 - เลือก EF 6.x DbContext Generator ในบานหน้าต่างตรงกลางและป้อน ModelFirstDemoModel ในฟิลด์ Name
Step 3 - คุณจะเห็นในตัวสำรวจโซลูชันของคุณว่าเทมเพลต ModelFirstDemoModel.Context.tt และ ModelFirstDemoModel.tt ถูกสร้างขึ้น
ModelFirstDemoModel.Context สร้าง DbCcontext และชุดออบเจ็กต์ที่คุณสามารถส่งคืนและใช้สำหรับการสอบถามพูดสำหรับบริบทนักเรียนและหลักสูตรเป็นต้น
แม่แบบอื่น ๆ เกี่ยวข้องกับทุกประเภทนักเรียนหลักสูตรและอื่น ๆ ต่อไปนี้คือชั้นเรียนนักเรียนซึ่งสร้างขึ้นโดยอัตโนมัติจากแบบจำลองเอนทิตี
ต่อไปนี้เป็นรหัส C # ซึ่งข้อมูลบางส่วนจะถูกป้อนและดึงมาจากฐานข้อมูล
using System;
using System.Linq;
namespace EFModelFirstDemo {
class Program {
static void Main(string[] args) {
using (var db = new ModelFirstDemoDBContext()) {
// Create and save a new Student
Console.Write("Enter a name for a new Student: ");
var firstName = Console.ReadLine();
var student = new Student {
StudentID = 1,
FirstName = firstName
};
db.Students.Add(student);
db.SaveChanges();
var query = from b in db.Students
orderby b.FirstName select b;
Console.WriteLine("All student in the database:");
foreach (var item in query) {
Console.WriteLine(item.FirstName);
}
Console.WriteLine("Press any key to exit...");
Console.ReadKey();
}
}
}
}
เมื่อดำเนินการโค้ดด้านบนคุณจะได้รับผลลัพธ์ต่อไปนี้ -
Enter a name for a new Student:
Ali Khan
All student in the database:
Ali Khan
Press any key to exit...
เราขอแนะนำให้คุณดำเนินการตามตัวอย่างข้างต้นในลักษณะทีละขั้นตอนเพื่อความเข้าใจที่ดีขึ้น
ในบทนี้ให้เราเรียนรู้เกี่ยวกับการสร้างแบบจำลองข้อมูลเอนทิตีด้วยวิธี Database First
แนวทางแรกของฐานข้อมูลเป็นทางเลือกสำหรับแนวทางของ Code First และ Model First สำหรับโมเดลข้อมูลเอนทิตี สร้างรหัสโมเดล (คลาสคุณสมบัติ DbContext ฯลฯ ) จากฐานข้อมูลในโครงการและคลาสเหล่านั้นจะกลายเป็นลิงก์ระหว่างฐานข้อมูลและคอนโทรลเลอร์
Database First Approach สร้างกรอบเอนทิตีจากฐานข้อมูลที่มีอยู่ เราใช้ฟังก์ชันอื่น ๆ ทั้งหมดเช่นการซิงค์โมเดล / ฐานข้อมูลและการสร้างรหัสในลักษณะเดียวกับที่เราใช้ในแนวทาง Model First
ลองยกตัวอย่างง่ายๆ เรามีฐานข้อมูลอยู่แล้วซึ่งมี 3 ตารางดังที่แสดงในภาพต่อไปนี้
Step 1 - มาสร้างโปรเจ็กต์คอนโซลใหม่ด้วยชื่อ DatabaseFirstDemo
Step 2 - ในการสร้างโมเดลให้คลิกขวาที่โปรเจ็กต์คอนโซลของคุณในตัวสำรวจโซลูชันก่อนแล้วเลือกเพิ่ม→รายการใหม่ ...
Step 3 - เลือก ADO.NET Entity Data Model จากบานหน้าต่างตรงกลางและป้อนชื่อ DatabaseFirstModel ในฟิลด์ Name
Step 4 - คลิกปุ่มเพิ่มซึ่งจะเปิดกล่องโต้ตอบตัวช่วยสร้างโมเดลข้อมูลเอนทิตี
Step 5 - เลือก EF Designer จากฐานข้อมูลและคลิกปุ่มถัดไป
Step 6 - เลือกฐานข้อมูลที่มีอยู่แล้วคลิกถัดไป
Step 7 - เลือก Entity Framework 6.x แล้วคลิกถัดไป
Step 8 - เลือกตารางทั้งหมดมุมมองและขั้นตอนการจัดเก็บที่คุณต้องการรวมแล้วคลิกเสร็จสิ้น
คุณจะเห็นว่าโมเดลเอนทิตีและคลาส POCO ถูกสร้างขึ้นจากฐานข้อมูล
ตอนนี้ให้เราดึงนักเรียนทั้งหมดจากฐานข้อมูลโดยเขียนรหัสต่อไปนี้ในไฟล์ program.cs
using System;
using System.Linq;
namespace DatabaseFirstDemo {
class Program {
static void Main(string[] args) {
using (var db = new UniContextEntities()) {
var query = from b in db.Students
orderby b.FirstMidName select b;
Console.WriteLine("All All student in the database:");
foreach (var item in query) {
Console.WriteLine(item.FirstMidName +" "+ item.LastName);
}
Console.WriteLine("Press any key to exit...");
Console.ReadKey();
}
}
}
}
เมื่อโปรแกรมข้างต้นทำงานคุณจะได้รับผลลัพธ์ต่อไปนี้ -
All student in the database:
Ali Khan
Arturo finand
Bill Gates
Carson Alexander
Gytis Barzdukas
Laura Norman
Meredith Alonso
Nino Olivetto
Peggy Justice
Yan Li
Press any key to exit...
เมื่อโปรแกรมด้านบนถูกเรียกใช้งานคุณจะเห็นชื่อนักเรียนทั้งหมดที่เคยกรอกไว้ในฐานข้อมูล
เราขอแนะนำให้คุณดำเนินการตามตัวอย่างข้างต้นในลักษณะทีละขั้นตอนเพื่อความเข้าใจที่ดีขึ้น
ในบทนี้ให้เรามุ่งเน้นไปที่การสร้างโมเดลด้วย Designer หรือ Database First หรือเพียงแค่ใช้ Code First ต่อไปนี้เป็นแนวทางบางประการที่จะช่วยให้คุณตัดสินใจเลือกเวิร์กโฟลว์การสร้างแบบจำลอง
เราได้เห็นตัวอย่างของ Code First modeling, Database First modeling และ Model First modeling workflow ไปแล้ว
เวิร์กโฟลว์ Database First และ Model First ใช้ Designer แต่อย่างใดอย่างหนึ่งเริ่มต้นด้วยฐานข้อมูลเพื่อสร้างโมเดลและอีกขั้นเริ่มต้นที่โมเดลเพื่อสร้างฐานข้อมูล
สำหรับนักพัฒนาที่ไม่ต้องการใช้ Visual Designer บวกกับการสร้างโค้ด Entity Framework มีเวิร์กโฟลว์ที่แตกต่างไปจากเดิมอย่างสิ้นเชิงที่เรียกว่า Code First
เวิร์กโฟลว์ทั่วไปสำหรับ Code First นั้นยอดเยี่ยมสำหรับแอปพลิเคชันใหม่ล่าสุดที่คุณไม่มีฐานข้อมูล คุณกำหนดคลาสและโค้ดของคุณจากนั้นให้ Code First พิจารณาว่าฐานข้อมูลของคุณควรมีลักษณะอย่างไร
นอกจากนี้ยังเป็นไปได้ที่จะเริ่ม Code First ด้วยฐานข้อมูลและนั่นทำให้ Code First มีความขัดแย้งเล็กน้อย แต่มีเครื่องมือที่ช่วยให้คุณทำวิศวกรรมย้อนกลับฐานข้อมูลเป็นชั้นเรียนซึ่งเป็นวิธีที่ดีในการเริ่มต้นการเขียนโค้ด
เมื่อได้รับตัวเลือกเหล่านี้มาดูที่แผนผังการตัดสินใจ
หากคุณต้องการทำงานกับ Visual Designer ในโค้ดที่สร้างขึ้นคุณจะต้องเลือกหนึ่งในเวิร์กโฟลว์ที่เกี่ยวข้องกับ EF Designer หากฐานข้อมูลของคุณมีอยู่แล้ว Database First คือเส้นทางของคุณ
หากคุณต้องการใช้ Visual Designer ในโครงการใหม่ที่ไม่มีฐานข้อมูลคุณจะต้องใช้ Model First
หากคุณแค่ต้องการทำงานกับโค้ดไม่ใช่นักออกแบบ Code First ก็น่าจะเหมาะกับคุณพร้อมกับตัวเลือกในการใช้เครื่องมือที่ย้อนวิศวกรฐานข้อมูลไปยังคลาส
หากคุณมีคลาสอยู่แล้วทางออกที่ดีที่สุดคือใช้กับ Code First
ในบทก่อนหน้านี้คุณได้เรียนรู้วิธีการกำหนดโมเดลข้อมูลเอนทิตีที่แตกต่างกันสามวิธี
สองในนั้นคือ Database First และ Model First ขึ้นอยู่กับตัวออกแบบ Entity Framework รวมกับการสร้างโค้ด
ประการที่สาม Code First ช่วยให้คุณสามารถข้ามนักออกแบบภาพและเขียนโค้ดของคุณเองได้
ไม่ว่าคุณจะเลือกเส้นทางใดคุณจะได้รับคลาสโดเมนและคลาสเอนทิตีเฟรมเวิร์ก DbContext อย่างน้อยหนึ่งคลาสช่วยให้คุณสามารถดึงและเก็บรักษาข้อมูลที่เกี่ยวข้องกับคลาสเหล่านั้นได้
DbContext API ในแอปพลิเคชันของคุณใช้เป็นสะพานเชื่อมระหว่างคลาสและฐานข้อมูลของคุณ DbContext เป็นหนึ่งในคลาสที่สำคัญที่สุดใน Entity Framework
ช่วยให้สามารถแสดงและดำเนินการสืบค้น
ใช้ผลลัพธ์แบบสอบถามจากฐานข้อมูลและแปลงเป็นอินสแตนซ์ของคลาสโมเดลของเรา
สามารถติดตามการเปลี่ยนแปลงของเอนทิตีรวมถึงการเพิ่มและการลบจากนั้นทริกเกอร์การสร้างคำสั่งแทรกอัปเดตและลบที่ส่งไปยังฐานข้อมูลตามต้องการ
ต่อไปนี้เป็นคลาสบริบทโฆษณาโดเมนที่เราจะดำเนินการต่างๆในบทนี้ นี่เป็นตัวอย่างเดียวกับที่เราได้สร้างขึ้นในฐานข้อมูลแนวทางแรก
การปรับใช้คลาสบริบท
using System;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Data.Entity.Core.Objects;
using System.Linq;
namespace DatabaseFirstDemo {
public partial class UniContextEntities : DbContext {
public UniContextEntities(): base("name = UniContextEntities") {}
protected override void OnModelCreating(DbModelBuilder modelBuilder) {
throw new UnintentionalCodeFirstException();
}
public virtual DbSet<Course> Courses { get; set; }
public virtual DbSet<Enrollment> Enrollments { get; set; }
public virtual DbSet<Student> Students { get; set; }
}
}
การใช้คลาสโดเมน
ชั้นเรียน
namespace DatabaseFirstDemo {
using System;
using System.Collections.Generic;
public partial class Course {
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage",
"CA2214:DoNotCallOverridableMethodsInConstructors")]
public Course() {
this.Enrollments = new HashSet<Enrollment>();
}
public int CourseID { get; set; }
public string Title { get; set; }
public int Credits { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage",
"CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<Enrollment> Enrollments { get; set; }
}
}
ชั้นนักเรียน
namespace DatabaseFirstDemo {
using System;
using System.Collections.Generic;
public partial class Student {
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage",
"CA2214:DoNotCallOverridableMethodsInConstructors")]
public Student() {
this.Enrollments = new HashSet<Enrollment>();
}
public int ID { get; set; }
public string LastName { get; set; }
public string FirstMidName { get; set; }
public System.DateTime EnrollmentDate { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage",
"CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<Enrollment> Enrollments { get; set; }
}
}
การลงทะเบียนเรียน
namespace DatabaseFirstDemo {
using System;
using System.Collections.Generic;
public partial class Enrollment {
public int EnrollmentID { get; set; }
public int CourseID { get; set; }
public int StudentID { get; set; }
public Nullable<int> Grade { get; set; }
public virtual Course Course { get; set; }
public virtual Student Student { get; set; }
}
}
สร้างการดำเนินการ
การเพิ่มอ็อบเจ็กต์ใหม่ด้วย Entity Framework ทำได้ง่ายเพียงแค่สร้างอินสแตนซ์ใหม่ของอ็อบเจ็กต์ของคุณและลงทะเบียนโดยใช้เมธอด Add บน DbSet รหัสต่อไปนี้ช่วยให้คุณสามารถเพิ่มนักเรียนใหม่ในฐานข้อมูลได้
class Program {
static void Main(string[] args) {
var newStudent = new Student();
//set student name
newStudent.FirstMidName = "Bill";
newStudent.LastName = "Gates";
newStudent.EnrollmentDate = DateTime.Parse("2015-10-21");
newStudent.ID = 100;
//create DBContext object
using (var dbCtx = new UniContextEntities()) {
//Add Student object into Students DBset
dbCtx.Students.Add(newStudent);
// call SaveChanges method to save student into database
dbCtx.SaveChanges();
}
}
}
อัปเดตการทำงาน
การเปลี่ยนออบเจ็กต์ที่มีอยู่ทำได้ง่ายเพียงแค่อัปเดตค่าที่กำหนดให้กับคุณสมบัติที่คุณต้องการเปลี่ยนแปลงและเรียกใช้ SaveChanges ตัวอย่างเช่นรหัสต่อไปนี้ใช้เพื่อเปลี่ยนนามสกุลของ Ali จาก Khan เป็น Aslam
using (var context = new UniContextEntities()) {
var student = (from d in context.Students where d.FirstMidName == "Ali" select d).Single();
student.LastName = "Aslam";
context.SaveChanges();
}
ลบการทำงาน
ในการลบเอนทิตีโดยใช้ Entity Framework คุณใช้เมธอด Remove บน DbSet ลบงานสำหรับทั้งเอนทิตีที่มีอยู่และเพิ่มใหม่ การเรียกลบในเอนทิตีที่ถูกเพิ่ม แต่ยังไม่ได้บันทึกลงในฐานข้อมูลจะยกเลิกการเพิ่มเอนทิตี เอนทิตีจะถูกลบออกจากตัวติดตามการเปลี่ยนแปลงและไม่ถูกติดตามโดย DbContext อีกต่อไป การเรียกลบในเอนทิตีที่มีอยู่ซึ่งกำลังถูกติดตามการเปลี่ยนแปลงจะลงทะเบียนเอนทิตีสำหรับการลบในครั้งถัดไปที่เรียก SaveChanges ตัวอย่างต่อไปนี้เป็นรหัสที่นักเรียนถูกลบออกจากฐานข้อมูลซึ่งมีชื่อแรกคือ Ali
using (var context = new UniContextEntities()) {
var bay = (from d in context.Students where d.FirstMidName == "Ali" select d).Single();
context.Students.Remove(bay);
context.SaveChanges();
}
อ่านการทำงาน
การอ่านข้อมูลที่มีอยู่จากฐานข้อมูลนั้นง่ายมาก ต่อไปนี้เป็นรหัสที่เรียกข้อมูลทั้งหมดจากตารางนักเรียนจากนั้นโปรแกรมจะแสดงพร้อมชื่อและนามสกุลของนักเรียนตามลำดับตัวอักษร
using (var db = new UniContextEntities()) {
var query = from b in db.Students orderby b.FirstMidName select b;
Console.WriteLine("All All student in the database:");
foreach (var item in query) {
Console.WriteLine(item.FirstMidName +" "+ item.LastName);
}
Console.WriteLine("Press any key to exit...");
Console.ReadKey();
}
นักพัฒนาด้านการเข้าถึงข้อมูลทุกคนประสบปัญหาในขณะที่ตอบคำถามเกี่ยวกับการทำงานพร้อมกันของข้อมูล "จะเกิดอะไรขึ้นถ้ามีคนมากกว่าหนึ่งคนแก้ไขข้อมูลเดียวกันในเวลาเดียวกัน"
ยิ่งโชคดีในหมู่พวกเราที่ต้องรับมือกับกฎทางธุรกิจที่บอกว่า“ ไม่มีปัญหาสุดท้ายก็ชนะ”
ในกรณีนี้การเกิดพร้อมกันไม่ใช่ปัญหา เป็นไปได้มากว่ามันไม่ง่ายอย่างนั้นและไม่มีกระสุนเงินที่จะแก้ปัญหาทุกสถานการณ์ในคราวเดียว
ตามค่าเริ่มต้น Entity Framework จะใช้เส้นทางของ“ last one in wins” ซึ่งหมายความว่ามีการใช้การอัปเดตล่าสุดแม้ว่าจะมีผู้อื่นอัปเดตข้อมูลระหว่างเวลาที่ดึงข้อมูลและบันทึกข้อมูลเวลา
มาเป็นตัวอย่างให้เข้าใจกันดีกว่า ตัวอย่างต่อไปนี้จะเพิ่มคอลัมน์ใหม่ VersionNo ในตารางหลักสูตร
ไปที่นักออกแบบและคลิกขวาที่หน้าต่างนักออกแบบและเลือกรุ่นอัปเดตจากฐานข้อมูล ...
คุณจะเห็นว่ามีการเพิ่มคอลัมน์อื่นในเอนทิตีหลักสูตร
คลิกขวาที่คอลัมน์ที่สร้างขึ้นใหม่ VersionNo และเลือก Properties และเปลี่ยน ConcurrencyMode เป็น Fixed ดังที่แสดงในภาพต่อไปนี้
ด้วย ConcurrencyMode ของหลักสูตรเวอร์ชันไม่ตั้งค่าเป็นคงที่เมื่อใดก็ตามที่มีการอัปเดตหลักสูตรคำสั่งอัปเดตจะค้นหาหลักสูตรโดยใช้คุณสมบัติของ EntityKey และ VersionNo
ลองมาดูสถานการณ์ง่ายๆ ผู้ใช้สองคนเรียกค้นหลักสูตรเดียวกันในเวลาเดียวกันและผู้ใช้ 1 เปลี่ยนชื่อวิชานั้นเป็นคณิตศาสตร์และบันทึกการเปลี่ยนแปลงก่อนผู้ใช้ 2 ในภายหลังเมื่อผู้ใช้ 2 เปลี่ยนชื่อของหลักสูตรนั้นซึ่งถูกดึงมาก่อนที่ผู้ใช้ 1 จะบันทึกการเปลี่ยนแปลงของเขาในนั้น ผู้ใช้กรณีที่ 2 จะได้รับข้อยกเว้นการเกิดพร้อมกัน"User2: Optimistic Concurrency exception occured".
using System;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Linq;
namespace DatabaseFirstDemo {
class Program {
static void Main(string[] args) {
Course c1 = null;
Course c2 = null;
//User 1 gets Course
using (var context = new UniContextEntities()) {
context.Configuration.ProxyCreationEnabled = false;
c1 = context.Courses.Where(s ⇒ s.CourseID == 1).Single();
}
//User 2 also get the same Course
using (var context = new UniContextEntities()) {
context.Configuration.ProxyCreationEnabled = false;
c2 = context.Courses.Where(s ⇒ s.CourseID == 1).Single();
}
//User 1 updates Course Title
c1.Title = "Edited from user1";
//User 2 updates Course Title
c2.Title = "Edited from user2";
//User 1 saves changes first
using (var context = new UniContextEntities()) {
try {
context.Entry(c1).State = EntityState.Modified;
context.SaveChanges();
} catch (DbUpdateConcurrencyException ex) {
Console.WriteLine("User1: Optimistic Concurrency exception occurred");
}
}
//User 2 saves changes after User 1.
//User 2 will get concurrency exection
//because CreateOrModifiedDate is different in the database
using (var context = new UniContextEntities()) {
try {
context.Entry(c2).State = EntityState.Modified;
context.SaveChanges();
} catch (DbUpdateConcurrencyException ex) {
Console.WriteLine("User2: Optimistic Concurrency exception occurred");
}
}
}
}
}
ในทุกเวอร์ชันของ Entity Framework เมื่อใดก็ตามที่คุณดำเนินการ SaveChanges()ในการแทรกอัพเดตหรือลบฐานข้อมูลเฟรมเวิร์กจะรวมการดำเนินการนั้นไว้ในธุรกรรม เมื่อคุณเรียกใช้ SaveChanges บริบทจะเริ่มต้นธุรกรรมโดยอัตโนมัติและกระทำหรือย้อนกลับขึ้นอยู่กับว่าการคงอยู่สำเร็จหรือไม่
ทั้งหมดนี้เป็นความโปร่งใสสำหรับคุณและคุณไม่จำเป็นต้องจัดการกับมัน
ธุรกรรมนี้ใช้เวลานานพอที่จะดำเนินการและเสร็จสิ้น
เมื่อคุณดำเนินการดังกล่าวอื่นธุรกรรมใหม่จะเริ่มขึ้น
Entity Framework 6 มีดังต่อไปนี้ -
ฐานข้อมูล BeginTransaction ()
เป็นวิธีการที่ง่ายและสะดวกกว่าภายใน DbContext ที่มีอยู่เพื่อเริ่มต้นและทำธุรกรรมให้กับผู้ใช้
ช่วยให้สามารถรวมการดำเนินการหลายอย่างภายในธุรกรรมเดียวกันและด้วยเหตุนี้ทั้งหมดจึงมีข้อผูกพันหรือทั้งหมดจะถูกย้อนกลับเป็นรายการเดียว
นอกจากนี้ยังช่วยให้ผู้ใช้ระบุระดับการแยกสำหรับธุรกรรมได้ง่ายขึ้น
Database.UseTransaction ()
อนุญาตให้ DbContext ใช้ธุรกรรมซึ่งเริ่มต้นนอก Entity Framework
ลองมาดูตัวอย่างต่อไปนี้ซึ่งมีการดำเนินการหลายรายการในธุรกรรมเดียว รหัสเป็น -
class Program {
static void Main(string[] args) {
using (var context = new UniContextEntities()) {
using (var dbContextTransaction = context.Database.BeginTransaction()) {
try {
Student student = new Student() {
ID = 200,
FirstMidName = "Ali",
LastName = "Khan",
EnrollmentDate = DateTime.Parse("2015-12-1")
};
context.Students.Add(student);
context.Database.ExecuteSqlCommand(@"UPDATE Course SET Title =
'Calculus'" + "WHERE CourseID = 1045");
var query = context.Courses.Where(c ⇒ c.CourseID == 1045);
foreach (var item in query) {
Console.WriteLine(item.CourseID.ToString()
+ " " + item.Title + " " + item.Credits);
}
context.SaveChanges();
var query1 = context.Students.Where(s ⇒ s.ID == 200);
foreach (var item in query1) {
Console.WriteLine(item.ID.ToString()
+ " " + item.FirstMidName + " " + item.LastName);
}
dbContextTransaction.Commit();
} catch (Exception) {
dbContextTransaction.Rollback();
}
}
}
}
}
การเริ่มต้นธุรกรรมจำเป็นต้องเปิดการเชื่อมต่อร้านค้าที่เกี่ยวข้อง
ดังนั้นการเรียกใช้ DatabaseBeginTransaction () จะเปิดการเชื่อมต่อหากยังไม่ได้เปิด
หาก DbContextTransaction เปิดการเชื่อมต่อการเชื่อมต่อจะปิดเมื่อเรียก Dispose ()
มุมมองคือวัตถุที่มีข้อมูลที่ได้รับจากแบบสอบถามที่กำหนดไว้ล่วงหน้า มุมมองคือวัตถุเสมือนหรือตารางที่ชุดผลลัพธ์ได้มาจากแบบสอบถาม คล้ายกับตารางจริงมากเพราะประกอบด้วยคอลัมน์และแถวของข้อมูล ต่อไปนี้เป็นการใช้มุมมองทั่วไป -
- กรองข้อมูลของตารางพื้นฐาน
- กรองข้อมูลเพื่อความปลอดภัย
- รวมศูนย์ข้อมูลที่กระจายไปทั่วเซิร์ฟเวอร์หลายเครื่อง
- สร้างชุดข้อมูลที่ใช้ซ้ำได้
มุมมองสามารถใช้ในลักษณะเดียวกันกับที่คุณสามารถใช้ตาราง ในการใช้มุมมองเป็นเอนทิตีก่อนอื่นคุณจะต้องเพิ่มมุมมองฐานข้อมูลไปยัง EDM หลังจากเพิ่มมุมมองให้กับโมเดลของคุณแล้วคุณสามารถทำงานกับมันได้เช่นเดียวกับเอนทิตีปกติยกเว้นการดำเนินการสร้างอัปเดตและลบ
ลองมาดูวิธีเพิ่มมุมมองลงในโมเดลจากฐานข้อมูล
Step 1 - สร้างโครงการแอปพลิเคชันคอนโซลใหม่
Step 2 - คลิกขวาที่โปรเจ็กต์ในตัวสำรวจโซลูชันแล้วเลือกเพิ่ม→รายการใหม่
Step 3 - เลือก ADO.NET Entity Data Model จากบานหน้าต่างตรงกลางและป้อนชื่อ ViewModel ในฟิลด์ Name
Step 4 - คลิกปุ่มเพิ่มซึ่งจะเปิดกล่องโต้ตอบตัวช่วยสร้างโมเดลข้อมูลเอนทิตี
Step 5 - เลือก EF Designer จากฐานข้อมูลและคลิกปุ่มถัดไป
Step 6 - เลือกฐานข้อมูลที่มีอยู่แล้วคลิกถัดไป
Step 7 - เลือก Entity Framework 6.x แล้วคลิกถัดไป
Step 8 - เลือกตารางและมุมมองจากฐานข้อมูลของคุณแล้วคลิกเสร็จสิ้น
คุณสามารถเห็นในหน้าต่างตัวออกแบบว่ามุมมองถูกสร้างขึ้นและคุณสามารถใช้ในโปรแกรมเป็นเอนทิตีได้
ในตัวสำรวจโซลูชันคุณจะเห็นว่าคลาส MyView ถูกสร้างขึ้นจากฐานข้อมูลด้วย
มาดูตัวอย่างที่ดึงข้อมูลทั้งหมดจากมุมมอง ต่อไปนี้เป็นรหัส -
class Program {
static void Main(string[] args) {
using (var db = new UniContextEntities()) {
var query = from b in db.MyViews
orderby b.FirstMidName select b;
Console.WriteLine("All student in the database:");
foreach (var item in query) {
Console.WriteLine(item.FirstMidName + " " + item.LastName);
}
Console.WriteLine("Press any key to exit...");
Console.ReadKey();
}
}
}
เมื่อดำเนินการโค้ดด้านบนคุณจะได้รับผลลัพธ์ต่อไปนี้ -
All student in the database:
Ali Khan
Arturo finand
Bill Gates
Carson Alexander
Gytis Barzdukas
Laura Norman
Meredith Alonso
Nino Olivetto
Peggy Justice
Yan Li
Press any key to exit...
เราขอแนะนำให้คุณดำเนินการตามตัวอย่างข้างต้นในลักษณะทีละขั้นตอนเพื่อความเข้าใจที่ดีขึ้น
ดัชนีคือโครงสร้างข้อมูลบนดิสก์ที่ยึดตามตารางและมุมมอง ดัชนีทำให้การดึงข้อมูลเร็วขึ้นและมีประสิทธิภาพในกรณีส่วนใหญ่ อย่างไรก็ตามการใส่ตารางหรือมุมมองที่มีดัชนีมากเกินไปอาจส่งผลต่อประสิทธิภาพของการดำเนินการอื่น ๆ เช่นการแทรกหรือการอัปเดต
การจัดทำดัชนีเป็นคุณลักษณะใหม่ในกรอบงานเอนทิตีที่คุณสามารถปรับปรุงประสิทธิภาพของแอปพลิเคชัน Code First ของคุณได้โดยลดเวลาที่ต้องใช้ในการสืบค้นข้อมูลจากฐานข้อมูล
คุณสามารถเพิ่มดัชนีในฐานข้อมูลของคุณโดยใช้ไฟล์ Index แอตทริบิวต์และแทนที่ค่าเริ่มต้น Unique และ Clustered การตั้งค่าเพื่อให้ได้ดัชนีที่เหมาะสมที่สุดกับสถานการณ์ของคุณ
มาดูโค้ดต่อไปนี้ซึ่งมีการเพิ่มแอตทริบิวต์ Index ในคลาส Course สำหรับ CourseID
public partial class Course {
public Course() {
this.Enrollments = new HashSet<Enrollment>();
}
[Index]
public int CourseID { get; set; }
public string Title { get; set; }
public int Credits { get; set; }
public byte[] VersionNo { get; set; }
public virtual ICollection<Enrollment> Enrollments { get; set; }
}
คีย์ที่สร้างข้างต้นไม่ซ้ำกันไม่ใช่คลัสเตอร์ มีโอเวอร์โหลดที่สามารถลบล้างค่าเริ่มต้นเหล่านี้ได้ -
ในการสร้างดัชนีให้เป็นดัชนีแบบคลัสเตอร์คุณต้องระบุ IsClustered = true
ในทำนองเดียวกันคุณสามารถสร้างดัชนีให้เป็นดัชนีเฉพาะได้โดยระบุ IsUnique = true
ลองดูรหัส C # ต่อไปนี้ที่ดัชนีเป็นคลัสเตอร์และไม่ซ้ำกัน
public partial class Course {
public Course() {
this.Enrollments = new HashSet<Enrollment>();
}
[Index(IsClustered = true, IsUnique = true)]
public int CourseID { get; set; }
public string Title { get; set; }
public int Credits { get; set; }
public byte[] VersionNo { get; set; }
public virtual ICollection<Enrollment> Enrollments { get; set; }
}
สามารถใช้แอตทริบิวต์ดัชนีเพื่อสร้างดัชนีเฉพาะในฐานข้อมูล อย่างไรก็ตามนี่ไม่ได้หมายความว่า EF จะสามารถให้เหตุผลเกี่ยวกับความเป็นเอกลักษณ์ของคอลัมน์ได้เมื่อจัดการกับความสัมพันธ์ ฯลฯ โดยทั่วไปแล้วคุณลักษณะนี้จะเรียกว่าการสนับสนุนสำหรับ
Entity Framework อนุญาตให้คุณใช้โพรซีเดอร์ที่เก็บไว้ใน Entity Data Model แทนหรือใช้ร่วมกับการสร้างคำสั่งอัตโนมัติ
คุณสามารถใช้กระบวนงานที่จัดเก็บเพื่อดำเนินการตรรกะที่กำหนดไว้ล่วงหน้าบนตารางฐานข้อมูลและหลายองค์กรมีนโยบายที่กำหนดให้ใช้กระบวนงานที่จัดเก็บเหล่านี้
นอกจากนี้ยังสามารถระบุว่า EF ควรใช้โพรซีเดอร์ที่จัดเก็บไว้ของคุณสำหรับการแทรกอัปเดตหรือลบเอนทิตี
แม้ว่าคำสั่งที่สร้างขึ้นแบบไดนามิกจะปลอดภัยมีประสิทธิภาพและโดยทั่วไปดีเท่ากับหรือดีกว่าคำสั่งที่คุณเขียนเอง แต่ก็มีหลายกรณีที่มีขั้นตอนการจัดเก็บอยู่แล้วและแนวปฏิบัติของ บริษัท ของคุณอาจ จำกัด การใช้ตารางโดยตรง
หรือคุณอาจต้องการควบคุมสิ่งที่ดำเนินการในร้านค้าอย่างชัดเจนและต้องการสร้างกระบวนงานที่จัดเก็บไว้
ตัวอย่างต่อไปนี้สร้างโปรเจ็กต์ใหม่จากไฟล์→ใหม่→โปรเจ็กต์
Step 1 - เลือกแอปพลิเคชันคอนโซลจากบานหน้าต่างตรงกลางและป้อน StoredProceduresDemo ในช่องชื่อ
Step 2 - ใน Server explorer คลิกขวาที่ฐานข้อมูลของคุณ
Step 3 - เลือกแบบสอบถามใหม่และป้อนรหัสต่อไปนี้ในตัวแก้ไข T-SQL เพื่อเพิ่มตารางใหม่ในฐานข้อมูลของคุณ
IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id =
OBJECT_ID(N'[dbo].[StudentGrade]') AND type in (N'U'))
BEGIN
CREATE TABLE [dbo].[StudentGrade](
[EnrollmentID] [int] IDENTITY(1,1) NOT NULL,
[CourseID] [int] NOT NULL,
[StudentID] [int] NOT NULL,
[Grade] [decimal](3, 2) NULL,
CONSTRAINT [PK_StudentGrade] PRIMARY KEY CLUSTERED (
[EnrollmentID] ASC
)
WITH (IGNORE_DUP_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
END
GO
Step 4 - คลิกขวาที่ตัวแก้ไขและเลือกดำเนินการ
Step 5- คลิกขวาที่ฐานข้อมูลของคุณแล้วคลิกรีเฟรช คุณจะเห็นตารางที่เพิ่มใหม่ในฐานข้อมูลของคุณ
Step 6 - ใน Server explorer ให้คลิกขวาที่ฐานข้อมูลของคุณอีกครั้ง
Step 7 - เลือกแบบสอบถามใหม่และป้อนรหัสต่อไปนี้ในตัวแก้ไข T-SQL เพื่อเพิ่มขั้นตอนการจัดเก็บในฐานข้อมูลของคุณซึ่งจะส่งคืนคะแนนของนักเรียน
IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id =
OBJECT_ID(N'[dbo].[GetStudentGrades]') AND type in (N'P', N'PC'))
BEGIN
EXEC dbo.sp_executesql @statement = N'
CREATE PROCEDURE [dbo].[GetStudentGrades]
@StudentID int
AS
SELECT EnrollmentID, Grade, CourseID, StudentID FROM dbo.StudentGrade
WHERE StudentID = @StudentID
'
END
GO
Step 8 - คลิกขวาที่ตัวแก้ไขและเลือกดำเนินการ
Step 9- คลิกขวาที่ฐานข้อมูลของคุณแล้วคลิกรีเฟรช คุณจะเห็นว่ามีการสร้างกระบวนงานที่จัดเก็บไว้ในฐานข้อมูลของคุณ
Step 10 - คลิกขวาที่ชื่อโครงการใน Solution Explorer แล้วเลือกเพิ่ม→รายการใหม่
Step 11 - จากนั้นเลือก ADO.NET Entity Data Model ในบานหน้าต่างเทมเพลต
Step 12 - ป้อน SPModel เป็นชื่อจากนั้นคลิกเพิ่ม
Step 13 - ในกล่องโต้ตอบเลือกเนื้อหาแบบจำลองให้เลือกตัวออกแบบ EF จากฐานข้อมูลจากนั้นคลิกถัดไป
Step 14 - เลือกฐานข้อมูลของคุณแล้วคลิกถัดไป
Step 15 - ในกล่องโต้ตอบเลือกวัตถุฐานข้อมูลของคุณให้คลิกที่ตารางมุมมอง
Step 16 - เลือกฟังก์ชัน GetStudentGradesForCourse ที่อยู่ภายใต้โหนด Stored Procedures and Functions แล้วคลิก Finish
Step 17 - เลือก View → Other Windows → Entity Data Model Browser แล้วคลิกขวา GetStudentGrades ภายใต้ Function Imports แล้วเลือก Edit
มันจะสร้างกล่องโต้ตอบต่อไปนี้
Step 18 - คลิกที่ปุ่มตัวเลือกเอนทิตีและเลือก StudentGrade จากคอมโบบ็อกซ์เป็นประเภทการส่งคืนของกระบวนงานที่เก็บไว้นี้แล้วคลิกตกลง
มาดูรหัส C # ต่อไปนี้ซึ่งจะมีการดึงคะแนนทั้งหมดโดยส่งรหัสนักเรียนเป็นพารามิเตอร์ในขั้นตอนการจัดเก็บ GetStudentGrades
class Program {
static void Main(string[] args) {
using (var context = new UniContextEntities()) {
int studentID = 22;
var studentGrades = context.GetStudentGrades(studentID);
foreach (var student in studentGrades) {
Console.WriteLine("Course ID: {0}, Title: {1}, Grade: {2} ",
student.CourseID, student.Course.Title, student.Grade);
}
Console.ReadKey();
}
}
}
เมื่อโค้ดด้านบนถูกคอมไพล์และดำเนินการคุณจะได้รับผลลัพธ์ต่อไปนี้ -
Course ID: 4022, Title: Microeconomics, Grade: 3.00
Course ID: 4041, Title: Macroeconomics, Grade: 3.50
เราขอแนะนำให้คุณดำเนินการตามตัวอย่างข้างต้นในลักษณะทีละขั้นตอนเพื่อความเข้าใจที่ดีขึ้น
ในบทนี้ให้เราดูวิธีการเปลี่ยนแปลงเอนทิตีที่บริบทไม่ถูกติดตาม เอนทิตีที่ไม่ได้ถูกติดตามโดยบริบทเรียกว่าเอนทิตี 'ไม่เชื่อมต่อ'
สำหรับแอปพลิเคชันระดับเดียวส่วนใหญ่ที่อินเทอร์เฟซผู้ใช้และเลเยอร์การเข้าถึงฐานข้อมูลทำงานในกระบวนการแอปพลิเคชันเดียวกันคุณอาจจะดำเนินการกับเอนทิตีที่ถูกติดตามโดยบริบท
การดำเนินการกับเอนทิตีที่ไม่ได้เชื่อมต่อนั้นพบได้บ่อยในแอปพลิเคชัน N-Tier
แอปพลิเคชัน N-Tier เกี่ยวข้องกับการดึงข้อมูลบางอย่างบนเซิร์ฟเวอร์และส่งคืนผ่านเครือข่ายไปยังเครื่องไคลเอนต์
จากนั้นไคลเอนต์แอปพลิเคชันจะจัดการข้อมูลนี้ก่อนที่จะส่งคืนไปยังเซิร์ฟเวอร์เพื่อคงอยู่
ต่อไปนี้เป็นสองขั้นตอนที่ต้องดำเนินการกับกราฟเอนทิตีที่ถูกตัดการเชื่อมต่อหรือแม้แต่เอนทิตีที่ถูกตัดการเชื่อมต่อเดียว
แนบเอนทิตีด้วยอินสแตนซ์บริบทใหม่และทำให้บริบททราบเกี่ยวกับเอนทิตีเหล่านี้
ตั้งค่า EntityStates ที่เหมาะสมให้กับเอนทิตีเหล่านี้ด้วยตนเอง
มาดูโค้ดต่อไปนี้ซึ่งมีการเพิ่มเอนทิตีนักศึกษาด้วยเอนทิตีการลงทะเบียนสองเอนทิตี
class Program {
static void Main(string[] args) {
var student = new Student {
ID = 1001,
FirstMidName = "Wasim",
LastName = "Akram",
EnrollmentDate = DateTime.Parse("2015-10-10"),
Enrollments = new List<Enrollment> {
new Enrollment{EnrollmentID = 2001,CourseID = 4022, StudentID = 1001 },
new Enrollment{EnrollmentID = 2002,CourseID = 4025, StudentID = 1001 },
}
};
using (var context = new UniContextEntities()) {
context.Students.Add(student);
Console.WriteLine("New Student ({0} {1}): {2}",
student.FirstMidName, student.LastName, context.Entry(student).State);
foreach (var enrollment in student.Enrollments) {
Console.WriteLine("Enrollment ID: {0} State: {1}",
enrollment.EnrollmentID, context.Entry(enrollment).State);
}
Console.WriteLine("Press any key to exit...");
Console.ReadKey();
}
}
}
โค้ดนี้สร้างอินสแตนซ์ Student ใหม่ซึ่งอ้างอิงอินสแตนซ์การลงทะเบียนใหม่สองอินสแตนซ์ในคุณสมบัติการลงทะเบียน
จากนั้นนักเรียนใหม่จะถูกเพิ่มลงในบริบทโดยใช้วิธีการเพิ่ม
เมื่อเพิ่ม Student แล้วโค้ดจะใช้เมธอด DbContext.Entry เพื่อเข้าถึงข้อมูลการติดตามการเปลี่ยนแปลงที่ Entity Framework มีเกี่ยวกับ Student ใหม่
จากข้อมูลการติดตามการเปลี่ยนแปลงนี้คุณสมบัติของรัฐจะใช้เพื่อเขียนสถานะปัจจุบันของเอนทิตี
จากนั้นกระบวนการนี้จะทำซ้ำสำหรับการลงทะเบียนที่สร้างขึ้นใหม่แต่ละรายการที่อ้างอิงจากนักเรียนใหม่ หากคุณเรียกใช้แอปพลิเคชันคุณจะได้รับผลลัพธ์ต่อไปนี้ -
New Student (Wasim Akram): Added
Enrollment ID: 2001 State: Added
Enrollment ID: 2002 State: Added
Press any key to exit...
ในขณะที่ DbSet.Add ใช้เพื่อบอก Entity Framework เกี่ยวกับเอนทิตีใหม่ DbSet.Attach ใช้เพื่อบอก Entity Framework เกี่ยวกับเอนทิตีที่มีอยู่ วิธีการแนบจะทำเครื่องหมายเอนทิตีในสถานะไม่เปลี่ยนแปลง
ลองดูรหัส C # ต่อไปนี้ซึ่งแนบเอนทิตีที่ไม่ได้เชื่อมต่อกับ DbContext
class Program {
static void Main(string[] args) {
var student = new Student {
ID = 1001,
FirstMidName = "Wasim",
LastName = "Akram",
EnrollmentDate = DateTime.Parse("2015-10-10"),
Enrollments = new List<Enrollment> {
new Enrollment { EnrollmentID = 2001, CourseID = 4022, StudentID = 1001 },
new Enrollment { EnrollmentID = 2002, CourseID = 4025, StudentID = 1001 },
}
};
using (var context = new UniContextEntities()) {
context.Students.Attach(student);
Console.WriteLine("New Student ({0} {1}): {2}",
student.FirstMidName, student.LastName, context.Entry(student).State);
foreach (var enrollment in student.Enrollments) {
Console.WriteLine("Enrollment ID: {0} State: {1}", enrollment.EnrollmentID,
context.Entry(enrollment).State);
}
Console.WriteLine("Press any key to exit...");
Console.ReadKey();
}
}
}
เมื่อโค้ดด้านบนถูกเรียกใช้ด้วยเมธอด Attach () คุณจะได้รับผลลัพธ์ต่อไปนี้
New Student (Wasim Akram): Unchanged
Enrollment ID: 2001 State: Unchanged
Enrollment ID: 2002 State: Unchanged
Press any key to exit...
ในบทนี้ให้เราเรียนรู้วิธีการแมป Table-valueed Functions (TVF) โดยใช้ Entity Framework Designer และวิธีเรียก TVF จากแบบสอบถาม LINQ
ปัจจุบันรองรับ TVF ในเวิร์กโฟลว์ Database First เท่านั้น
เปิดตัวครั้งแรกใน Entity Framework เวอร์ชัน 5
ในการใช้ TVF คุณต้องกำหนดเป้าหมาย. NET Framework 4.5 ขึ้นไป
คล้ายกับขั้นตอนการจัดเก็บ แต่มีข้อแตกต่างที่สำคัญอย่างหนึ่งคือผลลัพธ์ของ TVF สามารถประกอบได้ ซึ่งหมายความว่าผลลัพธ์จาก TVF สามารถใช้ในแบบสอบถาม LINQ ได้ในขณะที่ไม่สามารถใช้ผลลัพธ์ของกระบวนงานที่เก็บไว้ได้
มาดูตัวอย่างการสร้างโปรเจ็กต์ใหม่จาก File → New → Project ต่อไปนี้
Step 1 - เลือกแอปพลิเคชันคอนโซลจากบานหน้าต่างตรงกลางและป้อน TableValuedFunctionDemo ในช่องชื่อ
Step 2 - ใน Server explorer คลิกขวาที่ฐานข้อมูลของคุณ
Step 3 - เลือกแบบสอบถามใหม่และป้อนรหัสต่อไปนี้ในตัวแก้ไข T-SQL เพื่อเพิ่มตารางใหม่ในฐานข้อมูลของคุณ
IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id =
OBJECT_ID(N'[dbo].[StudentGrade]') AND type in (N'U'))
BEGIN
CREATE TABLE [dbo].[StudentGrade](
[EnrollmentID] [int] IDENTITY(1,1) NOT NULL,
[CourseID] [int] NOT NULL,
[StudentID] [int] NOT NULL,
[Grade] [decimal](3, 2) NULL,
CONSTRAINT [PK_StudentGrade] PRIMARY KEY CLUSTERED ([EnrollmentID] ASC)
WITH (IGNORE_DUP_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
END
GO
Step 4 - คลิกขวาที่ตัวแก้ไขและเลือกดำเนินการ
Step 5- คลิกขวาที่ฐานข้อมูลของคุณแล้วคลิกรีเฟรช คุณจะเห็นตารางที่เพิ่มใหม่ในฐานข้อมูลของคุณ
Step 6- ตอนนี้สร้างฟังก์ชันที่จะคืนเกรดของนักเรียนสำหรับหลักสูตร ป้อนรหัสต่อไปนี้ในตัวแก้ไข T-SQL
CREATE FUNCTION [dbo].[GetStudentGradesForCourse]
(@CourseID INT)
RETURNS TABLE
RETURN
SELECT [EnrollmentID],
[CourseID],
[StudentID],
[Grade]
FROM [dbo].[StudentGrade]
WHERE CourseID = @CourseID
Step 7 - คลิกขวาที่ตัวแก้ไขและเลือกดำเนินการ
ตอนนี้คุณจะเห็นว่าฟังก์ชันถูกสร้างขึ้น
Step 8 - คลิกขวาที่ชื่อโครงการใน Solution Explorer แล้วเลือกเพิ่ม→รายการใหม่
Step 9 - จากนั้นเลือก ADO.NET Entity Data Model ในบานหน้าต่างเทมเพลต
Step 10 - ป้อน TVFModel เป็นชื่อจากนั้นคลิกเพิ่ม
Step 11 - ในกล่องโต้ตอบเลือกเนื้อหาแบบจำลองให้เลือกตัวออกแบบ EF จากฐานข้อมูลจากนั้นคลิกถัดไป
Step 12 - เลือกฐานข้อมูลของคุณแล้วคลิกถัดไป
Step 13 - ในกล่องโต้ตอบเลือกวัตถุฐานข้อมูลของคุณให้เลือกตารางมุมมอง
Step 14 - เลือกฟังก์ชัน GetStudentGradesForCourse ที่อยู่ภายใต้โหนด Stored Procedures and Functions แล้วคลิก Finish
Step 15 - เลือก View → Other Windows → Entity Data Model Browser แล้วคลิกขวา GetStudentGradesForCourse ภายใต้ Function Imports แล้วเลือก Edit
คุณจะเห็นกล่องโต้ตอบต่อไปนี้
Step 16 - คลิกที่ปุ่มตัวเลือกเอนทิตีและเลือกการลงทะเบียนจากคอมโบบ็อกซ์เป็นประเภทการส่งคืนของฟังก์ชันนี้และคลิกตกลง
มาดูรหัส C # ต่อไปนี้ซึ่งจะมีการดึงคะแนนของนักเรียนทั้งหมดที่ลงทะเบียนเรียนใน Course ID = 4022 ในฐานข้อมูล
class Program {
static void Main(string[] args) {
using (var context = new UniContextEntities()) {
var CourseID = 4022;
// Return all the best students in the Microeconomics class.
var students = context.GetStudentGradesForCourse(CourseID);
foreach (var result in students) {
Console.WriteLine("Student ID: {0}, Grade: {1}",
result.StudentID, result.Grade);
}
Console.ReadKey();
}
}
}
เมื่อโค้ดด้านบนถูกคอมไพล์และดำเนินการคุณจะได้รับผลลัพธ์ต่อไปนี้ -
Student ID: 1, Grade: 2
Student ID: 4, Grade: 4
Student ID: 9, Grade: 3.5
เราขอแนะนำให้คุณดำเนินการตามตัวอย่างข้างต้นในลักษณะทีละขั้นตอนเพื่อความเข้าใจที่ดีขึ้น
ใน Entity Framework คุณสามารถสอบถามกับคลาสเอนทิตีของคุณโดยใช้ LINQ คุณยังสามารถเรียกใช้แบบสอบถามโดยใช้ SQL ดิบโดยตรงกับฐานข้อมูลโดยใช้ DbCOntext เทคนิคนี้สามารถนำไปใช้กับโมเดลที่สร้างด้วย Code First และ EF Designer ได้อย่างเท่าเทียมกัน
แบบสอบถาม SQL ในเอนทิตีที่มีอยู่
วิธี SqlQuery บน DbSet อนุญาตให้เขียนแบบสอบถาม SQL ดิบที่จะส่งคืนอินสแตนซ์ของเอนทิตี อ็อบเจ็กต์ที่ส่งคืนจะถูกติดตามโดยบริบทเช่นเดียวกับที่พวกมันถูกส่งคืนโดยคิวรี LINQ ตัวอย่างเช่น -
class Program {
static void Main(string[] args) {
using (var context = new UniContextEntities()) {
var students = context.Students.SqlQuery("SELECT * FROM dbo.Student").ToList();
foreach (var student in students) {
string name = student.FirstMidName + " " + student.LastName;
Console.WriteLine("ID: {0}, Name: {1}, \tEnrollment Date {2} ",
student.ID, name, student.EnrollmentDate.ToString());
}
Console.ReadKey();
}
}
}
รหัสด้านบนจะดึงข้อมูลนักเรียนทั้งหมดจากฐานข้อมูล
แบบสอบถาม SQL สำหรับประเภทที่ไม่ใช่เอนทิตี
แบบสอบถาม SQL ที่ส่งคืนอินสแตนซ์ทุกประเภทรวมถึงชนิดดั้งเดิมสามารถสร้างได้โดยใช้เมธอด SqlQuery บนคลาสฐานข้อมูล ตัวอย่างเช่น -
class Program {
static void Main(string[] args) {
using (var context = new UniContextEntities()) {
var studentNames = context.Database.SqlQuery
<string>("SELECT FirstMidName FROM dbo.Student").ToList();
foreach (var student in studentNames) {
Console.WriteLine("Name: {0}", student);
}
Console.ReadKey();
}
}
}
คำสั่ง SQL ไปยังฐานข้อมูล
เมธอด ExecuteSqlCommnad ใช้ในการส่งคำสั่งที่ไม่ใช่คิวรีไปยังฐานข้อมูลเช่นคำสั่ง Insert, Update หรือ Delete มาดูรหัสต่อไปนี้ซึ่งมีการอัปเดตชื่อของนักเรียนเป็น ID = 1
class Program {
static void Main(string[] args) {
using (var context = new UniContextEntities()) {
//Update command
int noOfRowUpdated = context.Database.ExecuteSqlCommand("Update
student set FirstMidName = 'Ali' where ID = 1");
context.SaveChanges();
var student = context.Students.SqlQuery("SELECT * FROM
dbo.Student where ID = 1").Single();
string name = student.FirstMidName + " " + student.LastName;
Console.WriteLine("ID: {0}, Name: {1}, \tEnrollment Date {2} ",
student.ID, name, student.EnrollmentDate.ToString());
Console.ReadKey();
}
}
}
รหัสด้านบนจะดึงชื่อของนักเรียนทั้งหมดจากฐานข้อมูล
ใน Entity Framework คุณลักษณะนี้จะช่วยให้คุณกำหนดคุณสมบัติบนคลาสโดเมนที่เป็นประเภท enum และแม็พกับคอลัมน์ฐานข้อมูลประเภทจำนวนเต็ม จากนั้น Entity Framework จะแปลงค่าฐานข้อมูลเป็นและจาก enum ที่เกี่ยวข้องในขณะที่ทำการค้นหาและบันทึกข้อมูล
ประเภทที่แจกแจงมีประโยชน์ทุกประเภทเมื่อทำงานกับคุณสมบัติที่มีจำนวนการตอบสนองคงที่
ทั้งความปลอดภัยและความน่าเชื่อถือของแอปพลิเคชันจะเพิ่มขึ้นเมื่อคุณใช้การแจงนับ
การแจงนับทำให้ผู้ใช้ทำผิดได้ยากขึ้นมากและปัญหาต่างๆเช่นการโจมตีด้วยการฉีดก็ไม่มีอยู่
ใน Entity Framework การแจงนับสามารถมีประเภทพื้นฐานดังต่อไปนี้ -
- Byte
- Int16
- Int32
- Int64
- SByte
ชนิดพื้นฐานเริ่มต้นขององค์ประกอบการแจงนับคือ int
โดยค่าเริ่มต้นตัวแจงนับตัวแรกจะมีค่า 0 และค่าของแต่ละตัวแจงนับต่อเนื่องจะเพิ่มขึ้น 1
ลองดูตัวอย่างต่อไปนี้ซึ่งเราจะสร้างเอนทิตีในตัวออกแบบจากนั้นจะเพิ่มคุณสมบัติบางอย่าง
Step 1 - สร้างโครงการใหม่จากไฟล์→ใหม่→ตัวเลือกเมนูโครงการ
Step 2 - ในบานหน้าต่างด้านซ้ายเลือกแอปพลิเคชันคอนโซล
Step 3 - ป้อน EFEnumDemo เป็นชื่อโครงการแล้วคลิกตกลง
Step 4 - คลิกขวาที่ชื่อโครงการใน Solution Explorer และเลือกเพิ่ม→ตัวเลือกเมนูรายการใหม่
Step 5 - เลือก ADO.NET Entity Data Model ในบานหน้าต่างเทมเพลต
Step 6 - ป้อน EFEnumModel.edmx สำหรับชื่อไฟล์จากนั้นคลิกเพิ่ม
Step 7 - ในหน้าตัวช่วยสร้างโมเดลข้อมูลเอนทิตีเลือก Empty EF designer Model
Step 8 - คลิกเสร็จสิ้น
Step 9 - จากนั้นคลิกขวาที่หน้าต่างนักออกแบบและเลือกเพิ่ม→เอนทิตี
กล่องโต้ตอบ New Entity จะปรากฏดังที่แสดงในภาพต่อไปนี้
Step 10 - ป้อน Department เป็นชื่อเอนทิตีและ DeptID เป็นชื่อคุณสมบัติปล่อยให้ประเภทคุณสมบัติเป็น Int32 แล้วคลิกตกลง
Step 11 - คลิกขวาที่เอนทิตีแล้วเลือกเพิ่มใหม่→คุณสมบัติสเกลาร์
Step 12 - เปลี่ยนชื่อคุณสมบัติใหม่เป็น DeptName
Step 13 - เปลี่ยนประเภทของคุณสมบัติใหม่เป็น Int32 (โดยค่าเริ่มต้นคุณสมบัติใหม่เป็นประเภท String)
Step 14 - หากต้องการเปลี่ยนประเภทให้เปิดหน้าต่าง Properties และเปลี่ยนคุณสมบัติ Type เป็น Int32
Step 15 - ใน Entity Framework Designer คลิกขวาที่คุณสมบัติ Name เลือก Convert to enum
Step 16 - ในกล่องโต้ตอบ Add Enum Type ให้ป้อน DepartmentNames สำหรับ Enum Type Name เปลี่ยน Underlying Type เป็น Int32 จากนั้นเพิ่มสมาชิกต่อไปนี้ให้กับประเภท: Physics, Chemistry, Computer และ Economics
Step 17 - คลิกตกลง
หากคุณเปลี่ยนไปใช้หน้าต่าง Model Browser คุณจะเห็นว่ามีการเพิ่มประเภทลงในโหนด Enum types ด้วย
มาสร้างฐานข้อมูลจากโมเดลโดยทำตามขั้นตอนทั้งหมดที่กล่าวถึงในบทแนวทางแรกของโมเดล
Step 1 - คลิกขวาที่พื้นผิว Entity Designer แล้วเลือกสร้างฐานข้อมูลจาก Model
กล่องโต้ตอบเลือกการเชื่อมต่อข้อมูลของตัวช่วยสร้างฐานข้อมูลจะปรากฏขึ้น
Step 2 - คลิกปุ่มการเชื่อมต่อใหม่
Step 3 - ป้อนชื่อเซิร์ฟเวอร์และ EnumDemo สำหรับฐานข้อมูลแล้วคลิกตกลง
Step 4 - กล่องโต้ตอบถามว่าคุณต้องการสร้างฐานข้อมูลใหม่หรือไม่จะปรากฏขึ้นให้คลิกใช่
Step 5- คลิกถัดไปและสร้างตัวช่วยสร้างฐานข้อมูลจะสร้างภาษานิยามข้อมูล (DDL) สำหรับสร้างฐานข้อมูล ตอนนี้คลิกเสร็จสิ้น
Step 6 - คลิกขวาที่ T-SQL Editor แล้วเลือก Execute
Step 7 - หากต้องการดูสคีมาที่สร้างขึ้นให้คลิกขวาที่ชื่อฐานข้อมูลใน SQL Server Object Explorer และเลือกรีเฟรช
คุณจะเห็นตารางแผนกในฐานข้อมูล
ลองมาดูตัวอย่างต่อไปนี้ซึ่งมีการเพิ่มและบันทึกวัตถุแผนกใหม่ในบริบท จากนั้นเรียกข้อมูลแผนกคอมพิวเตอร์
class Program {
static void Main(string[] args) {
using (var context = new EFEnumModelContainer()) {
context.Departments.Add(new Department { DeptName = DepartmentNames.Physics});
context.Departments.Add(new Department { DeptName = DepartmentNames.Computer});
context.Departments.Add(new Department { DeptName = DepartmentNames.Chemistry});
context.Departments.Add(new Department { DeptName = DepartmentNames.Economics});
context.SaveChanges();
var department = (
from d in context.Departments
where d.DeptName == DepartmentNames.Computer
select d
).FirstOrDefault();
Console.WriteLine(
"Department ID: {0}, Department Name: {1}",
department.DeptID, department.DeptName
);
Console.ReadKey();
}
}
}
เมื่อดำเนินการโค้ดด้านบนคุณจะได้รับผลลัพธ์ต่อไปนี้ -
Department ID: 2, Department Name: Computer
เราขอแนะนำให้คุณดำเนินการตามตัวอย่างข้างต้นในลักษณะทีละขั้นตอนเพื่อความเข้าใจที่ดีขึ้น
Asynchronous programmingเกี่ยวข้องกับการดำเนินการในพื้นหลังเพื่อให้เธรดหลักสามารถดำเนินการต่อไปได้ วิธีนี้เธรดหลักสามารถรักษาการตอบสนองของอินเทอร์เฟซผู้ใช้ในขณะที่เธรดพื้นหลังกำลังประมวลผลงานในมือ
Entity Framework 6.0 สนับสนุนการดำเนินการแบบอะซิงโครนัสสำหรับการสืบค้นและการบันทึกข้อมูล
การดำเนินการแบบอะซิงโครนัสสามารถช่วยแอปพลิเคชันของคุณได้ดังต่อไปนี้ -
- ทำให้แอปพลิเคชันของคุณตอบสนองต่อการโต้ตอบของผู้ใช้มากขึ้น
- ปรับปรุงประสิทธิภาพโดยรวมของแอปพลิเคชันของคุณ
คุณสามารถดำเนินการแบบอะซิงโครนัสได้หลายวิธี แต่คำหลัก async / await ถูกนำมาใช้ใน. NET Framework 4.5 ซึ่งทำให้งานของคุณง่ายขึ้น
สิ่งเดียวที่คุณต้องปฏิบัติตามคือรูปแบบ async / await ตามที่แสดงในส่วนของรหัสต่อไปนี้
ลองดูตัวอย่างต่อไปนี้ (โดยไม่ใช้ async / await) ซึ่งเมธอด DatabaseOperations จะบันทึกนักเรียนใหม่ลงในฐานข้อมูลจากนั้นดึงข้อมูลนักเรียนทั้งหมดจากฐานข้อมูลและในตอนท้ายจะมีการพิมพ์ข้อความเพิ่มเติมบนคอนโซล
class Program {
static void Main(string[] args) {
Console.WriteLine("Database Operations Started");
DatabaseOperations();
Console.WriteLine();
Console.WriteLine("Database Operations Completed");
Console.WriteLine();
Console.WriteLine("Entity Framework Tutorials");
Console.ReadKey();
}
public static void DatabaseOperations() {
using (var context = new UniContextEntities()) {
// Create a new student and save it
context.Students.Add(new Student {
FirstMidName = "Akram",
LastName = "Khan",
EnrollmentDate = DateTime.Parse(DateTime.Today.ToString())});
Console.WriteLine("Calling SaveChanges.");
context.SaveChanges();
Console.WriteLine("SaveChanges completed.");
// Query for all Students ordered by first name
var students = (from s in context.Students
orderby s.FirstMidName select s).ToList();
// Write all students out to Console
Console.WriteLine();
Console.WriteLine("All Student:");
foreach (var student in students) {
string name = student.FirstMidName + " " + student.LastName;
Console.WriteLine(" " + name);
}
}
}
}
เมื่อดำเนินการโค้ดด้านบนคุณจะได้รับผลลัพธ์ต่อไปนี้ -
Calling SaveChanges.
SaveChanges completed.
All Student:
Akram Khan
Ali Khan
Ali Alexander
Arturo Anand
Bill Gates
Gytis Barzdukas
Laura Nornan
Meredith fllonso
Nino Olioetto
Peggy Justice
Yan Li
Entity Framework Tutorials
มาใช้ async ใหม่และรอคำสำคัญและทำการเปลี่ยนแปลงต่อไปนี้กับ Program.cs
เพิ่ม System.Data.Entity namespace ซึ่งจะให้วิธีการขยาย EF async
เพิ่ม System.Threading.Tasks namespace ซึ่งจะช่วยให้เราใช้งานประเภท Task
อัปเดต DatabaseOperations ที่จะทำเครื่องหมายเป็น async และส่งคืนไฟล์ Task.
เรียกใช้ SaveChanges เวอร์ชัน Async และรอให้เสร็จสิ้น
เรียกใช้ ToList เวอร์ชัน Async และรอผล
class Program {
static void Main(string[] args) {
var task = DatabaseOperations();
Console.WriteLine();
Console.WriteLine("Entity Framework Tutorials");
task.Wait();
Console.ReadKey();
}
public static async Task DatabaseOperations() {
using (var context = new UniContextEntities()) {
// Create a new blog and save it
context.Students.Add(new Student {
FirstMidName = "Salman",
LastName = "Khan",
EnrollmentDate = DateTime.Parse(DateTime.Today.ToString())});
Console.WriteLine("Calling SaveChanges.");
await context.SaveChangesAsync();
Console.WriteLine("SaveChanges completed.");
// Query for all Students ordered by first name
var students = await (from s in context.Students
orderby s.FirstMidName select s).ToListAsync();
// Write all students out to Console
Console.WriteLine();
Console.WriteLine("All Student:");
foreach (var student in students) {
string name = student.FirstMidName + " " + student.LastName;
Console.WriteLine(" " + name);
}
}
}
}
ในการดำเนินการจะให้ผลลัพธ์ดังต่อไปนี้
Calling SaveChanges.
Entity Framework Tutorials
SaveChanges completed.
All Student:
Akram Khan
Ali Khan
Ali Alexander
Arturo Anand
Bill Gates
Gytis Barzdukas
Laura Nornan
Meredith fllonso
Nino Olioetto
Peggy Justice
Salman Khan
Yan Li
เมื่อโค้ดเป็นแบบอะซิงโครนัสแล้วคุณสามารถสังเกตขั้นตอนการดำเนินการที่แตกต่างกันของโปรแกรมของคุณได้
SaveChanges เริ่มส่ง Student ใหม่ไปยังฐานข้อมูลจากนั้นเมธอด DatabaseOperations จะส่งกลับ (แม้ว่าจะยังไม่เสร็จสิ้นการดำเนินการ) และขั้นตอนของโปรแกรมในเมธอดหลักยังคง
จากนั้นข้อความจะถูกเขียนลงในคอนโซล
เธรดที่มีการจัดการจะถูกบล็อกในการรอสายจนกว่าการดำเนินการฐานข้อมูลจะเสร็จสิ้น เมื่อดำเนินการเสร็จสิ้นส่วนที่เหลือของ DatabaseOperations ของเราจะถูกดำเนินการ
SaveChanges เสร็จสมบูรณ์
ดึงข้อมูลนักเรียนทั้งหมดจากฐานข้อมูลและเขียนลงในคอนโซล
เราขอแนะนำให้คุณดำเนินการตามตัวอย่างข้างต้นในลักษณะทีละขั้นตอนเพื่อความเข้าใจที่ดีขึ้น
ตอนนี้ Entity Framework ช่วยให้คุณได้รับประโยชน์จาก Entity Framework โดยไม่ต้องบังคับให้ทุกส่วนของแอปพลิเคชันของคุณต้องตระหนักถึง Entity Framework แยกเอนทิตีออกจากโครงสร้างพื้นฐาน คุณสามารถสร้างชั้นเรียนที่สามารถมุ่งเน้นไปที่กฎทางธุรกิจของพวกเขาได้โดยไม่ต้องคำนึงถึงวิธีการคงอยู่ (ที่จัดเก็บข้อมูลและวิธีที่ข้อมูลกลับไปกลับมาระหว่างออบเจ็กต์ของคุณ)
การสร้างเอนทิตีที่เพิกเฉยต่อเนื่อง
ย่อหน้าก่อนหน้านี้อธิบายถึงวิธีการที่ไม่มีความรู้อย่างใกล้ชิดเกี่ยวกับแหล่งที่มาของข้อมูลที่ใช้ สิ่งนี้เน้นถึงสาระสำคัญของความไม่รู้ที่คงอยู่ซึ่งก็คือเมื่อชั้นเรียนของคุณและเลเยอร์แอปพลิเคชันจำนวนมากของเราอยู่รอบ ๆ พวกเขาไม่สนใจว่าข้อมูลจะถูกจัดเก็บอย่างไร
ใน Entity Framework เวอร์ชัน. NET 3.5 หากคุณต้องการใช้คลาสที่มีอยู่ก่อนคุณจะต้องแก้ไขโดยบังคับให้ได้มาจาก EntityObject
ใน. NET 4 สิ่งนี้ไม่จำเป็นอีกต่อไป คุณไม่จำเป็นต้องแก้ไขเอนทิตีของคุณเพื่อให้พวกเขามีส่วนร่วมในการดำเนินการของ Entity Framework
สิ่งนี้ช่วยให้เราสร้างแอปพลิเคชันที่รองรับการเชื่อมต่อแบบหลวม ๆ และการแยกข้อกังวล
ด้วยรูปแบบการเข้ารหัสเหล่านี้ชั้นเรียนของคุณจะเกี่ยวข้องกับงานของตนเองเท่านั้นและหลาย ๆ ชั้นของแอปพลิเคชันของคุณรวมถึง UI จะไม่มีการพึ่งพาตรรกะภายนอกเช่น Entity Framework APIs แต่ API ภายนอกเหล่านั้นสามารถโต้ตอบกับเราได้ เอนทิตี
มี 2 วิธี (เชื่อมต่อและตัดการเชื่อมต่อ) เมื่อยังคงมีเอนทิตีด้วย Entity Framework ทั้งสองทางมีความสำคัญในตัวเอง ในกรณีของสถานการณ์จำลองที่เชื่อมต่อการเปลี่ยนแปลงจะถูกติดตามโดยบริบท แต่ในกรณีของสถานการณ์ที่ขาดการเชื่อมต่อเราจำเป็นต้องแจ้งบริบทเกี่ยวกับสถานะของเอนทิตี
สถานการณ์ที่เชื่อมต่อ
สถานการณ์ที่เชื่อมต่อคือเมื่อมีการดึงข้อมูลเอนทิตีจากฐานข้อมูลและแก้ไขในบริบทเดียวกัน สำหรับสถานการณ์ที่เชื่อมต่อกันให้เราสมมติว่าเรามีบริการ Windows และเรากำลังดำเนินการทางธุรกิจกับเอนทิตีนั้นดังนั้นเราจะเปิดบริบทวนรอบเอนทิตีทั้งหมดดำเนินการทางธุรกิจของเราแล้วบันทึกการเปลี่ยนแปลงด้วยบริบทเดียวกันกับที่เรา เปิดตั้งแต่ต้น
มาดูตัวอย่างต่อไปนี้ที่นักเรียนดึงข้อมูลจากฐานข้อมูลและอัปเดตชื่อของนักเรียนจากนั้นบันทึกการเปลี่ยนแปลงลงในฐานข้อมูล
class Program {
static void Main(string[] args) {
using (var context = new MyContext()) {
var studentList = context.Students.ToList();
foreach (var stdnt in studentList) {
stdnt.FirstMidName = "Edited " + stdnt.FirstMidName;
}
context.SaveChanges();
//// Display all Students from the database
var students = (from s in context.Students
orderby s.FirstMidName select s).ToList<Student>();
Console.WriteLine("Retrieve all Students from the database:");
foreach (var stdnt in students) {
string name = stdnt.FirstMidName + " " + stdnt.LastName;
Console.WriteLine("ID: {0}, Name: {1}", stdnt.ID, name);
}
Console.ReadKey();
}
}
}
เมื่อโค้ดด้านบนถูกคอมไพล์และดำเนินการคุณจะได้รับผลลัพธ์ต่อไปนี้และคุณจะเห็นว่า Edited word ถูกแนบมาก่อนชื่อดังที่แสดงในผลลัพธ์ต่อไปนี้
Retrieve all Students from the database:
ID: 1, Name: Edited Edited Alain Bomer
ID: 2, Name: Edited Edited Mark Upston
สถานการณ์ที่ไม่เชื่อมต่อ
สถานการณ์ที่ไม่เชื่อมต่อคือเมื่อมีการดึงข้อมูลเอนทิตีจากฐานข้อมูลและแก้ไขในบริบทที่แตกต่างกัน สมมติว่าเราต้องการแสดงข้อมูลบางส่วนใน Presentation Layer และเรากำลังใช้แอปพลิเคชัน n-tier ดังนั้นจึงเป็นการดีกว่าที่จะเปิดบริบทดึงข้อมูลและปิดบริบทในที่สุด เนื่องจากที่นี่เราได้ดึงข้อมูลและปิดบริบทเอนทิตีที่เราดึงข้อมูลจึงไม่ถูกติดตามอีกต่อไปและนี่คือสถานการณ์ที่ขาดการเชื่อมต่อ
มาดูโค้ดต่อไปนี้ซึ่งมีการเพิ่มเอนทิตี Student ที่ไม่ได้เชื่อมต่อใหม่ลงในบริบทโดยใช้วิธีการเพิ่ม
class Program {
static void Main(string[] args) {
var student = new Student {
ID = 1001,
FirstMidName = "Wasim",
LastName = "Akram",
EnrollmentDate = DateTime.Parse( DateTime.Today.ToString())
};
using (var context = new MyContext()) {
context.Students.Add(student);
context.SaveChanges();
//// Display all Students from the database
var students = (from s in context.Students
orderby s.FirstMidName select s).ToList<Student>();
Console.WriteLine("Retrieve all Students from the database:");
foreach (var stdnt in students) {
string name = stdnt.FirstMidName + " " + stdnt.LastName;
Console.WriteLine("ID: {0}, Name: {1}", stdnt.ID, name);
}
Console.ReadKey();
}
}
}
เมื่อโค้ดด้านบนถูกคอมไพล์และดำเนินการคุณจะได้รับผลลัพธ์ต่อไปนี้
Retrieve all Students from the database:
ID: 1, Name: Edited Edited Edited Alain Bomer
ID: 2, Name: Edited Edited Edited Mark Upston
ID: 3, Name: Wasim Akram
LINQ ถึงเอนทิตี
แนวคิดที่สำคัญที่สุดอย่างหนึ่งในการทำความเข้าใจ LINQ to Entities คือภาษาที่ใช้ในการประกาศ โฟกัสอยู่ที่การกำหนดว่าคุณต้องการข้อมูลอะไรมากกว่าที่จะได้รับข้อมูล
หมายความว่าคุณสามารถใช้เวลาในการทำงานกับข้อมูลได้มากขึ้นและใช้เวลาน้อยลงในการพยายามหารหัสพื้นฐานที่จำเป็นในการทำงานต่างๆเช่นการเข้าถึงฐานข้อมูล
สิ่งสำคัญคือต้องเข้าใจว่าภาษาที่ใช้ในการประกาศไม่ได้ลบการควบคุมใด ๆ จากนักพัฒนา แต่จะช่วยให้นักพัฒนามุ่งความสนใจไปที่สิ่งที่สำคัญ
LINQ to Entities คีย์เวิร์ดสำคัญ
สิ่งสำคัญคือต้องทราบคำหลักพื้นฐานที่ใช้ในการสร้างแบบสอบถาม LINQ มีคำหลักเพียงไม่กี่คำที่ต้องจำ แต่คุณสามารถรวมเข้าด้วยกันได้หลายวิธีเพื่อให้ได้ผลลัพธ์ที่เฉพาะเจาะจง รายการต่อไปนี้ประกอบด้วยคำหลักพื้นฐานเหล่านี้และให้คำอธิบายง่ายๆของแต่ละคำ
เลขที่ | คำหลักและคำอธิบาย |
---|---|
1 | Ascending ระบุว่าการดำเนินการเรียงลำดับเกิดขึ้นจากองค์ประกอบน้อยที่สุด (หรือต่ำสุด) ของช่วงถึงองค์ประกอบสูงสุดของช่วง โดยปกติเป็นการตั้งค่าเริ่มต้น ตัวอย่างเช่นเมื่อทำการเรียงลำดับตามตัวอักษรการเรียงลำดับจะอยู่ในช่วงตั้งแต่ A ถึง Z |
2 | By ระบุฟิลด์หรือนิพจน์ที่ใช้ในการจัดกลุ่ม ฟิลด์หรือนิพจน์กำหนดคีย์ที่ใช้เพื่อดำเนินการจัดกลุ่ม |
3 | Descending ระบุว่าการดำเนินการเรียงลำดับเกิดขึ้นจากองค์ประกอบที่ยิ่งใหญ่ที่สุด (หรือสูงสุด) ของช่วงไปยังองค์ประกอบที่ต่ำที่สุดของช่วง ตัวอย่างเช่นเมื่อทำการเรียงลำดับตามตัวอักษรการเรียงลำดับจะอยู่ในช่วงตั้งแต่ Z ถึง A |
4 | Equals ใช้ระหว่างส่วนคำสั่งด้านซ้ายและด้านขวาของคำสั่ง join เพื่อรวมแหล่งข้อมูลตามบริบทหลักกับแหล่งข้อมูลบริบทรอง ฟิลด์หรือนิพจน์ทางด้านซ้ายของคีย์เวิร์ดเท่ากับระบุแหล่งข้อมูลหลักในขณะที่ฟิลด์หรือนิพจน์ทางด้านขวาของคีย์เวิร์ดเท่ากับระบุแหล่งข้อมูลรอง |
5 | From ระบุแหล่งข้อมูลที่ใช้เพื่อรับข้อมูลที่ต้องการและกำหนดตัวแปรช่วง ตัวแปรนี้มีจุดประสงค์เดียวกับตัวแปรที่ใช้สำหรับการวนซ้ำ |
6 | Group จัดระเบียบเอาต์พุตเป็นกลุ่มโดยใช้ค่าคีย์ที่คุณระบุ ใช้กลุ่มคำสั่งหลายกลุ่มเพื่อสร้างองค์กรเอาต์พุตหลายระดับ ลำดับของประโยคกลุ่มจะกำหนดความลึกที่ค่าคีย์หนึ่ง ๆ ปรากฏในลำดับการจัดกลุ่ม คุณรวมคำหลักนี้เข้ากับ by เพื่อสร้างบริบทเฉพาะ |
7 | In ใช้ในหลายวิธี ในกรณีนี้คีย์เวิร์ดจะกำหนดแหล่งฐานข้อมูลบริบทที่ใช้สำหรับคิวรี เมื่อทำงานกับการเข้าร่วมคีย์เวิร์ด in จะใช้สำหรับแหล่งฐานข้อมูลบริบทแต่ละแหล่งที่ใช้สำหรับการเข้าร่วม |
8 | Into ระบุตัวระบุที่คุณสามารถใช้เป็นข้อมูลอ้างอิงสำหรับส่วนคำสั่งการสืบค้น LINQ เช่นเข้าร่วมกลุ่มและเลือก |
9 | Join สร้างแหล่งข้อมูลเดียวจากแหล่งข้อมูลที่เกี่ยวข้องสองแหล่งเช่นในการตั้งค่าหลัก / รายละเอียด การรวมสามารถระบุการรวมภายในกลุ่มหรือการรวมด้านซ้ายโดยให้การรวมภายในเป็นค่าเริ่มต้น คุณสามารถอ่านเพิ่มเติมเกี่ยวกับการรวมได้ที่msdn.microsoft.com |
10 | Let กำหนดตัวแปรช่วงที่คุณสามารถใช้เพื่อเก็บผลลัพธ์นิพจน์ย่อยในนิพจน์แบบสอบถาม โดยทั่วไปตัวแปร range จะใช้เพื่อจัดเตรียมเอาต์พุตที่แจกแจงเพิ่มเติมหรือเพื่อเพิ่มประสิทธิภาพของแบบสอบถาม (เพื่อให้งานเฉพาะเช่นการค้นหาค่าตัวพิมพ์เล็กของสตริงไม่จำเป็นต้องทำมากกว่าหนึ่งครั้ง) |
11 | On ระบุฟิลด์หรือนิพจน์ที่ใช้เพื่อใช้การรวม ฟิลด์หรือนิพจน์กำหนดองค์ประกอบที่ใช้ร่วมกันกับแหล่งข้อมูลตามบริบททั้งสอง |
12 | Orderby สร้างลำดับการจัดเรียงสำหรับแบบสอบถาม คุณสามารถเพิ่มคีย์เวิร์ดจากน้อยไปมากหรือมากไปหาน้อยเพื่อควบคุมลำดับของการจัดเรียง ใช้คำสั่ง orderby หลายคำเพื่อสร้างการเรียงลำดับหลายระดับ ลำดับของคำสั่ง orderby จะกำหนดลำดับการจัดการนิพจน์การเรียงลำดับดังนั้นการใช้ลำดับที่แตกต่างกันจะทำให้ได้ผลลัพธ์ที่แตกต่างกัน |
13 | Where กำหนดสิ่งที่ LINQ ควรดึงจากแหล่งข้อมูล คุณใช้นิพจน์บูลีนอย่างน้อยหนึ่งรายการเพื่อกำหนดลักษณะเฉพาะของสิ่งที่จะดึงข้อมูล นิพจน์บูลีนถูกแยกออกจากกันโดยใช้เครื่องหมาย && (AND) และ || (หรือ) ตัวดำเนินการ |
14 | Select กำหนดผลลัพธ์จากแบบสอบถาม LINQ โดยระบุข้อมูลที่จะส่งคืน คำสั่งนี้กำหนดชนิดข้อมูลขององค์ประกอบที่ LINQ ส่งคืนในระหว่างกระบวนการวนซ้ำ |
การฉายภาพ
แบบสอบถามการฉายภาพช่วยเพิ่มประสิทธิภาพของแอปพลิเคชันของคุณโดยการดึงเฉพาะฟิลด์ที่ระบุจากฐานข้อมูลของคุณ
เมื่อคุณมีข้อมูลแล้วคุณอาจต้องการฉายหรือกรองตามความจำเป็นเพื่อกำหนดรูปร่างข้อมูลก่อนที่จะส่งออก
งานหลักของนิพจน์ LINQ to Entities คือการรับข้อมูลและจัดเตรียมเป็นเอาต์พุต
ส่วน“ การพัฒนา LINQ to Entities queries” ของบทนี้แสดงให้เห็นถึงเทคนิคในการดำเนินงานพื้นฐานนี้
มาดูโค้ดต่อไปนี้ว่าจะเรียกข้อมูลรายชื่อนักเรียนใดบ้าง
using (var context = new UniContextEntities()) {
var studentList = from s in context.Students select s;
foreach (var student in studentList) {
string name = student.FirstMidName + " " + student.LastName;
Console.WriteLine("ID : {0}, Name: {1}", student.ID, name);
}
}
วัตถุเดี่ยว
ในการดึงอ็อบเจ็กต์นักเรียนเดี่ยวคุณสามารถใช้วิธีการนับ First () หรือ FirstOrDefault ซึ่งส่งคืนองค์ประกอบแรกของลำดับ ความแตกต่างระหว่าง First และ FirstOrDefault คือ First () จะทำให้เกิดข้อยกเว้นหากไม่มีข้อมูลผลลัพธ์สำหรับเกณฑ์ที่ให้มาในขณะที่ FirstOrDefault () จะส่งคืนค่าเริ่มต้นเป็น null หากไม่มีข้อมูลผลลัพธ์ ในข้อมูลโค้ดด้านล่างนี้นักเรียนคนแรกจากรายชื่อจะถูกดึงข้อมูลซึ่งมีชื่อแรกคือ Ali
using (var context = new UniContextEntities()) {
var student = (from s in context.Students where s.FirstMidName
== "Ali" select s).FirstOrDefault<Student>();
string name = student.FirstMidName + " " + student.LastName;
Console.WriteLine("ID : {0}, Name: {1}", student.ID, name);
}
คุณยังสามารถใช้ Single () หรือ SingleOrDefault เพื่อรับอ็อบเจ็กต์นักเรียนเดี่ยวซึ่งส่งคืนองค์ประกอบเดียวที่เฉพาะเจาะจงของลำดับ ในตัวอย่างต่อไปนี้นักเรียนคนเดียวจะถูกดึงข้อมูลซึ่งมี ID คือ 2
using (var context = new UniContextEntities()) {
var student = (from s in context.Students where s.ID
== 2 select s).SingleOrDefault<Student>();
string name = student.FirstMidName + " " + student.LastName;
Console.WriteLine("ID : {0}, Name: {1}", student.ID, name);
Console.ReadKey();
}
รายชื่อวัตถุ
หากคุณต้องการดึงข้อมูลรายชื่อนักเรียนที่มีนามสกุลของ Ali คุณสามารถใช้ ToList () วิธีการแจงนับ
using (var context = new UniContextEntities()) {
var studentList = (from s in context.Students where s.FirstMidName
== "Ali" select s).ToList();
foreach (var student in studentList) {
string name = student.FirstMidName + " " + student.LastName;
Console.WriteLine("ID : {0}, Name: {1}", student.ID, name);
}
Console.ReadKey();
}
ใบสั่ง
ในการดึงข้อมูล / รายการตามลำดับใด ๆ คุณสามารถใช้คำหลัก orderby ในรหัสต่อไปนี้รายชื่อตัวอย่างของนักเรียนจะถูกเรียกดูตามลำดับจากน้อยไปมาก
using (var context = new UniContextEntities()) {
var studentList = (from s in context.Students orderby
s.FirstMidName ascending select s).ToList();
foreach (var student in studentList) {
string name = student.FirstMidName + " " + student.LastName;
Console.WriteLine("ID : {0}, Name: {1}", student.ID, name);
}
Console.ReadKey();
}
แบบสอบถามกรอบเอนทิตีมาตรฐาน Vs
สมมติว่าคุณมีโมเดล Student ที่มี ID, FirstMidName, LastName และ EnrollmentDate หากคุณต้องการส่งคืนรายชื่อนักเรียนแบบสอบถามมาตรฐานจะส่งคืนฟิลด์ทั้งหมด แต่ถ้าคุณต้องการรับเฉพาะรายชื่อนักเรียนที่มีฟิลด์ ID, FirstMidName และ LastName นี่คือที่ที่คุณควรใช้แบบสอบถามการฉายภาพ ต่อไปนี้เป็นตัวอย่างง่ายๆของแบบสอบถามการฉายภาพ
using (var context = new UniContextEntities()) {
var studentList = from s in context.Students
orderby s.FirstMidName ascending
where s.FirstMidName == "Ali"
select new {s.ID, s.FirstMidName, s.LastName};
foreach (var student in studentList) {
string name = student.FirstMidName + " " + student.LastName;
Console.WriteLine("ID : {0}, Name: {1}", student.ID, name);
}
Console.ReadKey();
}
แบบสอบถามการฉายภาพด้านบนไม่รวมฟิลด์ EnrollmentDate สิ่งนี้จะทำให้แอปพลิเคชันของคุณเร็วขึ้นมาก
ใน Entity Framework 6.0 มีการแนะนำคุณลักษณะใหม่ซึ่งเรียกว่า Logging SQL. ในขณะที่ทำงานกับ Entity Framework จะส่งคำสั่งหรือแบบสอบถาม SQL ที่เทียบเท่าไปยังฐานข้อมูลเพื่อดำเนินการ CRUD (สร้างอ่านอัปเดตและลบ)
คุณลักษณะนี้ของ Entity Framework คือการรวบรวมแบบสอบถาม SQL ที่เทียบเท่าซึ่งสร้างโดย Entity Framework ภายในและจัดให้เป็นผลลัพธ์
ก่อน Entity Framework 6 เมื่อใดก็ตามที่จำเป็นต้องติดตามคิวรีและคำสั่งฐานข้อมูลนักพัฒนาไม่มีทางเลือกนอกจากใช้ยูทิลิตี้การติดตามของบุคคลที่สามหรือเครื่องมือติดตามฐานข้อมูล
ใน Entity Framework 6 คุณลักษณะใหม่นี้มีวิธีง่ายๆโดยการบันทึกการดำเนินการทั้งหมดที่ดำเนินการโดย Entity Framework
กิจกรรมทั้งหมดที่ดำเนินการโดย Entity Framework จะถูกบันทึกโดยใช้ DbContext.Database.Log
มาดูรหัสต่อไปนี้ที่มีการเพิ่มนักเรียนใหม่ลงในฐานข้อมูล
class Program {
static void Main(string[] args) {
using (var context = new UniContextEntities()) {
context.Database.Log = Console.Write;
// Create a new student and save it
context.Students.Add(new Student {
FirstMidName = "Salman",
LastName = "Khan",
EnrollmentDate = DateTime.Parse(DateTime.Today.ToString())
});
context.SaveChanges();
Console.ReadKey();
}
}
}
เมื่อดำเนินการโค้ดด้านบนคุณจะได้รับผลลัพธ์ต่อไปนี้ซึ่งจริงๆแล้วคือบันทึกของกิจกรรมทั้งหมดที่ดำเนินการโดย EF ในโค้ดด้านบน
Opened connection at 10/28/2015 6:27:35 PM +05:00
Started transaction at 10/28/2015 6:27:35 PM +05:00
INSERT [dbo].[Student]([LastName], [FirstMidName], [EnrollmentDate])
VALUES (@0, @1, @2)
SELECT [ID]
FROM [dbo].[Student]
WHERE @@ROWCOUNT > 0 AND [ID] = scope_identity()
-- @0: 'Khan' (Type = String, Size = -1)
-- @1: 'Salman' (Type = String, Size = -1)
-- @2: '10/28/2015 12:00:00 AM' (Type = DateTime)
-- Executing at 10/28/2015 6:27:35 PM +05:00
-- Completed in 5 ms with result: SqlDataReader
Committed transaction at 10/28/2015 6:27:35 PM +05:00
Closed connection at 10/28/2015 6:27:35 PM +05:00
เมื่อคุณสมบัติ Log ถูกตั้งค่ากิจกรรมต่อไปนี้จะถูกบันทึก -
SQL สำหรับคำสั่งประเภทต่างๆเช่นแบบสอบถามรวมถึงการแทรกการอัปเดตและการลบที่สร้างขึ้นโดยเป็นส่วนหนึ่งของ SaveChanges
Parameters
ไม่ว่าจะมีการเรียกใช้คำสั่งแบบอะซิงโครนัสหรือไม่
การประทับเวลาที่ระบุเมื่อคำสั่งเริ่มดำเนินการ
คำสั่งเสร็จสมบูรณ์หรือล้มเหลว
การบ่งชี้บางอย่างของค่าผลลัพธ์
ระยะเวลาโดยประมาณที่ใช้ในการดำเนินการคำสั่ง
การเข้าสู่สถานที่อื่น
หากคุณมีเฟรมเวิร์กการบันทึกอยู่แล้วและกำหนดวิธีการบันทึกคุณสามารถบันทึกไปยังที่อื่นได้
ลองดูตัวอย่างต่อไปนี้ซึ่งเรามี MyLogger คลาสอื่น
class Program {
static void Main(string[] args) {
using (var context = new UniContextEntities()) {
context.Database.Log = s ⇒ MyLogger.Log("EFLoggingDemo", s);
// Create a new student and save it
context.Students.Add(new Student {
FirstMidName = "Salman",
LastName = "Khan",
EnrollmentDate = DateTime.Parse(DateTime.Today.ToString())
});
context.SaveChanges();
Console.ReadKey();
}
}
}
public class MyLogger {
public static void Log(string application, string message) {
Console.WriteLine("Application: {0}, EF Message: {1} ",application, message);
}
}
เราขอแนะนำให้คุณดำเนินการตามตัวอย่างข้างต้นในลักษณะทีละขั้นตอนเพื่อความเข้าใจที่ดีขึ้น
ใน Entity Framework 6.0 มีคุณลักษณะใหม่อื่นที่เรียกว่า Interceptorหรือการสกัดกั้น รหัสการสกัดกั้นถูกสร้างขึ้นตามแนวคิดของinterception interfaces. ตัวอย่างเช่นอินเทอร์เฟซ IDbCommandInterceptor กำหนดเมธอดที่ถูกเรียกก่อนที่ EF จะเรียกใช้ ExecuteNonQuery, ExecuteScalar, ExecuteReader และวิธีการที่เกี่ยวข้อง
Entity Framework สามารถเปล่งประกายได้อย่างแท้จริงโดยใช้การสกัดกั้น การใช้วิธีนี้ช่วยให้คุณสามารถเก็บข้อมูลได้มากขึ้นชั่วคราวโดยไม่ต้องแก้รหัสของคุณ
ในการดำเนินการนี้คุณต้องสร้างตัวสกัดกั้นที่กำหนดเองและลงทะเบียนตามนั้น
เมื่อสร้างคลาสที่ใช้อินเทอร์เฟซ IDbCommandInterceptor แล้วจะสามารถลงทะเบียนกับ Entity Framework โดยใช้คลาส DbInterception
อินเทอร์เฟซ IDbCommandInterceptor มีหกวิธีและคุณต้องใช้วิธีการเหล่านี้ทั้งหมด ต่อไปนี้เป็นการใช้งานขั้นพื้นฐานของวิธีการเหล่านี้
มาดูโค้ดต่อไปนี้ที่ใช้อินเทอร์เฟซ IDbCommandInterceptor
public class MyCommandInterceptor : IDbCommandInterceptor {
public static void Log(string comm, string message) {
Console.WriteLine("Intercepted: {0}, Command Text: {1} ", comm, message);
}
public void NonQueryExecuted(DbCommand command,
DbCommandInterceptionContext<int> interceptionContext) {
Log("NonQueryExecuted: ", command.CommandText);
}
public void NonQueryExecuting(DbCommand command,
DbCommandInterceptionContext<int> interceptionContext) {
Log("NonQueryExecuting: ", command.CommandText);
}
public void ReaderExecuted(DbCommand command,
DbCommandInterceptionContext<DbDataReader> interceptionContext) {
Log("ReaderExecuted: ", command.CommandText);
}
public void ReaderExecuting(DbCommand command,
DbCommandInterceptionContext<DbDataReader> interceptionContext) {
Log("ReaderExecuting: ", command.CommandText);
}
public void ScalarExecuted(DbCommand command,
DbCommandInterceptionContext<object> interceptionContext) {
Log("ScalarExecuted: ", command.CommandText);
}
public void ScalarExecuting(DbCommand command,
DbCommandInterceptionContext<object> interceptionContext) {
Log("ScalarExecuting: ", command.CommandText);
}
}
การลงทะเบียน Interceptors
เมื่อสร้างคลาสที่ใช้อินเทอร์เฟซการสกัดกั้นอย่างน้อยหนึ่งคลาสแล้วจะสามารถลงทะเบียนกับ EF โดยใช้คลาส DbInterception ดังที่แสดงในโค้ดต่อไปนี้
DbInterception.Add(new MyCommandInterceptor());
นอกจากนี้ยังสามารถลงทะเบียนตัวสกัดกั้นที่ระดับโดเมนแอปโดยใช้การกำหนดค่าตามรหัส DbConfiguration ดังที่แสดงในรหัสต่อไปนี้
public class MyDBConfiguration : DbConfiguration {
public MyDBConfiguration() {
DbInterception.Add(new MyCommandInterceptor());
}
}
คุณยังสามารถกำหนดค่าไฟล์กำหนดค่า interceptor โดยใช้รหัส -
<entityFramework>
<interceptors>
<interceptor type = "EFInterceptDemo.MyCommandInterceptor, EFInterceptDemo"/>
</interceptors>
</entityFramework>
การสนับสนุนประเภทเชิงพื้นที่ถูกนำมาใช้ใน Entity Framework 5 ชุดของตัวดำเนินการรวมอยู่ด้วยเพื่อให้แบบสอบถามวิเคราะห์ข้อมูลเชิงพื้นที่ ตัวอย่างเช่นข้อความค้นหาสามารถกรองตามระยะทางระหว่างสถานที่ตั้งทางภูมิศาสตร์สองแห่ง
Entity Framework จะอนุญาตให้ประเภทข้อมูลเชิงพื้นที่ใหม่เปิดเผยเป็นคุณสมบัติบนคลาสของคุณและแมปกับคอลัมน์เชิงพื้นที่ในฐานข้อมูลของคุณ
นอกจากนี้คุณยังสามารถเขียนข้อความค้นหา LINQ ที่ใช้ตัวดำเนินการเชิงพื้นที่เพื่อกรองเรียงลำดับและจัดกลุ่มตามการคำนวณเชิงพื้นที่ที่ดำเนินการในฐานข้อมูล
มีข้อมูลเชิงพื้นที่สองประเภทหลัก -
ประเภทข้อมูลทางภูมิศาสตร์จะจัดเก็บข้อมูลรูปไข่ตัวอย่างเช่นพิกัดละติจูด GPS และลองจิจูด
ชนิดข้อมูลเรขาคณิตแสดงถึงระบบพิกัดแบบยุคลิด (แบน)
ลองมาดูตัวอย่างของสนามคริกเก็ตต่อไปนี้
Step 1 - สร้างโครงการใหม่จากไฟล์→ใหม่→ตัวเลือกเมนูโครงการ
Step 2 - ในบานหน้าต่างด้านซ้ายเลือกแอปพลิเคชันคอนโซล
Step 3 - คลิกขวาที่ชื่อโครงการและเลือกจัดการ NuGet Packages ...
Step 4 - ติดตั้ง Entity Framework
Step 5 - เพิ่มการอ้างอิงไปยังแอสเซมบลี System.Data.Entity และเพิ่ม System.Data.Spatial โดยใช้คำสั่งสำหรับชนิดข้อมูลเชิงพื้นที่
Step 6 - เพิ่มคลาสต่อไปนี้ในไฟล์ Program.cs
public class CricketGround {
public int ID { get; set; }
public string Name { get; set; }
public DbGeography Location { get; set; }
}
Step 7 - นอกเหนือจากการกำหนดเอนทิตีคุณต้องกำหนดคลาสที่มาจาก DbContext และแสดงคุณสมบัติ DbSet <TEntity>
ใน Program.cs เพิ่มนิยามบริบท
public partial class CricketGroundContext : DbContext {
public DbSet<CricketGround> CricketGrounds { get; set; }
}
Step 8 - เพิ่มรหัสต่อไปนี้ลงในฟังก์ชันหลักซึ่งจะเพิ่มวัตถุ CricketGround ใหม่สองรายการในบริบท
class Program {
static void Main(string[] args) {
using (var context = new CricketGroundContext()) {
context.CricketGrounds.Add(new CricketGround() {
Name = "Shalimar Cricket Ground",
Location = DbGeography.FromText("POINT(-122.336106 47.605049)"),
});
context.CricketGrounds.Add(new CricketGround() {
Name = "Marghazar Stadium", Location = DbGeography
.FromText("POINT(-122.335197 47.646711)"),
});
context.SaveChanges();
var myLocation = DbGeography.FromText("POINT(-122.296623 47.640405)");
var cricketGround = (from cg in context.CricketGrounds
orderby cg.Location.Distance(myLocation) select cg).FirstOrDefault();
Console.WriteLine("The closest Cricket Ground to you is: {0}.", cricketGround.Name);
}
}
}
คุณสมบัติเชิงพื้นที่เริ่มต้นโดยใช้วิธี DbGeography.FromText จุดภูมิศาสตร์ที่แสดงเป็น WellKnownText จะถูกส่งผ่านไปยังเมธอดจากนั้นจึงบันทึกข้อมูล หลังจากนั้นวัตถุ CricketGround จะถูกเรียกคืนโดยที่ตำแหน่งของมันอยู่ใกล้กับตำแหน่งที่ระบุมากที่สุด
เมื่อดำเนินการโค้ดด้านบนคุณจะได้รับผลลัพธ์ต่อไปนี้ -
The closest Cricket Ground to you is: Marghazar Stadium
เราขอแนะนำให้คุณดำเนินการตามตัวอย่างข้างต้นในลักษณะทีละขั้นตอนเพื่อความเข้าใจที่ดีขึ้น
การสืบทอดทำให้สามารถสร้างแบบจำลองที่ซับซ้อนซึ่งสะท้อนวิธีคิดของนักพัฒนาได้ดีขึ้นและยังลดงานที่ต้องใช้ในการโต้ตอบกับโมเดลเหล่านั้น การสืบทอดที่ใช้กับเอนทิตีมีจุดประสงค์เดียวกับการสืบทอดที่ใช้กับคลาสดังนั้นนักพัฒนาจึงทราบพื้นฐานของการทำงานของคุณลักษณะนี้อยู่แล้ว
ลองดูตัวอย่างต่อไปนี้และโดยการสร้างโครงการแอปพลิเคชันคอนโซลใหม่
Step 1 - เพิ่ม ADO.NET Entity Data Model โดยคลิกขวาที่ชื่อโปรเจ็กต์แล้วเลือกเพิ่ม→รายการใหม่ ...
Step 2 - เพิ่มเอนทิตีหนึ่งรายการและตั้งชื่อเป็นบุคคลโดยทำตามขั้นตอนทั้งหมดที่กล่าวถึงในบทแนวทางแรกของโมเดล
Step 3 - เพิ่มคุณสมบัติสเกลาร์ตามที่แสดงในภาพต่อไปนี้
Step 4 - เราจะเพิ่มอีกสองเอนทิตี Student และ Teacherซึ่งจะสืบทอดคุณสมบัติจาก Person Table
Step 5 - ตอนนี้เพิ่มเอนทิตีนักเรียนและเลือกบุคคลจากคอมโบบ็อกซ์ประเภทพื้นฐานดังที่แสดงในภาพต่อไปนี้
Step 6 - เพิ่มเอนทิตีครูในทำนองเดียวกัน
Step 7 - ตอนนี้เพิ่มคุณสมบัติสเกลาร์ EnrollmentDate ให้กับเอนทิตีนักเรียนและคุณสมบัติ HireDate ไปยังเอนทิตีครู
Step 8 - มาสร้างฐานข้อมูลกันเลย
Step 9 - คลิกขวาที่พื้นผิวการออกแบบและเลือกสร้างฐานข้อมูลจากแบบจำลอง ...
Step 10- ในการสร้างฐานข้อมูลใหม่ให้คลิกที่การเชื่อมต่อใหม่ ... กล่องโต้ตอบต่อไปนี้จะเปิดขึ้น คลิกตกลง
Step 11- คลิกเสร็จสิ้น สิ่งนี้จะเพิ่มไฟล์ * .edmx.sql ในโปรเจ็กต์ คุณสามารถรันสคริปต์ DDL ใน Visual Studio ได้โดยเปิดไฟล์. sql ตอนนี้คลิกขวาและเลือกดำเนินการ
Step 12 - ไปที่ server explorer คุณจะเห็นว่าฐานข้อมูลถูกสร้างขึ้นด้วยตารางสามตารางที่ระบุไว้
Step 13 - คุณยังสามารถดูได้ว่าคลาสโดเมนต่อไปนี้จะถูกสร้างขึ้นโดยอัตโนมัติ
public partial class Person {
public int ID { get; set; }
public string FirstMidName { get; set; }
public string LastName { get; set; }
}
public partial class Student : Person {
public System.DateTime EnrollmentDate { get; set; }
}
public partial class Teacher : Person {
public System.DateTime HireDate { get; set; }
}
ต่อไปนี้เป็นคลาสบริบท
public partial class InheritanceModelContainer : DbContext {
public InheritanceModelContainer() :
base("name = InheritanceModelContainer") {}
protected override void OnModelCreating(DbModelBuilder modelBuilder) {
throw new UnintentionalCodeFirstException();
}
public virtual DbSet<Person> People { get; set; }
}
มาเพิ่มนักเรียนและครูบางส่วนในฐานข้อมูลแล้วดึงข้อมูลจากฐานข้อมูล
class Program {
static void Main(string[] args) {
using (var context = new InheritanceModelContainer()) {
var student = new Student {
FirstMidName = "Meredith",
LastName = "Alonso",
EnrollmentDate = DateTime.Parse(DateTime.Today.ToString())
};
context.People.Add(student);
var student1 = new Student {
FirstMidName = "Arturo",
LastName = "Anand",
EnrollmentDate = DateTime.Parse(DateTime.Today.ToString())
};
context.People.Add(student1);
var techaer = new Teacher {
FirstMidName = "Peggy",
LastName = "Justice",
HireDate = DateTime.Parse(DateTime.Today.ToString())
};
context.People.Add(techaer);
var techaer1 = new Teacher {
FirstMidName = "Yan",
LastName = "Li",
HireDate = DateTime.Parse(DateTime.Today.ToString())
};
context.People.Add(techaer1);
context.SaveChanges();
}
}
}
มีการเพิ่มนักเรียนและครูในฐานข้อมูล NT เพื่อดึงข้อมูลนักเรียนและครูไฟล์OfType จำเป็นต้องใช้วิธีการซึ่งจะส่งคืนนักเรียนและครูที่เกี่ยวข้องกับแผนกที่ระบุ
Console.WriteLine("All students in database");
Console.WriteLine("");
foreach (var student in context.People.OfType<Student>()) {
string name = student.FirstMidName + " " + student.LastName;
Console.WriteLine("ID: {0}, Name: {1}, \tEnrollment Date {2} ",
student.ID, name, student.EnrollmentDate.ToString());
}
Console.WriteLine("");
Console.WriteLine("************************************************************ *****");
Console.WriteLine("");
Console.WriteLine("All teachers in database");
Console.WriteLine("");
foreach (var teacher in context.People.OfType<Teacher>()) {
string name = teacher.FirstMidName + " " + teacher.LastName;
Console.WriteLine("ID: {0}, Name: {1}, \tHireDate {2} ",
teacher.ID, name, teacher.HireDate.ToString());
}
Console.WriteLine("");
Console.WriteLine("************************************************************ *****");
Console.ReadKey();
ในแบบสอบถามแรกเมื่อคุณใช้ OfType <Student> () คุณจะไม่สามารถเข้าถึง HireDate ได้เนื่องจากคุณสมบัติ HireDate เป็นส่วนหนึ่งของ Teacher Entity และคุณสมบัติ EnrollmentDate ในทำนองเดียวกันจะไม่สามารถเข้าถึงได้เมื่อคุณใช้ OfType <Teacher> ()
เมื่อดำเนินการโค้ดด้านบนคุณจะได้รับผลลัพธ์ต่อไปนี้ -
All students in database
ID: 1, Name: Meredith Alonso, Enrollment Date 10/30/2015 12:00:00 AM
ID: 2, Name: Arturo Anand, Enrollment Date 10/30/2015 12:00:00 AM
*****************************************************************
All teachers in database
ID: 3, Name: Peggy Justice, HireDate 10/30/2015 12:00:00 AM
ID: 4, Name: Yan Li, HireDate 10/30/2015 12:00:00 AM
*****************************************************************
เราขอแนะนำให้คุณดำเนินการตามตัวอย่างข้างต้นในลักษณะทีละขั้นตอนเพื่อความเข้าใจที่ดีขึ้น
ใน Entity Framework 5 และ Entity Framework เวอร์ชันก่อนหน้ารหัสจะถูกแบ่งระหว่างไลบรารีหลัก (โดยหลักคือ System.Data.Entity.dll) ที่จัดส่งเป็นส่วนหนึ่งของ. NET Framework และไลบรารีเพิ่มเติม (โดยหลักคือ EntityFramework.dll) ถูกแจกจ่ายและ จัดส่งโดยใช้ NuGet ดังแสดงในแผนภาพต่อไปนี้
ใน Entity Framework 6 นั้น API หลักซึ่งก่อนหน้านี้เคยเป็นส่วนหนึ่งของเฟรมเวิร์ก. NET จะถูกจัดส่งและแจกจ่ายเป็นส่วนหนึ่งของแพ็คเกจ NuGet
สิ่งนี้จำเป็นเพื่อให้ Entity Framework เป็นโอเพ่นซอร์ส อย่างไรก็ตามจะต้องสร้างแอปพลิเคชันใหม่เมื่อใดก็ตามที่จำเป็นต้องโยกย้ายหรืออัปเกรดแอปพลิเคชันของคุณจาก Entity Framework เวอร์ชันเก่าไปเป็น EF 6
ขั้นตอนการย้ายข้อมูลนั้นตรงไปตรงมาหากแอปพลิเคชันของคุณใช้ DbContext ซึ่งจัดส่งใน EF 4.1 และใหม่กว่า แต่ถ้าแอปพลิเคชันของคุณเป็น ObjectContext ก็ต้องทำงานเพิ่มขึ้นเล็กน้อย
มาดูขั้นตอนต่อไปนี้ที่คุณต้องทำเพื่ออัปเกรดแอปพลิเคชันที่มีอยู่เป็น EF6
Step 1 - ขั้นตอนแรกคือกำหนดเป้าหมาย. NET Framework 4.5.2 และคลิกขวาที่โปรเจ็กต์ของคุณในภายหลังและเลือกคุณสมบัติ
Step 2 - คลิกขวาที่โครงการของคุณอีกครั้งและเลือกจัดการ NuGet Packages ...
Step 3- ใต้แท็บออนไลน์เลือก EntityFramework แล้วคลิกติดตั้ง ตรวจสอบให้แน่ใจว่าการอ้างอิงแอสเซมบลีไปยัง System.Data.Entity.dll ถูกลบออก
เมื่อคุณติดตั้งแพ็คเกจ EF6 NuGet ควรลบการอ้างอิงถึง System.Data.Entity ออกจากโปรเจ็กต์ของคุณโดยอัตโนมัติ
Step 4 - หากคุณมีโมเดลใด ๆ ที่สร้างด้วย EF Designer คุณจะต้องอัปเดตเทมเพลตการสร้างโค้ดเพื่อสร้างโค้ดที่เข้ากันได้กับ EF6
Step 5 - ใน Solution Explorer ของคุณภายใต้ไฟล์ edmx ของคุณให้ลบเทมเพลตการสร้างโค้ดที่มีอยู่ซึ่งโดยทั่วไปจะมีชื่อว่า <edmx_file_name> .tt และ <edmx_file_name> .Context.tt
Step 6 - เปิดแบบจำลองของคุณใน EF Designer คลิกขวาที่พื้นผิวการออกแบบแล้วเลือกเพิ่มรหัสสร้างรายการ ...
Step 7 - เพิ่มเทมเพลตการสร้างโค้ด EF 6.x ที่เหมาะสม
นอกจากนี้ยังสร้างรหัสที่เข้ากันได้กับ EF6 โดยอัตโนมัติ
หากแอปพลิเคชันของคุณใช้ EF 4.1 หรือใหม่กว่าคุณไม่จำเป็นต้องเปลี่ยนแปลงอะไรในโค้ดเนื่องจากเนมสเปซสำหรับประเภท DbContext และ Code First ไม่ได้เปลี่ยนแปลง
แต่ถ้าแอปพลิเคชันของคุณใช้ Entity Framework เวอร์ชันเก่าให้พิมพ์ ObjectContext ที่เคยอยู่ใน System.Data.Entity.dll ไปยังเนมสเปซใหม่
Step 8 - คุณจะต้องอัปเดตคำสั่งการใช้หรือนำเข้าเพื่อสร้างเทียบกับ EF6
กฎทั่วไปสำหรับการเปลี่ยนแปลงเนมสเปซคือประเภทใด ๆ ใน System.Data. * จะถูกย้ายไปที่ System.Data.Entity.Core. * ต่อไปนี้คือบางส่วนของพวกเขา -
- System.Data.EntityException ⇒ System.Data.Entity.Core.EntityException
- System.Data.Objects.ObjectContext ⇒ System.Data.Entity.Core.Objects.ObjectContext;
- System.Data.Objects.DataClasses.RelationshipManager ⇒ System.Data.Entity.Core.Objects.DataClasses.RelationshipManager;
บางชนิดอยู่ในเนมสเปซหลักเนื่องจากไม่ได้ใช้โดยตรงสำหรับแอปพลิเคชันที่ใช้ DbContext ส่วนใหญ่
- System.Data.EntityState ⇒ System.Data.Entity.EntityState
- System.Data.Objects.DataClasses.EdmFunctionAttribute ⇒ System.Data.Entity.DbFunctionAttribute
โครงการ Entity Framework ที่คุณมีอยู่จะทำงานใน Entity Framework 6.0 โดยไม่มีการเปลี่ยนแปลงที่สำคัญใด ๆ
การโหลดอย่างกระตือรือร้นเป็นกระบวนการที่เคียวรีสำหรับเอนทิตีประเภทหนึ่งจะโหลดเอนทิตีที่เกี่ยวข้องเป็นส่วนหนึ่งของคิวรีด้วย การโหลดที่กระตือรือร้นทำได้โดยการใช้ไฟล์Include method.
หมายความว่าการร้องขอข้อมูลที่เกี่ยวข้องจะถูกส่งคืนพร้อมกับผลลัพธ์การสืบค้นจากฐานข้อมูล มีการเชื่อมต่อเพียงครั้งเดียวกับแหล่งข้อมูลข้อมูลจำนวนมากจะถูกส่งกลับในแบบสอบถามเริ่มต้น
ตัวอย่างเช่นเมื่อสอบถามนักเรียนให้โหลดการลงทะเบียนของตนอย่างกระตือรือร้น นักเรียนและการลงทะเบียนของพวกเขาจะถูกเรียกค้นในแบบสอบถามเดียว
มาดูตัวอย่างต่อไปนี้ซึ่งนักเรียนทุกคนที่มีการลงทะเบียนตามลำดับจะถูกดึงข้อมูลจากฐานข้อมูลโดยใช้การโหลดอย่างกระตือรือร้น
class Program {
static void Main(string[] args) {
using (var context = new UniContextEntities()) {
// Load all students and related enrollments
var students = context.Students
.Include(s ⇒ s.Enrollments).ToList();
foreach (var student in students) {
string name = student.FirstMidName + " " + student.LastName;
Console.WriteLine("ID: {0}, Name: {1}", student.ID, name);
foreach (var enrollment in student.Enrollments) {
Console.WriteLine("Enrollment ID: {0}, Course ID: {1}",
enrollment.EnrollmentID, enrollment.CourseID);
}
}
Console.ReadKey();
}
}
}
เมื่อโค้ดด้านบนถูกคอมไพล์และดำเนินการคุณจะได้รับผลลัพธ์ต่อไปนี้
ID: 1, Name: Ali Alexander
Enrollment ID: 1, Course ID: 1050
Enrollment ID: 2, Course ID: 4022
Enrollment ID: 3, Course ID: 4041
ID: 2, Name: Meredith Alonso
Enrollment ID: 4, Course ID: 1045
Enrollment ID: 5, Course ID: 3141
Enrollment ID: 6, Course ID: 2021
ID: 3, Name: Arturo Anand
Enrollment ID: 7, Course ID: 1050
ID: 4, Name: Gytis Barzdukas
Enrollment ID: 8, Course ID: 1050
Enrollment ID: 9, Course ID: 4022
ด้านล่างนี้คือรูปแบบอื่น ๆ ของการค้นหาที่ต้องการโหลดซึ่งสามารถใช้ได้
// Load one Student and its related enrollments
var student1 = context.Students
.Where(s ⇒ s.FirstMidName == "Ali")
.Include(s ⇒ s.Enrollments).FirstOrDefault();
// Load all Students and related enrollments
// using a string to specify the relationship
var studentList = context.Students
.Include("Enrollments").ToList();
// Load one Student and its related enrollments
// using a string to specify the relationship
var student = context.Students
.Where(s ⇒ s.FirstMidName == "Salman")
.Include("Enrollments").FirstOrDefault();
หลายระดับ
นอกจากนี้ยังสามารถโหลดเอนทิตีที่เกี่ยวข้องหลายระดับได้อย่างกระตือรือร้น ข้อความค้นหาต่อไปนี้แสดงตัวอย่างของนักเรียนการลงทะเบียนและหลักสูตร
// Load all Students, all related enrollments, and all related courses
var studentList = context.Students
.Include(s ⇒ s.Enrollments.Select(c ⇒ c.Course)).ToList();
// Load all Students, all related enrollments, and all related courses
// using a string to specify the relationships
var students = context.Students
.Include("Enrollments.Course").ToList();
เราขอแนะนำให้คุณดำเนินการตามตัวอย่างข้างต้นในลักษณะทีละขั้นตอนเพื่อความเข้าใจที่ดีขึ้น
การโหลดแบบขี้เกียจเป็นกระบวนการที่เอนทิตีหรือคอลเลกชันของเอนทิตีถูกโหลดโดยอัตโนมัติจากฐานข้อมูลในครั้งแรกที่มีการเข้าถึงคุณสมบัติที่อ้างถึงเอนทิตี / เอนทิตี การโหลดอย่างขี้เกียจหมายถึงการโหลดข้อมูลที่เกี่ยวข้องล่าช้าจนกว่าคุณจะร้องขอโดยเฉพาะ
เมื่อใช้ชนิดเอนทิตี POCO การโหลดแบบขี้เกียจทำได้โดยการสร้างอินสแตนซ์ของประเภทพร็อกซีที่ได้รับจากนั้นแทนที่คุณสมบัติเสมือนเพื่อเพิ่มเบ็ดการโหลด
การโหลดขี้เกียจเป็นค่าเริ่มต้นค่อนข้างมาก
หากคุณออกจากการกำหนดค่าเริ่มต้นและไม่ได้บอก Entity Framework อย่างชัดเจนในแบบสอบถามของคุณว่าคุณต้องการอย่างอื่นนอกเหนือจากการโหลดแบบขี้เกียจการโหลดแบบขี้เกียจคือสิ่งที่คุณจะได้รับ
ตัวอย่างเช่นเมื่อใช้คลาสเอนทิตี Student การลงทะเบียนที่เกี่ยวข้องจะถูกโหลดในครั้งแรกที่เข้าถึงคุณสมบัติการนำทางการลงทะเบียน
คุณสมบัติการนำทางควรกำหนดเป็นสาธารณะเสมือน บริบทจะNOT ขี้เกียจโหลดถ้าคุณสมบัติไม่ได้กำหนดเป็นเสมือน
ต่อไปนี้เป็นชั้นเรียนนักเรียนที่มีคุณสมบัติการนำทางของการลงทะเบียน
public partial class Student {
public Student() {
this.Enrollments = new HashSet<Enrollment>();
}
public int ID { get; set; }
public string LastName { get; set; }
public string FirstMidName { get; set; }
public System.DateTime EnrollmentDate { get; set; }
public virtual ICollection<Enrollment> Enrollments { get; set; }
}
มาดูตัวอย่างง่ายๆในการโหลดรายชื่อนักเรียนจากฐานข้อมูลก่อนจากนั้นจะโหลดการลงทะเบียนของนักเรียนคนใดคนหนึ่งเมื่อใดก็ตามที่คุณต้องการ
class Program {
static void Main(string[] args) {
using (var context = new UniContextEntities()) {
//Loading students only
IList<Student> students = context.Students.ToList<Student>();
foreach (var student in students) {
string name = student.FirstMidName + " " + student.LastName;
Console.WriteLine("ID: {0}, Name: {1}", student.ID, name);
foreach (var enrollment in student.Enrollments) {
Console.WriteLine("Enrollment ID: {0}, Course ID: {1}",
enrollment.EnrollmentID, enrollment.CourseID);
}
}
Console.ReadKey();
}
}
}
เมื่อโค้ดด้านบนถูกคอมไพล์และดำเนินการคุณจะได้รับผลลัพธ์ต่อไปนี้
ID: 1, Name: Ali Alexander
Enrollment ID: 1, Course ID: 1050
Enrollment ID: 2, Course ID: 4022
Enrollment ID: 3, Course ID: 4041
ID: 2, Name: Meredith Alonso
Enrollment ID: 4, Course ID: 1045
Enrollment ID: 5, Course ID: 3141
Enrollment ID: 6, Course ID: 2021
ID: 3, Name: Arturo Anand
Enrollment ID: 7, Course ID: 1050
ID: 4, Name: Gytis Barzdukas
Enrollment ID: 8, Course ID: 1050
Enrollment ID: 9, Course ID: 4022
ID: 5, Name: Yan Li
Enrollment ID: 10, Course ID: 4041
ID: 6, Name: Peggy Justice
Enrollment ID: 11, Course ID: 1045
ID: 7, Name: Laura Norman
Enrollment ID: 12, Course ID: 3141
ปิด Lazy Loading
การโหลดแบบขี้เกียจและการทำให้เป็นอนุกรมไม่สามารถผสมกันได้และหากคุณไม่ระมัดระวังคุณสามารถค้นหาฐานข้อมูลทั้งหมดของคุณได้เพียงเพราะเปิดใช้งานการโหลดแบบขี้เกียจ เป็นแนวทางปฏิบัติที่ดีในการปิดการโหลดแบบเกียจคร้านก่อนที่คุณจะจัดลำดับเอนทิตี
การปิดสำหรับคุณสมบัติการนำทางเฉพาะ
สามารถปิดการโหลดคอลเล็กชันการลงทะเบียนได้โดยการทำให้คุณสมบัติการลงทะเบียนไม่เสมือนจริงดังที่แสดงในตัวอย่างต่อไปนี้
public partial class Student {
public Student() {
this.Enrollments = new HashSet<Enrollment>();
}
public int ID { get; set; }
public string LastName { get; set; }
public string FirstMidName { get; set; }
public System.DateTime EnrollmentDate { get; set; }
public ICollection<Enrollment> Enrollments { get; set; }
}
ปิดสำหรับเอนทิตีทั้งหมด
Lazy loading สามารถปิดได้สำหรับเอนทิตีทั้งหมดในบริบทโดยตั้งค่าแฟล็กบนคุณสมบัติ Configuration เป็น false ดังที่แสดงในตัวอย่างต่อไปนี้
public partial class UniContextEntities : DbContext {
public UniContextEntities(): base("name=UniContextEntities") {
this.Configuration.LazyLoadingEnabled = false;
}
protected override void OnModelCreating(DbModelBuilder modelBuilder) {
throw new UnintentionalCodeFirstException();
}
}
หลังจากปิดการโหลดแบบขี้เกียจตอนนี้เมื่อคุณเรียกใช้ตัวอย่างด้านบนอีกครั้งคุณจะเห็นว่าการลงทะเบียนไม่ได้โหลดและดึงเฉพาะข้อมูลนักเรียนเท่านั้น
ID: 1, Name: Ali Alexander
ID: 2, Name: Meredith Alons
ID: 3, Name: Arturo Anand
ID: 4, Name: Gytis Barzduka
ID: 5, Name: Yan Li
ID: 6, Name: Peggy Justice
ID: 7, Name: Laura Norman
ID: 8, Name: Nino Olivetto
เราขอแนะนำให้คุณดำเนินการตามตัวอย่างข้างต้นในลักษณะทีละขั้นตอนเพื่อความเข้าใจที่ดีขึ้น
เมื่อคุณปิดใช้งานการโหลดแบบขี้เกียจคุณยังคงสามารถโหลดเอนทิตีที่เกี่ยวข้องได้อย่างเฉื่อยชา แต่ต้องดำเนินการด้วยการเรียกที่ชัดเจน
ซึ่งแตกต่างจากการโหลดแบบขี้เกียจไม่มีความคลุมเครือหรือความสับสนเกี่ยวกับเวลาที่เรียกใช้แบบสอบถาม
ในการดำเนินการดังกล่าวให้คุณใช้วิธีการโหลดในรายการของเอนทิตีที่เกี่ยวข้อง
สำหรับความสัมพันธ์แบบหนึ่งต่อกลุ่มเรียกใช้วิธีการโหลดบนคอลเล็กชัน
และสำหรับความสัมพันธ์แบบหนึ่งต่อกลุ่มให้เรียกใช้เมธอด Load ใน Reference
ลองมาดูตัวอย่างต่อไปนี้ซึ่งปิดใช้งานการโหลดแบบขี้เกียจแล้วนักเรียนที่มีชื่ออาลีจะถูกดึงออกมา
จากนั้นข้อมูลนักเรียนจะถูกเขียนบนคอนโซล หากคุณดูรหัสข้อมูลการลงทะเบียนจะถูกเขียนด้วย แต่เอนทิตีการลงทะเบียนยังไม่ได้โหลดดังนั้น foreach loop จะไม่ถูกดำเนินการ
หลังจากนั้นเอนทิตีการลงทะเบียนจะถูกโหลดอย่างชัดเจนในขณะนี้ข้อมูลของนักเรียนและข้อมูลการลงทะเบียนจะถูกเขียนบนหน้าต่างคอนโซล
class Program {
static void Main(string[] args) {
using (var context = new UniContextEntities()) {
context.Configuration.LazyLoadingEnabled = false;
var student = (from s in context.Students where s.FirstMidName ==
"Ali" select s).FirstOrDefault<Student>();
string name = student.FirstMidName + " " + student.LastName;
Console.WriteLine("ID: {0}, Name: {1}", student.ID, name);
foreach (var enrollment in student.Enrollments) {
Console.WriteLine("Enrollment ID: {0}, Course ID: {1}",
enrollment.EnrollmentID, enrollment.CourseID);
}
Console.WriteLine();
Console.WriteLine("Explicitly loaded Enrollments");
Console.WriteLine();
context.Entry(student).Collection(s ⇒ s.Enrollments).Load();
Console.WriteLine("ID: {0}, Name: {1}", student.ID, name);
foreach (var enrollment in student.Enrollments) {
Console.WriteLine("Enrollment ID: {0}, Course ID: {1}",
enrollment.EnrollmentID, enrollment.CourseID);
}
Console.ReadKey();
}
}
}
เมื่อดำเนินการตามตัวอย่างข้างต้นคุณจะได้รับผลลัพธ์ต่อไปนี้ ข้อมูลนักเรียนอันดับแรกเท่านั้นที่จะปรากฏขึ้นและหลังจากโหลดเอนทิตีการลงทะเบียนอย่างชัดเจนทั้งข้อมูลนักเรียนและข้อมูลการลงทะเบียนที่เกี่ยวข้องจะปรากฏขึ้น
ID: 1, Name: Ali Alexander
Explicitly loaded Enrollments
ID: 1, Name: Ali Alexander
Enrollment ID: 1, Course ID: 1050
Enrollment ID: 2, Course ID: 4022
Enrollment ID: 3, Course ID: 4041
เราขอแนะนำให้คุณดำเนินการตามตัวอย่างข้างต้นในลักษณะทีละขั้นตอนเพื่อความเข้าใจที่ดีขึ้น
ในบทนี้ให้เราเรียนรู้เกี่ยวกับเทคนิคการตรวจสอบความถูกต้องที่สามารถใช้ใน ADO.NET Entity Framework เพื่อตรวจสอบความถูกต้องของข้อมูลแบบจำลอง Entity Framework มีคุณลักษณะการตรวจสอบความถูกต้องที่หลากหลายซึ่งสามารถนำไปใช้กับส่วนติดต่อผู้ใช้สำหรับการตรวจสอบความถูกต้องฝั่งไคลเอ็นต์หรือสามารถใช้สำหรับการตรวจสอบความถูกต้องทางฝั่งเซิร์ฟเวอร์
ใน Entity Framework การตรวจสอบข้อมูลเป็นส่วนหนึ่งของโซลูชันสำหรับการดักจับข้อมูลที่ไม่ดีในแอปพลิเคชัน
Entity Framework ตรวจสอบความถูกต้องของข้อมูลทั้งหมดก่อนที่จะเขียนลงในฐานข้อมูลโดยค่าเริ่มต้นโดยใช้วิธีการตรวจสอบข้อมูลที่หลากหลาย
อย่างไรก็ตาม Entity Framework มาหลังจากการตรวจสอบข้อมูลอินเทอร์เฟซผู้ใช้ ดังนั้นในกรณีนี้จำเป็นต้องมีการตรวจสอบเอนทิตีเพื่อจัดการกับข้อยกเว้นใด ๆ ที่ EF พ่นและแสดงข้อความทั่วไป
มีเทคนิคบางอย่างในการตรวจสอบข้อมูลเพื่อปรับปรุงการตรวจสอบข้อผิดพลาดและวิธีส่งข้อความแสดงข้อผิดพลาดกลับไปยังผู้ใช้
DbContext มีเมธอด Overridable ที่เรียกว่า ValidateEntity เมื่อคุณเรียกใช้ SaveChanges Entity Framework จะเรียกเมธอดนี้สำหรับแต่ละเอนทิตีในแคชที่มีสถานะไม่เปลี่ยนแปลง คุณสามารถใส่ตรรกะการตรวจสอบได้โดยตรงที่นี่ดังที่แสดงในตัวอย่างต่อไปนี้สำหรับเอนทิตีนักเรียน
public partial class UniContextEntities : DbContext {
protected override System.Data.Entity.Validation
.DbEntityValidationResult ValidateEntity(DbEntityEntry entityEntry,
System.Collections.Generic.IDictionary<object, object> items) {
if (entityEntry.Entity is Student) {
if (entityEntry.CurrentValues.GetValue<string>("FirstMidName") == "") {
var list = new List<System.Data.Entity
.Validation.DbValidationError>();
list.Add(new System.Data.Entity.Validation
.DbValidationError("FirstMidName", "FirstMidName is required"));
return new System.Data.Entity.Validation
.DbEntityValidationResult(entityEntry, list);
}
}
if (entityEntry.CurrentValues.GetValue<string>("LastName") == "") {
var list = new List<System.Data.Entity
.Validation.DbValidationError>();
list.Add(new System.Data.Entity.Validation
.DbValidationError("LastName", "LastName is required"));
return new System.Data.Entity.Validation
.DbEntityValidationResult(entityEntry, list);
}
return base.ValidateEntity(entityEntry, items);
}
}
ในวิธีการ ValidateEntity ข้างต้นคุณสมบัติ Student entity FirstMidName และ LastName จะถูกตรวจสอบหากคุณสมบัติใด ๆ เหล่านี้มีสตริงว่างจากนั้นจะส่งคืนข้อความแสดงข้อผิดพลาด
ลองมาดูตัวอย่างง่ายๆที่นักเรียนใหม่ถูกสร้างขึ้น แต่ FirstMidName ของนักเรียนเป็นสตริงว่างดังที่แสดงในโค้ดต่อไปนี้
class Program {
static void Main(string[] args) {
using (var context = new UniContextEntities()) {
Console.WriteLine("Adding new Student to the database");
Console.WriteLine();
try {
context.Students.Add(new Student() {
FirstMidName = "",
LastName = "Upston"
});
context.SaveChanges();
} catch (DbEntityValidationException dbValidationEx) {
foreach (DbEntityValidationResult entityErr in
dbValidationEx.EntityValidationErrors) {
foreach (DbValidationError error in entityErr.ValidationErrors) {
Console.WriteLine("Error: {0}",error.ErrorMessage);
}
}
}
Console.ReadKey();
}
}
}
เมื่อตัวอย่างด้านบนถูกคอมไพล์และดำเนินการคุณจะได้รับข้อความแสดงข้อผิดพลาดต่อไปนี้บนหน้าต่างคอนโซล
Adding new Student to the database
Error: FirstMidName is required
เราขอแนะนำให้คุณดำเนินการตามตัวอย่างข้างต้นในลักษณะทีละขั้นตอนเพื่อความเข้าใจที่ดีขึ้น
Entity Framework ให้ความสามารถในการติดตามการเปลี่ยนแปลงที่เกิดขึ้นกับเอนทิตีและความสัมพันธ์ของพวกเขาดังนั้นการอัปเดตที่ถูกต้องจะทำบนฐานข้อมูลเมื่อมีการเรียกวิธีการ SaveChanges ของบริบท นี่คือคุณสมบัติหลักของ Entity Framework
การติดตามการเปลี่ยนแปลงจะติดตามการเปลี่ยนแปลงในขณะที่เพิ่มเรกคอร์ดใหม่ในคอลเล็กชันเอนทิตีแก้ไขหรือลบเอนทิตีที่มีอยู่
จากนั้นการเปลี่ยนแปลงทั้งหมดจะถูกเก็บไว้โดยระดับ DbContext
การเปลี่ยนแปลงแทร็กเหล่านี้จะหายไปหากไม่ได้บันทึกไว้ก่อนที่วัตถุ DbContext จะถูกทำลาย
คลาส DbChangeTracker ให้ข้อมูลทั้งหมดเกี่ยวกับเอนทิตีปัจจุบันที่ถูกติดตามโดยบริบท
ในการติดตามเอนทิตีตามบริบทนั้นจะต้องมีคุณสมบัติคีย์หลัก
ใน Entity Framework การติดตามการเปลี่ยนแปลงจะเปิดใช้งานโดยค่าเริ่มต้น คุณยังสามารถปิดใช้งานการติดตามการเปลี่ยนแปลงได้โดยตั้งค่าคุณสมบัติ AutoDetectChangesEnabled ของ DbContext เป็น false ถ้าคุณสมบัตินี้ถูกตั้งค่าเป็น true ดังนั้น Entity Framework จะรักษาสถานะของเอนทิตี
using (var context = new UniContextEntities()) {
context.Configuration.AutoDetectChangesEnabled = true;
}
ลองมาดูตัวอย่างต่อไปนี้ซึ่งนักเรียนและการลงทะเบียนของพวกเขาถูกดึงมาจากฐานข้อมูล
class Program {
static void Main(string[] args) {
using (var context = new UniContextEntities()) {
context.Configuration.AutoDetectChangesEnabled = true;
Console.WriteLine("Retrieve Student");
var student = (from s in context.Students where s.FirstMidName ==
"Ali" select s).FirstOrDefault<Student>();
string name = student.FirstMidName + " " + student.LastName;
Console.WriteLine("ID: {0}, Name: {1}", student.ID, name);
Console.WriteLine();
Console.WriteLine("Retrieve all related enrollments");
foreach (var enrollment in student.Enrollments) {
Console.WriteLine("Enrollment ID: {0}, Course ID: {1}",
enrollment.EnrollmentID, enrollment.CourseID);
}
Console.WriteLine();
Console.WriteLine("Context tracking changes of {0} entity.",
context.ChangeTracker.Entries().Count());
var entries = context.ChangeTracker.Entries();
foreach (var entry in entries) {
Console.WriteLine("Entity Name: {0}", entry.Entity.GetType().Name);
Console.WriteLine("Status: {0}", entry.State);
}
Console.ReadKey();
}
}
}
เมื่อรวบรวมและดำเนินการตัวอย่างข้างต้นคุณจะได้รับผลลัพธ์ต่อไปนี้
Retrieve Student
ID: 1, Name: Ali Alexander
Retrieve all related enrollments
Enrollment ID: 1, Course ID: 1050
Enrollment ID: 2, Course ID: 4022
Enrollment ID: 3, Course ID: 4041
Context tracking changes of 4 entity.
Entity Name: Student
Status: Unchanged
Entity Name: Enrollment
Status: Unchanged
Entity Name: Enrollment
Status: Unchanged
Entity Name: Enrollment
Status: Unchanged
คุณจะเห็นว่าข้อมูลทั้งหมดถูกดึงมาจากฐานข้อมูลเท่านั้นนั่นคือสาเหตุที่สถานะไม่เปลี่ยนแปลงสำหรับเอนทิตีทั้งหมด
ตอนนี้เรามาดูตัวอย่างง่ายๆอีกตัวอย่างหนึ่งซึ่งเราจะเพิ่มการลงทะเบียนอีกหนึ่งรายการและลบนักเรียนหนึ่งคนออกจากฐานข้อมูล ต่อไปนี้เป็นรหัสที่เพิ่มการลงทะเบียนใหม่และนักเรียนหนึ่งคนถูกลบ
class Program {
static void Main(string[] args) {
using (var context = new UniContextEntities()) {
context.Configuration.AutoDetectChangesEnabled = true;
Enrollment enr = new Enrollment() {
StudentID = 1, CourseID = 3141
};
Console.WriteLine("Adding New Enrollment");
context.Enrollments.Add(enr);
Console.WriteLine("Delete Student");
var student = (from s in context.Students where s.ID ==
23 select s).SingleOrDefault<Student>();
context.Students.Remove(student);
Console.WriteLine("");
Console.WriteLine("Context tracking changes of {0} entity.",
context.ChangeTracker.Entries().Count());
var entries = context.ChangeTracker.Entries();
foreach (var entry in entries) {
Console.WriteLine("Entity Name: {0}", entry.Entity.GetType().Name);
Console.WriteLine("Status: {0}", entry.State);
}
Console.ReadKey();
}
}
}
เมื่อรวบรวมและดำเนินการตัวอย่างข้างต้นคุณจะได้รับผลลัพธ์ต่อไปนี้
Adding New Enrollment
Delete Student
Context tracking changes of 2 entity.
Entity Name: Enrollment
Status: Added
Entity Name: Student
Status: Deleted
ตอนนี้คุณจะเห็นได้ว่าสถานะของเอนทิตีการลงทะเบียนถูกตั้งค่าเป็นเพิ่มและสถานะของเอนทิตีนักเรียนจะถูกลบเนื่องจากมีการเพิ่มการลงทะเบียนใหม่และนักเรียนหนึ่งคนถูกลบออกจากฐานข้อมูล
เราขอแนะนำให้คุณดำเนินการตามตัวอย่างข้างต้นในลักษณะทีละขั้นตอนเพื่อความเข้าใจที่ดีขึ้น
ในเอนทิตีกรอบงานเอนทิตีสีส่วนใหญ่เกี่ยวกับการเปลี่ยนสีของเอนทิตีในตัวออกแบบเพื่อให้นักพัฒนาสามารถระบุกลุ่มของเอนทิตีที่เกี่ยวข้องในตัวออกแบบ Visual Studio ได้ง่าย ฟีเจอร์นี้เปิดตัวครั้งแรกใน Entity Framework 5.0
คุณลักษณะนี้ไม่มีส่วนเกี่ยวข้องกับด้านประสิทธิภาพ
เมื่อคุณมีโปรเจ็กต์ขนาดใหญ่และเอนทิตีจำนวนมากในไฟล์ edmx เดียวฟีเจอร์นี้จะมีประโยชน์มากในการแยกเอนทิตีของคุณในโมดูลต่างๆ
หากคุณกำลังทำงานกับไฟล์ edmx และคุณได้เปิดในตัวออกแบบหากต้องการเปลี่ยนสีให้เลือกเอนทิตีบนหน้าต่างออกแบบ จากนั้นคลิกขวาและเลือก Properties
ในหน้าต่างคุณสมบัติเลือกคุณสมบัติเติมสี
ระบุสีโดยใช้ชื่อสีที่ถูกต้องเช่นสีเขียวหรือ RGB ที่ถูกต้อง (255, 128, 128) หรือคุณยังสามารถเลือกจากตัวเลือกสี
หากต้องการเปลี่ยนสีของหลายเอนทิตีในครั้งเดียวให้เลือกหลายเอนทิตีและเปลี่ยนสีเติมสำหรับเอนทิตีทั้งหมดโดยใช้หน้าต่างคุณสมบัติ
คุณยังสามารถเปลี่ยนรูปแบบของคุณสมบัติโดยเลือกตัวเลือกใด ๆ ต่อไปนี้ -
- ชื่อที่แสดง
- ชื่อและประเภทที่ปรากฏ
โดยค่าเริ่มต้นตัวเลือกชื่อที่แสดงจะถูกเลือก หากต้องการเปลี่ยนรูปแบบคุณสมบัติให้คลิกขวาที่หน้าต่างตัวออกแบบ
เลือกรูปแบบคุณสมบัติ Scalar →ชื่อที่แสดงและประเภท
ตอนนี้คุณจะเห็นว่าประเภทนั้นแสดงพร้อมกับชื่อด้วย
Entity Framework มีสามแนวทางในการสร้างแบบจำลองเอนทิตีและแต่ละวิธีมีข้อดีและข้อเสียของตนเอง
- รหัสแรก
- ฐานข้อมูลก่อน
- รุ่นแรก
ในบทนี้เราจะอธิบายสั้น ๆ เกี่ยวกับแนวทางแรกของรหัส นักพัฒนาบางคนชอบที่จะทำงานร่วมกับ Designer ใน Code ในขณะที่บางคนต้องการทำงานกับ Code ของตนเท่านั้น สำหรับนักพัฒนาเหล่านั้น Entity Framework มีเวิร์กโฟลว์การสร้างแบบจำลองที่เรียกว่า Code First
เวิร์กโฟลว์การสร้างโมเดล Code First กำหนดเป้าหมายฐานข้อมูลที่ไม่มีอยู่และ Code First จะสร้างขึ้น
นอกจากนี้ยังสามารถใช้ได้หากคุณมีฐานข้อมูลว่างจากนั้น Code First จะเพิ่มตารางใหม่เข้าไป
Code First ช่วยให้คุณกำหนดโมเดลของคุณโดยใช้คลาส C # หรือ VB.Net
การกำหนดค่าเพิ่มเติมสามารถเลือกดำเนินการได้โดยใช้แอตทริบิวต์ในคลาสและคุณสมบัติของคุณหรือใช้ API ที่คล่องแคล่ว
ทำไมต้องเป็นรหัสแรก
Code First สร้างขึ้นจากชุดตัวต่อ อันดับแรกคือคลาสโดเมนของคุณ
คลาสโดเมนไม่มีส่วนเกี่ยวข้องกับ Entity Framework เป็นเพียงรายการในโดเมนธุรกิจของคุณ
ดังนั้น Entity Framework จะมีบริบทที่จัดการปฏิสัมพันธ์ระหว่างคลาสเหล่านั้นกับฐานข้อมูลของคุณ
บริบทไม่เฉพาะเจาะจงสำหรับ Code First เป็นคุณสมบัติของ Entity Framework
Code First เพิ่มตัวสร้างโมเดลที่ตรวจสอบคลาสของคุณว่าบริบทกำลังจัดการจากนั้นใช้ชุดของกฎหรืออนุสัญญาเพื่อกำหนดว่าคลาสเหล่านั้นและความสัมพันธ์อธิบายโมเดลอย่างไรและโมเดลนั้นควรแมปกับฐานข้อมูลของคุณอย่างไร
ทั้งหมดนี้เกิดขึ้นที่รันไทม์ คุณจะไม่เห็นโมเดลนี้ แต่อยู่ในความทรงจำ
Code First ยังมีความสามารถในการใช้โมเดลนั้นเพื่อสร้างฐานข้อมูลหากคุณต้องการ
นอกจากนี้ยังสามารถอัปเดตฐานข้อมูลหากโมเดลเปลี่ยนแปลงโดยใช้คุณลักษณะที่เรียกว่า Code First Migrations
การตั้งค่าสภาพแวดล้อม
ในการเริ่มต้นทำงานกับแนวทาง EF Code First คุณต้องติดตั้งเครื่องมือต่อไปนี้ในระบบของคุณ
- Visual Studio 2013 (.net framework 4.5.2) หรือเวอร์ชันที่ใหม่กว่า
- MS SQL Server 2012 หรือใหม่กว่า
- Entity Framework ผ่าน NuGet Package
ติดตั้ง EF ผ่าน NuGet Package
Step 1 - ขั้นแรกให้สร้างแอปพลิเคชันคอนโซลจากไฟล์→ใหม่→โครงการ ...
Step 2 - เลือก Windows จากบานหน้าต่างด้านซ้ายและแอปพลิเคชันคอนโซลจากบานหน้าต่างเทมเพลต
Step 3 - ป้อน EFCodeFirstDemo เป็นชื่อและเลือกตกลง
Step 4 - คลิกขวาที่โครงการของคุณในตัวสำรวจโซลูชันและเลือกจัดการ NuGet Packages ...
เพื่อเปิด NuGet Package Manager และค้นหา EntityFramework เพื่อค้นหาแพ็คเกจทั้งหมดที่เกี่ยวข้องกับ Entity Framework
Step 5- เลือก EntityFramework และคลิกที่ติดตั้ง หรือจากเมนู Tools คลิก NuGet Package Manager จากนั้นคลิก Package Manager Console ในหน้าต่าง Package Manager Console ให้ป้อนคำสั่งต่อไปนี้: Install-Package EntityFramework
เมื่อการติดตั้งเสร็จสมบูรณ์คุณจะเห็นข้อความต่อไปนี้ในหน้าต่างผลลัพธ์“ ติดตั้งเรียบร้อยแล้ว 'EntityFramework 6.1.2' ไปยัง EFCodeFirstDemo”
หลังการติดตั้ง EntityFramework.dll จะรวมอยู่ในโครงการของคุณดังที่แสดงในภาพต่อไปนี้
ตอนนี้คุณพร้อมที่จะเริ่มทำงานกับแนวทาง Code First แล้ว
มากำหนดโมเดลง่ายๆโดยใช้คลาส เรากำลังกำหนดสิ่งเหล่านี้ในไฟล์ Program.cs แต่ในแอปพลิเคชันในโลกแห่งความเป็นจริงคุณจะแบ่งชั้นเรียนของคุณออกเป็นไฟล์แยกกันและอาจเป็นโครงการแยกต่างหาก ต่อไปนี้เป็นแบบจำลองข้อมูลที่เราจะสร้างโดยใช้แนวทาง Code First
สร้างแบบจำลอง
เพิ่มสามคลาสต่อไปนี้ในไฟล์ Program.cs โดยใช้รหัสต่อไปนี้สำหรับคลาสนักเรียน
public class Student {
public int ID { get; set; }
public string LastName { get; set; }
public string FirstMidName { get; set; }
public DateTime EnrollmentDate { get; set; }
public virtual ICollection<Enrollment> Enrollments { get; set; }
}
คุณสมบัติ ID จะกลายเป็นคอลัมน์คีย์หลักของตารางฐานข้อมูลที่สอดคล้องกับคลาสนี้
คุณสมบัติการลงทะเบียนเป็นคุณสมบัติการนำทาง คุณสมบัติการนำทางถือเอนทิตีอื่น ๆ ที่เกี่ยวข้องกับเอนทิตีนี้
ในกรณีนี้คุณสมบัติการลงทะเบียนของเอนทิตีนักศึกษาจะเก็บเอนทิตีการลงทะเบียนทั้งหมดที่เกี่ยวข้องกับเอนทิตีนักศึกษานั้น
โดยทั่วไปคุณสมบัติการนำทางถูกกำหนดให้เป็นเสมือนเพื่อให้สามารถใช้ประโยชน์จากฟังก์ชันการทำงานของ Entity Framework บางอย่างเช่นการโหลดแบบขี้เกียจ
หากคุณสมบัติการนำทางสามารถเก็บเอนทิตีได้หลายรายการ (เช่นเดียวกับความสัมพันธ์แบบกลุ่มต่อกลุ่มหรือแบบกลุ่มเดียว) ประเภทจะต้องเป็นรายการที่สามารถเพิ่มลบและอัปเดตรายการได้เช่น ICollection
ต่อไปนี้คือการนำไปใช้สำหรับชั้นเรียนหลักสูตร
public class Course {
public int CourseID { get; set; }
public string Title { get; set; }
public int Credits { get; set; }
public virtual ICollection<Enrollment> Enrollments { get; set; }
}
คุณสมบัติการลงทะเบียนเป็นคุณสมบัติการนำทาง เอนทิตีหลักสูตรสามารถเกี่ยวข้องกับเอนทิตีการลงทะเบียนจำนวนเท่าใดก็ได้
ต่อไปนี้คือการใช้งานสำหรับคลาสการลงทะเบียนและ enum
public enum Grade {
A, B, C, D, F
}
public class Enrollment {
public int EnrollmentID { get; set; }
public int CourseID { get; set; }
public int StudentID { get; set; }
public Grade? Grade { get; set; }
public virtual Course Course { get; set; }
public virtual Student Student { get; set; }
}
คุณสมบัติ EnrollmentID จะเป็นคีย์หลัก
คุณสมบัติเกรดคือ enum เครื่องหมายคำถามหลังการประกาศประเภทเกรดบ่งชี้ว่าคุณสมบัติเกรดเป็นโมฆะ
เกรดที่เป็นโมฆะจะแตกต่างจากเกรดศูนย์ Null หมายถึงเกรดที่ยังไม่ทราบหรือยังไม่ได้รับมอบหมาย
คุณสมบัติ StudentID และ CourseID เป็นคีย์ต่างประเทศและคุณสมบัติการนำทางที่เกี่ยวข้องคือ Student และ Course
เอนทิตีการลงทะเบียนเชื่อมโยงกับนักเรียนหนึ่งคนและเอนทิตีหลักสูตรหนึ่งหน่วยงานดังนั้นคุณสมบัติจึงสามารถเก็บเอนทิตีนักเรียนและหลักสูตรเดียวเท่านั้น
สร้างบริบทฐานข้อมูล
คลาสหลักที่ประสานการทำงานของ Entity Framework สำหรับโมเดลข้อมูลที่กำหนดคือคลาสบริบทฐานข้อมูลซึ่งอนุญาตให้สอบถามและบันทึกข้อมูล คุณสามารถสร้างคลาสนี้โดยได้มาจากคลาส DbContext และเปิดเผย DbSet ที่พิมพ์
public class MyContext : DbContext {
public virtual DbSet<Course> Courses { get; set; }
public virtual DbSet<Enrollment> Enrollments { get; set; }
public virtual DbSet<Student> Students { get; set; }
}
ต่อไปนี้เป็นรหัสที่สมบูรณ์ในไฟล์ Program.cs
using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace EFCodeFirstDemo {
class Program {
static void Main(string[] args) {}
}
public enum Grade {
A, B, C, D, F
}
public class Enrollment {
public int EnrollmentID { get; set; }
public int CourseID { get; set; }
public int StudentID { get; set; }
public Grade? Grade { get; set; }
public virtual Course Course { get; set; }
public virtual Student Student { get; set; }
}
public class Student {
public int ID { get; set; }
public string LastName { get; set; }
public string FirstMidName { get; set; }
public DateTime EnrollmentDate { get; set; }
public virtual ICollection<Enrollment> Enrollments { get; set; }
}
public class Course {
public int CourseID { get; set; }
public string Title { get; set; }
public int Credits { get; set; }
public virtual ICollection<Enrollment> Enrollments { get; set; }
}
public class MyContext : DbContext {
public virtual DbSet<Course> Courses { get; set; }
public virtual DbSet<Enrollment> Enrollments { get; set; }
public virtual DbSet<Student> Students { get; set; }
}
}
โค้ดด้านบนคือทั้งหมดที่เราต้องใช้ในการเริ่มจัดเก็บและดึงข้อมูล มาเพิ่มข้อมูลแล้วดึงข้อมูล ต่อไปนี้เป็นรหัสในวิธีการหลัก
static void Main(string[] args) {
using (var context = new MyContext()) {
// Create and save a new Students
Console.WriteLine("Adding new students");
var student = new Student {
FirstMidName = "Alain", LastName = "Bomer",
EnrollmentDate = DateTime.Parse(DateTime.Today.ToString())
};
context.Students.Add(student);
var student1 = new Student {
FirstMidName = "Mark", LastName = "Upston",
EnrollmentDate = DateTime.Parse(DateTime.Today.ToString())
};
context.Students.Add(student1);
context.SaveChanges();
// Display all Students from the database
var students = (from s in context.Students
orderby s.FirstMidName select s).ToList<Student>();
Console.WriteLine("Retrieve all Students from the database:");
foreach (var stdnt in students) {
string name = stdnt.FirstMidName + " " + stdnt.LastName;
Console.WriteLine("ID: {0}, Name: {1}", stdnt.ID, name);
}
Console.WriteLine("Press any key to exit...");
Console.ReadKey();
}
}
เมื่อดำเนินการโค้ดด้านบนคุณจะได้รับผลลัพธ์ต่อไปนี้
Adding new students
Retrieve all Students from the database:
ID: 1, Name: Alain Bomer
ID: 2, Name: Mark Upston
Press any key to exit...
คำถามที่อยู่ในใจคือข้อมูลและฐานข้อมูลอยู่ที่ไหนซึ่งเราได้เพิ่มข้อมูลบางส่วนแล้วดึงมาจากฐานข้อมูล ตามแบบแผน DbContext ได้สร้างฐานข้อมูลสำหรับคุณ
หากอินสแตนซ์ SQL Express ในเครื่องพร้อมใช้งาน Code First ได้สร้างฐานข้อมูลบนอินสแตนซ์นั้น
หาก SQL Express ไม่พร้อมใช้งาน Code First จะลองใช้ LocalDb
ฐานข้อมูลถูกตั้งชื่อตามชื่อแบบเต็มของบริบทที่ได้รับ
ในกรณีของเราอินสแตนซ์ SQL Express พร้อมใช้งานและชื่อฐานข้อมูลคือ EFCodeFirstDemo.MyContext ดังแสดงในภาพต่อไปนี้
นี่เป็นเพียงข้อตกลงเริ่มต้นและมีหลายวิธีในการเปลี่ยนฐานข้อมูลที่ Code First ใช้
ดังที่คุณเห็นในภาพด้านบนมันได้สร้างตารางนักเรียนหลักสูตรและการลงทะเบียนและแต่ละตารางประกอบด้วยคอลัมน์ที่มีประเภทข้อมูลและความยาวที่เหมาะสม
ชื่อคอลัมน์และประเภทข้อมูลยังตรงกับคุณสมบัติของคลาสโดเมนที่เกี่ยวข้อง
การเริ่มต้นฐานข้อมูล
ในตัวอย่างข้างต้นเราได้เห็นว่า Code First สร้างฐานข้อมูลโดยอัตโนมัติ แต่ถ้าคุณต้องการเปลี่ยนชื่อฐานข้อมูลและเซิร์ฟเวอร์ให้เราดูว่า Code First ตัดสินใจชื่อฐานข้อมูลและเซิร์ฟเวอร์อย่างไรในขณะที่เริ่มต้นฐานข้อมูล ดูแผนภาพต่อไปนี้
คุณสามารถกำหนดตัวสร้างพื้นฐานของคลาสบริบทได้ด้วยวิธีต่อไปนี้
- ไม่มีพารามิเตอร์
- ชื่อฐานข้อมูล
- ชื่อสตริงการเชื่อมต่อ
ไม่มีพารามิเตอร์
หากคุณระบุคอนสตรัคเตอร์พื้นฐานของคลาสบริบทโดยไม่มีพารามิเตอร์ใด ๆ ดังที่แสดงในตัวอย่างด้านบนเอนทิตีเฟรมเวิร์กจะสร้างฐานข้อมูลในเซิร์ฟเวอร์ SQLEXPRESS ในเครื่องของคุณด้วยชื่อ {Namespace} {ชื่อคลาสบริบท}
ในตัวอย่างข้างต้นฐานข้อมูลที่สร้างขึ้นโดยอัตโนมัติมีชื่อ EFCodeFirstDemo.MyContext หากคุณดูชื่อคุณจะพบว่า EFCodeFirstDemo คือเนมสเปซและ MyContext คือชื่อคลาสบริบทดังที่แสดงในโค้ดต่อไปนี้
public class MyContext : DbContext {
public MyContext() : base() {}
public virtual DbSet<Course> Courses { get; set; }
public virtual DbSet<Enrollment> Enrollments { get; set; }
public virtual DbSet<Student> Students { get; set; }
}
ชื่อฐานข้อมูล
ถ้าคุณส่งชื่อฐานข้อมูลเป็นพารามิเตอร์ในคอนสตรัคเตอร์พื้นฐานของคลาสบริบท Code First จะสร้างฐานข้อมูลโดยอัตโนมัติอีกครั้ง แต่คราวนี้ชื่อจะถูกส่งเป็นพารามิเตอร์ในตัวสร้างฐานบนเซิร์ฟเวอร์ฐานข้อมูล SQLEXPRESS ในเครื่อง .
ในรหัสต่อไปนี้ MyContextDB ถูกระบุเป็นพารามิเตอร์ในตัวสร้างฐาน หากเรียกใช้แอปพลิเคชันของคุณฐานข้อมูลที่มีชื่อ MyContextDB จะถูกสร้างขึ้นในเซิร์ฟเวอร์ SQL ภายในของคุณ
public class MyContext : DbContext {
public MyContext() : base("MyContextDB") {}
public virtual DbSet<Course> Courses { get; set; }
public virtual DbSet<Enrollment> Enrollments { get; set; }
public virtual DbSet<Student> Students { get; set; }
}
ชื่อสตริงการเชื่อมต่อ
นี่เป็นวิธีง่ายๆในการบอกให้ DbContext ใช้เซิร์ฟเวอร์ฐานข้อมูลอื่นที่ไม่ใช่ SQL Express หรือ LocalDb คุณสามารถเลือกที่จะใส่สตริงการเชื่อมต่อในไฟล์ app.config ของคุณ
หากชื่อของสตริงการเชื่อมต่อตรงกับชื่อบริบทของคุณ (ไม่ว่าจะมีหรือไม่มีคุณสมบัติเนมสเปซก็ตาม) DbContext จะพบเมื่อพารามิเตอร์น้อยกว่าตัวสร้างถูกใช้
หากชื่อสตริงการเชื่อมต่อแตกต่างจากชื่อบริบทของคุณคุณสามารถบอกให้ DbContext ใช้การเชื่อมต่อนี้ในโหมด Code First โดยส่งชื่อสตริงการเชื่อมต่อไปยังตัวสร้าง DbContext
public class MyContext : DbContext {
public MyContext() : base("name = MyContextDB") {}
public virtual DbSet<Course> Courses { get; set; }
public virtual DbSet<Enrollment> Enrollments { get; set; }
public virtual DbSet<Student> Students { get; set; }
}
ในโค้ดด้านบนข้อมูลโค้ดของสตริงการเชื่อมต่อคลาสบริบทถูกระบุเป็นพารามิเตอร์ในตัวสร้างพื้นฐาน
ชื่อสตริงการเชื่อมต่อต้องขึ้นต้นด้วย "name =" มิฉะนั้นจะถือว่าเป็นชื่อฐานข้อมูล
แบบฟอร์มนี้ทำให้ชัดเจนว่าคุณคาดว่าจะพบสตริงการเชื่อมต่อในไฟล์กำหนดค่าของคุณ ข้อยกเว้นจะเกิดขึ้นหากไม่พบสตริงการเชื่อมต่อที่มีชื่อที่กำหนด
<connectionStrings>
<add name = "MyContextDB"
connectionString = "Data Source =.;Initial Catalog = EFMyContextDB;Integrated Security = true"
providerName = "System.Data.SqlClient"/>
</connectionStrings>
ชื่อฐานข้อมูลในสตริงการเชื่อมต่อใน app.config คือ EFMyContextDB. CodeFirst จะสร้างไฟล์EFMyContextDB ฐานข้อมูลหรือใช้ที่มีอยู่ EFMyContextDB ฐานข้อมูลที่ SQL Server ในเครื่อง
คลาสโดเมน
จนถึงตอนนี้เราเพิ่งปล่อยให้ EF ค้นพบโมเดลโดยใช้รูปแบบเริ่มต้น แต่จะมีบางครั้งที่ชั้นเรียนของเราไม่เป็นไปตามอนุสัญญาและเราจำเป็นต้องสามารถกำหนดค่าเพิ่มเติมได้ แต่คุณสามารถลบล้างข้อตกลงเหล่านี้ได้โดยกำหนดค่าคลาสโดเมนของคุณเพื่อให้ EF มีข้อมูลที่จำเป็น มีสองตัวเลือกในการกำหนดค่าคลาสโดเมนของคุณ -
- คำอธิบายประกอบข้อมูล
- API ที่คล่องแคล่ว
คำอธิบายประกอบข้อมูล
DataAnnotations ใช้เพื่อกำหนดค่าคลาสของคุณซึ่งจะเน้นการกำหนดค่าที่จำเป็นโดยทั่วไป นอกจากนี้ DataAnnotations ยังเข้าใจโดยแอปพลิเคชัน. NET จำนวนมากเช่น ASP.NET MVC ซึ่งอนุญาตให้แอปพลิเคชันเหล่านี้ใช้ประโยชน์จากคำอธิบายประกอบเดียวกันสำหรับการตรวจสอบความถูกต้องฝั่งไคลเอ็นต์
ต่อไปนี้เป็นคำอธิบายประกอบข้อมูลที่ใช้ในชั้นเรียนของนักเรียน
public class Enrollment {
[Key]
public int EnrollmentID { get; set; }
public int CourseID { get; set; }
public int StudentID { get; set; }
public Grade? Grade { get; set; }
[ForeignKey("CourseID")]
public virtual Course Course { get; set; }
[ForeignKey("ID")]
public virtual Student Student { get; set; }
}
API ที่คล่องแคล่ว
การกำหนดค่าโมเดลส่วนใหญ่สามารถทำได้โดยใช้คำอธิบายประกอบข้อมูลอย่างง่าย Fluent API เป็นวิธีขั้นสูงในการระบุการกำหนดค่าโมเดลที่ครอบคลุมทุกสิ่งที่คำอธิบายประกอบข้อมูลทำได้นอกเหนือจากการกำหนดค่าขั้นสูงบางอย่างที่ไม่สามารถทำได้ด้วยคำอธิบายประกอบข้อมูล คำอธิบายประกอบข้อมูลและ API ที่คล่องแคล่วสามารถใช้ร่วมกันได้
ในการเข้าถึง API ที่คล่องแคล่วคุณจะแทนที่เมธอด OnModelCreating ใน DbContext ตอนนี้ขอเปลี่ยนชื่อคอลัมน์ในตารางนักเรียนจาก FirstMidName เป็น FirstName ตามที่แสดงในโค้ดต่อไปนี้
public class MyContext : DbContext {
protected override void OnModelCreating(DbModelBuilder modelBuilder) {
modelBuilder.Entity<Student>().Property(s ⇒ s.FirstMidName)
.HasColumnName("FirstName");
}
public virtual DbSet<Course> Courses { get; set; }
public virtual DbSet<Enrollment> Enrollments { get; set; }
public virtual DbSet<Student> Students { get; set; }
}
DataAnnotations ใช้เพื่อกำหนดค่าคลาสซึ่งจะเน้นการกำหนดค่าที่จำเป็นโดยทั่วไป นอกจากนี้ DataAnnotations ยังเข้าใจโดยแอปพลิเคชัน. NET จำนวนมากเช่น ASP.NET MVC ซึ่งช่วยให้แอปพลิเคชันเหล่านี้ใช้ประโยชน์จากคำอธิบายประกอบเดียวกันสำหรับการตรวจสอบความถูกต้องฝั่งไคลเอ็นต์ แอตทริบิวต์ DataAnnotation แทนที่อนุสัญญา CodeFirst เริ่มต้น
System.ComponentModel.DataAnnotations มีแอตทริบิวต์ต่อไปนี้ที่ส่งผลต่อความเป็นโมฆะหรือขนาดของคอลัมน์
- Key
- Timestamp
- ConcurrencyCheck
- Required
- MinLength
- MaxLength
- StringLength
System.ComponentModel.DataAnnotations.Schema เนมสเปซประกอบด้วยแอ็ตทริบิวต์ต่อไปนี้ที่มีผลต่อสคีมาของฐานข้อมูล
- Table
- Column
- Index
- ForeignKey
- NotMapped
- InverseProperty
สำคัญ
Entity Framework อาศัยทุกเอนทิตีที่มีค่าคีย์ที่ใช้สำหรับการติดตามเอนทิตี หนึ่งในอนุสัญญาที่ Code First ขึ้นอยู่กับว่ามันบอกเป็นนัยว่าคุณสมบัติใดเป็นกุญแจสำคัญในแต่ละคลาสของ Code First
Convention คือการมองหาคุณสมบัติชื่อ“ Id” หรือคุณสมบัติที่รวมชื่อคลาสและ“ Id” เช่น“ StudentId”
คุณสมบัติจะแมปกับคอลัมน์คีย์หลักในฐานข้อมูล
ชั้นเรียนนักศึกษาหลักสูตรและการลงทะเบียนเป็นไปตามอนุสัญญานี้
ตอนนี้สมมติว่าคลาสนักเรียนใช้ชื่อ StdntID แทน ID เมื่อ Code First ไม่พบคุณสมบัติที่ตรงกับข้อตกลงนี้จะทำให้เกิดข้อยกเว้นเนื่องจากข้อกำหนดของ Entity Framework ที่คุณต้องมีคุณสมบัติหลัก คุณสามารถใช้คำอธิบายประกอบคีย์เพื่อระบุคุณสมบัติที่จะใช้เป็น EntityKey
ลองดูรหัสต่อไปนี้ของชั้นเรียนนักเรียนซึ่งมี StdntID แต่ไม่เป็นไปตามหลักการเริ่มต้นของ Code First ดังนั้นเพื่อจัดการกับสิ่งนี้จึงมีการเพิ่มแอตทริบิวต์ของคีย์ซึ่งจะทำให้เป็นคีย์หลัก
public class Student {
[Key]
public int StdntID { get; set; }
public string LastName { get; set; }
public string FirstMidName { get; set; }
public DateTime EnrollmentDate { get; set; }
public virtual ICollection<Enrollment> Enrollments { get; set; }
}
เมื่อคุณเรียกใช้แอปพลิเคชันของคุณและตรวจสอบฐานข้อมูลของคุณใน SQL Server Explorer คุณจะเห็นว่าขณะนี้คีย์หลักคือ StdntID ในตารางนักเรียน
Entity Framework ยังรองรับคีย์คอมโพสิต Composite keysยังเป็นคีย์หลักที่ประกอบด้วยคุณสมบัติมากกว่าหนึ่งรายการ ตัวอย่างเช่นคุณมีคลาส DrivingLicense ซึ่งมีคีย์หลักเป็นชุดของ LicenseNumber และ IssuingCountry
public class DrivingLicense {
[Key, Column(Order = 1)]
public int LicenseNumber { get; set; }
[Key, Column(Order = 2)]
public string IssuingCountry { get; set; }
public DateTime Issued { get; set; }
public DateTime Expires { get; set; }
}
เมื่อคุณมีคีย์ผสม Entity Framework ต้องการให้คุณกำหนดลำดับคุณสมบัติของคีย์ คุณสามารถทำได้โดยใช้คำอธิบายประกอบคอลัมน์เพื่อระบุคำสั่งซื้อ
การประทับเวลา
Code First จะปฏิบัติต่อคุณสมบัติ Timestamp เช่นเดียวกับคุณสมบัติ ConcurrencyCheck แต่ยังช่วยให้แน่ใจว่าฟิลด์ฐานข้อมูลที่โค้ดสร้างขึ้นเป็นครั้งแรกไม่เป็นค่าว่าง
เป็นเรื่องปกติมากขึ้นที่จะใช้เขตข้อมูล rowversion หรือ timestamp สำหรับการตรวจสอบการทำงานพร้อมกัน
แทนที่จะใช้คำอธิบายประกอบ ConcurrencyCheck คุณสามารถใช้คำอธิบายประกอบ TimeStamp ที่เฉพาะเจาะจงมากขึ้นได้ตราบเท่าที่ชนิดของคุณสมบัติเป็นไบต์อาร์เรย์
คุณสามารถมีคุณสมบัติการประทับเวลาได้เพียงหนึ่งรายการในคลาสที่กำหนด
มาดูตัวอย่างง่ายๆโดยการเพิ่มคุณสมบัติ TimeStamp ในคลาส Course -
public class Course {
public int CourseID { get; set; }
public string Title { get; set; }
public int Credits { get; set; }
[Timestamp]
public byte[] TStamp { get; set; }
public virtual ICollection<Enrollment> Enrollments { get; set; }
}
ดังที่คุณเห็นในตัวอย่างข้างต้นแอตทริบิวต์ Timestamp ถูกนำไปใช้กับคุณสมบัติ Byte [] ของคลาสหลักสูตร ดังนั้น Code First จะสร้างคอลัมน์การประทับเวลาTStamp
ในตารางหลักสูตร
การทำงานพร้อมกันตรวจสอบ
คำอธิบายประกอบ ConcurrencyCheck อนุญาตให้คุณตั้งค่าสถานะคุณสมบัติอย่างน้อยหนึ่งรายการเพื่อใช้สำหรับการตรวจสอบพร้อมกันในฐานข้อมูลเมื่อผู้ใช้แก้ไขหรือลบเอนทิตี หากคุณเคยทำงานกับ EF Designer สิ่งนี้จะสอดคล้องกับการตั้งค่า ConcurrencyMode ของคุณสมบัติเป็นคงที่
ลองมาดูตัวอย่างง่ายๆของการทำงานของ ConcurrencyCheck โดยการเพิ่มเข้าไปในคุณสมบัติ Title ในคลาส Course
public class Course {
public int CourseID { get; set; }
[ConcurrencyCheck]
public string Title { get; set; }
public int Credits { get; set; }
[Timestamp, DataType("timestamp")]
public byte[] TimeStamp { get; set; }
public virtual ICollection<Enrollment> Enrollments { get; set; }
}
ในชั้นเรียนหลักสูตรข้างต้นแอตทริบิวต์ ConcurrencyCheck ถูกนำไปใช้กับคุณสมบัติ Title ที่มีอยู่ ตอนนี้ Code First จะรวมคอลัมน์ Title ไว้ในคำสั่ง update เพื่อตรวจสอบการเกิดพร้อมกันในแง่ดีดังที่แสดงในโค้ดต่อไปนี้
exec sp_executesql N'UPDATE [dbo].[Courses]
SET [Title] = @0
WHERE (([CourseID] = @1) AND ([Title] = @2))
',N'@0 nvarchar(max) ,@1 int,@2 nvarchar(max) ',@0=N'Maths',@1=1,@2=N'Calculus'
go
คำอธิบายประกอบที่จำเป็น
คำอธิบายประกอบที่จำเป็นจะบอก EF ว่าจำเป็นต้องมีคุณสมบัติเฉพาะ มาดูคลาสนักเรียนต่อไปนี้ซึ่งไอดีที่ต้องการถูกเพิ่มเข้าไปในคุณสมบัติ FirstMidName แอตทริบิวต์ที่จำเป็นจะบังคับให้ EF ต้องแน่ใจว่าคุณสมบัตินั้นมีข้อมูลอยู่
public class Student {
[Key]
public int StdntID { get; set; }
[Required]
public string LastName { get; set; }
[Required]
public string FirstMidName { get; set; }
public DateTime EnrollmentDate { get; set; }
public virtual ICollection<Enrollment> Enrollments { get; set; }
}
ดังที่เห็นในตัวอย่างข้างต้นแอตทริบิวต์ที่จำเป็นถูกนำไปใช้กับ FirstMidName และ LastName ดังนั้น Code First จะสร้างคอลัมน์ NOT NULL FirstMidName และ LastName ในตาราง Students ดังที่แสดงในภาพต่อไปนี้
MaxLength
แอตทริบิวต์ MaxLength ช่วยให้คุณระบุการตรวจสอบคุณสมบัติเพิ่มเติม สามารถนำไปใช้กับคุณสมบัติประเภทสตริงหรืออาร์เรย์ของคลาสโดเมน EF Code First จะกำหนดขนาดของคอลัมน์ตามที่ระบุในแอตทริบิวต์ MaxLength
มาดูคลาสหลักสูตรต่อไปนี้ซึ่งใช้แอตทริบิวต์ MaxLength (24) กับคุณสมบัติ Title
public class Course {
public int CourseID { get; set; }
[ConcurrencyCheck]
[MaxLength(24)]
public string Title { get; set; }
public int Credits { get; set; }
public virtual ICollection<Enrollment> Enrollments { get; set; }
}
เมื่อคุณเรียกใช้แอปพลิเคชันข้างต้น Code First จะสร้างชื่อคอลัมน์ nvarchar (24) ในตาราง CourseId ดังที่แสดงในภาพต่อไปนี้
เมื่อผู้ใช้ตั้งค่า Title ซึ่งมีอักขระมากกว่า 24 ตัว EF จะโยน EntityValidationError
MinLength
แอตทริบิวต์ MinLength ยังช่วยให้คุณสามารถระบุการตรวจสอบคุณสมบัติเพิ่มเติมได้เช่นเดียวกับที่คุณทำกับ MaxLength นอกจากนี้ยังสามารถใช้แอตทริบิวต์ MinLength กับแอตทริบิวต์ MaxLength ดังที่แสดงในรหัสต่อไปนี้
public class Course {
public int CourseID { get; set; }
[ConcurrencyCheck]
[MaxLength(24) , MinLength(5)]
public string Title { get; set; }
public int Credits { get; set; }
public virtual ICollection<Enrollment> Enrollments { get; set; }
}
EF จะโยน EntityValidationError ถ้าคุณตั้งค่าคุณสมบัติ Title น้อยกว่าความยาวที่ระบุในแอตทริบิวต์ MinLength หรือมากกว่าความยาวที่ระบุในแอตทริบิวต์ MaxLength
StringLength
StringLength ยังช่วยให้คุณระบุการตรวจสอบคุณสมบัติเพิ่มเติมเช่น MaxLength ข้อแตกต่างเพียงอย่างเดียวคือแอตทริบิวต์ StringLength สามารถใช้ได้กับคุณสมบัติประเภทสตริงของคลาสโดเมนเท่านั้น
public class Course {
public int CourseID { get; set; }
[StringLength (24)]
public string Title { get; set; }
public int Credits { get; set; }
public virtual ICollection<Enrollment> Enrollments { get; set; }
}
Entity Framework ยังตรวจสอบค่าของคุณสมบัติสำหรับแอตทริบิวต์ StringLength หากผู้ใช้ตั้งค่า Title ซึ่งมีอักขระมากกว่า 24 ตัว EF จะโยน EntityValidationError
ตาราง
หลักการเริ่มต้นของ Code First สร้างชื่อตารางที่คล้ายกับชื่อคลาส หากคุณปล่อยให้ Code First สร้างฐานข้อมูลและต้องการเปลี่ยนชื่อของตารางที่กำลังสร้าง จากนั้น -
คุณสามารถใช้ Code First กับฐานข้อมูลที่มีอยู่ แต่ไม่ใช่ทุกกรณีที่ชื่อของคลาสจะตรงกับชื่อของตารางในฐานข้อมูลของคุณ
แอตทริบิวต์ของตารางจะแทนที่แบบแผนเริ่มต้นนี้
EF Code First จะสร้างตารางที่มีชื่อที่ระบุในแอตทริบิวต์ Table สำหรับคลาสโดเมนที่กำหนด
ลองมาดูตัวอย่างต่อไปนี้ซึ่งชั้นเรียนมีชื่อว่า Student และตามแบบแผน Code First สันนิษฐานว่าสิ่งนี้จะจับคู่กับตารางที่ชื่อนักเรียน หากไม่เป็นเช่นนั้นคุณสามารถระบุชื่อของตารางด้วยแอตทริบิวต์ Table ดังที่แสดงในรหัสต่อไปนี้
[Table("StudentsInfo")]
public class Student {
[Key]
public int StdntID { get; set; }
[Required]
public string LastName { get; set; }
[Required]
public string FirstMidName { get; set; }
public DateTime EnrollmentDate { get; set; }
public virtual ICollection<Enrollment> Enrollments { get; set; }
}
ตอนนี้คุณจะเห็นว่าแอตทริบิวต์ Table ระบุตารางเป็น StudentsInfo เมื่อสร้างตารางคุณจะเห็นชื่อตาราง StudentsInfo ดังแสดงในภาพต่อไปนี้
คุณไม่สามารถระบุชื่อตารางเท่านั้น แต่คุณสามารถระบุสคีมาสำหรับตารางโดยใช้แอตทริบิวต์ Table ดังที่แสดงในโค้ดต่อไปนี้
[Table("StudentsInfo", Schema = "Admin")]
public class Student {
[Key]
public int StdntID { get; set; }
[Required]
public string LastName { get; set; }
[Required]
public string FirstMidName { get; set; }
public DateTime EnrollmentDate { get; set; }
public virtual ICollection<Enrollment> Enrollments { get; set; }
}
คุณสามารถดูในตัวอย่างด้านบนตารางถูกระบุด้วยสคีมาของผู้ดูแลระบบ ตอนนี้ Code First จะสร้างตาราง StudentsInfo ใน Admin schema ดังที่แสดงในภาพต่อไปนี้
คอลัมน์
นอกจากนี้ยังเหมือนกับแอตทริบิวต์ Table แต่แอตทริบิวต์ Table จะแทนที่ลักษณะการทำงานของตารางในขณะที่แอตทริบิวต์ Column แทนที่ลักษณะการทำงานของคอลัมน์ หลักการเริ่มต้นของ Code First สร้างชื่อคอลัมน์ที่คล้ายกับชื่อคุณสมบัติ หากคุณปล่อยให้ Code First สร้างฐานข้อมูลและต้องการเปลี่ยนชื่อคอลัมน์ในตารางของคุณด้วย จากนั้น -
แอตทริบิวต์คอลัมน์จะแทนที่การประชุมเริ่มต้น
EF Code First จะสร้างคอลัมน์ที่มีชื่อที่ระบุในแอตทริบิวต์คอลัมน์สำหรับคุณสมบัติที่กำหนด
ลองดูตัวอย่างต่อไปนี้ซึ่งคุณสมบัตินี้มีชื่อว่า FirstMidName และตามแบบแผน Code First สันนิษฐานว่าสิ่งนี้จะแมปกับคอลัมน์ชื่อ FirstMidName
หากไม่ใช่กรณีนี้คุณสามารถระบุชื่อของคอลัมน์ด้วยแอตทริบิวต์ Column ดังที่แสดงในรหัสต่อไปนี้
public class Student {
public int ID { get; set; }
public string LastName { get; set; }
[Column("FirstName")]
public string FirstMidName { get; set; }
public DateTime EnrollmentDate { get; set; }
public virtual ICollection<Enrollment> Enrollments { get; set; }
}
คุณจะเห็นว่าคอลัมน์แอตทริบิวต์ระบุคอลัมน์เป็น FirstName เมื่อสร้างตารางขึ้นคุณจะเห็นชื่อคอลัมน์ FirstName ดังแสดงในภาพต่อไปนี้
ดัชนี
แอตทริบิวต์ Index ถูกนำมาใช้ใน Entity Framework 6.1 หากคุณใช้เวอร์ชันก่อนหน้านี้ข้อมูลในส่วนนี้จะใช้ไม่ได้
คุณสามารถสร้างดัชนีในคอลัมน์อย่างน้อยหนึ่งคอลัมน์โดยใช้ IndexAttribute
การเพิ่มแอตทริบิวต์ให้กับคุณสมบัติอย่างน้อยหนึ่งคุณสมบัติจะทำให้ EF สร้างดัชนีที่สอดคล้องกันในฐานข้อมูลเมื่อสร้างฐานข้อมูล
ดัชนีทำให้การดึงข้อมูลเร็วขึ้นและมีประสิทธิภาพในกรณีส่วนใหญ่ อย่างไรก็ตามการใส่ตารางหรือมุมมองที่มีดัชนีมากเกินไปอาจส่งผลต่อประสิทธิภาพของการดำเนินการอื่น ๆ เช่นการแทรกหรือการอัปเดต
การจัดทำดัชนีเป็นคุณลักษณะใหม่ใน Entity Framework ที่คุณสามารถปรับปรุงประสิทธิภาพของแอปพลิเคชัน Code First ของคุณได้โดยลดเวลาที่ต้องใช้ในการสืบค้นข้อมูลจากฐานข้อมูล
คุณสามารถเพิ่มดัชนีให้กับฐานข้อมูลของคุณโดยใช้แอตทริบิวต์ดัชนีและแทนที่ค่าเริ่มต้นที่ไม่ซ้ำกันและการตั้งค่าคลัสเตอร์เพื่อให้ได้ดัชนีที่เหมาะสมกับสถานการณ์ของคุณมากที่สุด
ตามค่าเริ่มต้นดัชนีจะถูกตั้งชื่อว่า IX_ <ชื่อคุณสมบัติ>
มาดูโค้ดต่อไปนี้ซึ่งมีการเพิ่มแอตทริบิวต์ดัชนีในคลาสหลักสูตรสำหรับเครดิต
public class Course {
public int CourseID { get; set; }
public string Title { get; set; }
[Index]
public int Credits { get; set; }
public virtual ICollection<Enrollment> Enrollments { get; set; }
}
คุณจะเห็นว่าแอตทริบิวต์ดัชนีถูกนำไปใช้กับคุณสมบัติเครดิต เมื่อสร้างตารางขึ้นคุณจะเห็น IX_Credits ใน Indexes
โดยค่าเริ่มต้นดัชนีจะไม่ซ้ำกัน แต่คุณสามารถใช้ไฟล์ IsUniqueตั้งชื่อพารามิเตอร์เพื่อระบุว่าดัชนีควรไม่ซ้ำกัน ตัวอย่างต่อไปนี้แนะนำดัชนีเฉพาะดังที่แสดงในโค้ดต่อไปนี้
public class Course {
public int CourseID { get; set; }
[Index(IsUnique = true)]
public string Title { get; set; }
[Index]
public int Credits { get; set; }
public virtual ICollection<Enrollment> Enrollments { get; set; }
}
ForeignKey
การประชุม Code First จะดูแลความสัมพันธ์ที่พบบ่อยที่สุดในโมเดลของคุณ แต่มีบางกรณีที่ต้องการความช่วยเหลือ ตัวอย่างเช่นการเปลี่ยนชื่อของคุณสมบัติหลักในคลาสนักเรียนทำให้เกิดปัญหากับความสัมพันธ์กับคลาสการลงทะเบียน
public class Enrollment {
public int EnrollmentID { get; set; }
public int CourseID { get; set; }
public int StudentID { get; set; }
public Grade? Grade { get; set; }
public virtual Course Course { get; set; }
public virtual Student Student { get; set; }
}
public class Student {
[Key]
public int StdntID { get; set; }
public string LastName { get; set; }
public string FirstMidName { get; set; }
public DateTime EnrollmentDate { get; set; }
public virtual ICollection<Enrollment> Enrollments { get; set; }
}
ในขณะที่สร้างฐานข้อมูล Code First จะเห็นคุณสมบัติ StudentID ในคลาสการลงทะเบียนและรับรู้โดยแบบแผนว่าตรงกับชื่อคลาสบวกกับ "ID" เป็นคีย์ต่างประเทศสำหรับคลาส Student อย่างไรก็ตามไม่มีคุณสมบัติ StudentID ในคลาส Student แต่คุณสมบัติ StdntID คือคลาส Student
วิธีแก้ปัญหานี้คือการสร้างคุณสมบัติการนำทางในการลงทะเบียนและใช้ ForeignKey DataAnnotation เพื่อช่วยให้ Code First เข้าใจวิธีสร้างความสัมพันธ์ระหว่างสองคลาสดังที่แสดงในโค้ดต่อไปนี้
public class Enrollment {
public int EnrollmentID { get; set; }
public int CourseID { get; set; }
public int StudentID { get; set; }
public Grade? Grade { get; set; }
public virtual Course Course { get; set; }
[ForeignKey("StudentID")]
public virtual Student Student { get; set; }
}
ตอนนี้คุณสามารถเห็นได้ว่าแอตทริบิวต์ ForeignKey ถูกนำไปใช้กับคุณสมบัติการนำทาง
ไม่แมป
ตามมาตรฐานเริ่มต้นของ Code First ทุกคุณสมบัติที่เป็นชนิดข้อมูลที่รองรับและซึ่งรวมถึง getters และ setters จะแสดงในฐานข้อมูล แต่นี่ไม่ใช่กรณีเสมอไปในแอปพลิเคชันของคุณ แอตทริบิวต์ NotMapped แทนที่แบบแผนเริ่มต้นนี้ ตัวอย่างเช่นคุณอาจมีคุณสมบัติในชั้นเรียนนักเรียนเช่น FatherName แต่ไม่จำเป็นต้องจัดเก็บ คุณสามารถใช้แอตทริบิวต์ NotMapped กับคุณสมบัติ FatherName ซึ่งคุณไม่ต้องการสร้างคอลัมน์ในฐานข้อมูลดังที่แสดงในรหัสต่อไปนี้
public class Student {
[Key]
public int StdntID { get; set; }
public string LastName { get; set; }
public string FirstMidName { get; set; }
public DateTime EnrollmentDate { get; set; }
[NotMapped]
public int FatherName { get; set; }
public virtual ICollection<Enrollment> Enrollments { get; set; }
}
คุณจะเห็นว่ามีการใช้แอตทริบิวต์ NotMapped กับคุณสมบัติ FatherName เมื่อสร้างตารางคุณจะเห็นว่าคอลัมน์ FatherName จะไม่ถูกสร้างขึ้นในฐานข้อมูล แต่มีอยู่ในชั้นเรียนของนักเรียน
Code First จะไม่สร้างคอลัมน์สำหรับคุณสมบัติซึ่งไม่มี getters หรือ setters ดังที่แสดงในตัวอย่างคุณสมบัติ Address and Age ของคลาส Student ต่อไปนี้
คุณสมบัติผกผัน
InverseProperty ถูกใช้เมื่อคุณมีหลายความสัมพันธ์ระหว่างคลาส ในชั้นเรียนการลงทะเบียนคุณอาจต้องการติดตามผู้ที่ลงทะเบียนหลักสูตรปัจจุบันและหลักสูตรก่อนหน้า เพิ่มคุณสมบัติการนำทางสองอย่างสำหรับคลาสการลงทะเบียน
public class Enrollment {
public int EnrollmentID { get; set; }
public int CourseID { get; set; }
public int StudentID { get; set; }
public Grade? Grade { get; set; }
public virtual Course CurrCourse { get; set; }
public virtual Course PrevCourse { get; set; }
public virtual Student Student { get; set; }
}
ในทำนองเดียวกันคุณจะต้องเพิ่มในชั้นเรียนหลักสูตรที่อ้างอิงโดยคุณสมบัติเหล่านี้ คลาสหลักสูตรมีคุณสมบัติการนำทางกลับไปที่คลาสการลงทะเบียนซึ่งมีการลงทะเบียนปัจจุบันและก่อนหน้าทั้งหมด
public class Course {
public int CourseID { get; set; }
public string Title { get; set; }
[Index]
public int Credits { get; set; }
public virtual ICollection<Enrollment> CurrEnrollments { get; set; }
public virtual ICollection<Enrollment> PrevEnrollments { get; set; }
}
Code First สร้างคอลัมน์คีย์ต่างประเทศของ {Class Name} _ {Primary Key} ถ้าคุณสมบัติ Foreign Key ไม่รวมอยู่ในคลาสใดคลาสหนึ่งดังที่แสดงในคลาสด้านบน เมื่อสร้างฐานข้อมูลคุณจะเห็นคีย์ต่างประเทศต่อไปนี้
อย่างที่คุณเห็นว่า Code first ไม่สามารถจับคู่คุณสมบัติในสองคลาสได้ด้วยตัวเอง ตารางฐานข้อมูลสำหรับการลงทะเบียนควรมีคีย์ต่างประเทศหนึ่งคีย์สำหรับ CurrCourse และอีกหนึ่งคีย์สำหรับ PrevCourse แต่ Code First จะสร้างคุณสมบัติคีย์ต่างประเทศสี่รายการ ได้แก่
- CurrCourse _CourseID
- PrevCourse _CourseID
- Course_CourseID และ
- Course_CourseID1
ในการแก้ไขปัญหาเหล่านี้คุณสามารถใช้คำอธิบายประกอบ InverseProperty เพื่อระบุการจัดตำแหน่งของคุณสมบัติ
public class Course {
public int CourseID { get; set; }
public string Title { get; set; }
[Index]
public int Credits { get; set; }
[InverseProperty("CurrCourse")]
public virtual ICollection<Enrollment> CurrEnrollments { get; set; }
[InverseProperty("PrevCourse")]
public virtual ICollection<Enrollment> PrevEnrollments { get; set; }
}
ดังที่คุณเห็นแอตทริบิวต์ InverseProperty ถูกนำไปใช้ในคลาสหลักสูตรข้างต้นโดยระบุคุณสมบัติอ้างอิงของคลาสการลงทะเบียนที่เป็นของ ตอนนี้ Code First จะสร้างฐานข้อมูลและสร้างคอลัมน์คีย์ต่างประเทศเพียงสองคอลัมน์ในตารางการลงทะเบียนดังที่แสดงในภาพต่อไปนี้
เราขอแนะนำให้คุณดำเนินการตามตัวอย่างข้างต้นในลักษณะทีละขั้นตอนเพื่อความเข้าใจที่ดีขึ้น
Fluent API เป็นวิธีขั้นสูงในการระบุการกำหนดค่าโมเดลที่ครอบคลุมทุกสิ่งที่คำอธิบายประกอบข้อมูลสามารถทำได้นอกเหนือจากการกำหนดค่าขั้นสูงบางอย่างที่ไม่สามารถทำได้ด้วยคำอธิบายประกอบข้อมูล คำอธิบายประกอบข้อมูลและ API ที่คล่องแคล่วสามารถใช้ร่วมกันได้ แต่ Code First ให้ความสำคัญกับ Fluent API> คำอธิบายประกอบข้อมูล> ข้อตกลงเริ่มต้น
Fluent API เป็นอีกวิธีหนึ่งในการกำหนดค่าคลาสโดเมนของคุณ
Code First Fluent API สามารถเข้าถึงได้บ่อยที่สุดโดยการแทนที่เมธอด OnModelCreating บน DbContext ที่ได้รับของคุณ
Fluent API มีฟังก์ชันการทำงานสำหรับการกำหนดค่ามากกว่า DataAnnotations Fluent API รองรับการแมปประเภทต่อไปนี้
ในบทนี้เราจะดำเนินการต่อด้วยตัวอย่างง่ายๆซึ่งประกอบด้วยคลาสนักเรียนหลักสูตรและการลงทะเบียนและคลาสบริบทหนึ่งคลาสที่มีชื่อ MyContext ดังแสดงในโค้ดต่อไปนี้
using System.Data.Entity;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace EFCodeFirstDemo {
class Program {
static void Main(string[] args) {}
}
public enum Grade {
A, B, C, D, F
}
public class Enrollment {
public int EnrollmentID { get; set; }
public int CourseID { get; set; }
public int StudentID { get; set; }
public Grade? Grade { get; set; }
public virtual Course Course { get; set; }
public virtual Student Student { get; set; }
}
public class Student {
public int ID { get; set; }
public string LastName { get; set; }
public string FirstMidName { get; set; }
public DateTime EnrollmentDate { get; set; }
public virtual ICollection<Enrollment> Enrollments { get; set; }
}
public class Course {
public int CourseID { get; set; }
public string Title { get; set; }
public int Credits { get; set; }
public virtual ICollection<Enrollment> Enrollments { get; set; }
}
public class MyContext : DbContext {
public virtual DbSet<Course> Courses { get; set; }
public virtual DbSet<Enrollment> Enrollments { get; set; }
public virtual DbSet<Student> Students { get; set; }
}
}
ในการเข้าถึง Fluent API คุณต้องแทนที่เมธอด OnModelCreating ใน DbContext มาดูตัวอย่างง่ายๆที่เราจะเปลี่ยนชื่อคอลัมน์ในตารางนักเรียนจาก FirstMidName เป็น FirstName ดังแสดงในโค้ดต่อไปนี้
public class MyContext : DbContext {
protected override void OnModelCreating(DbModelBuilder modelBuilder) {
modelBuilder.Entity<Student>().Property(s ⇒ s.FirstMidName)
.HasColumnName("FirstName");}
public virtual DbSet<Course> Courses { get; set; }
public virtual DbSet<Enrollment> Enrollments { get; set; }
public virtual DbSet<Student> Students { get; set; }
}
DbModelBuilder ใช้เพื่อแม็พคลาส CLR กับสกีมาฐานข้อมูล เป็นคลาสหลักและคุณสามารถกำหนดค่าคลาสโดเมนทั้งหมดของคุณได้ แนวทางรหัสเป็นศูนย์กลางในการสร้าง Entity Data Model (EDM) เรียกว่า Code First
Fluent API มีวิธีการที่สำคัญหลายวิธีในการกำหนดค่าเอนทิตีและคุณสมบัติเพื่อแทนที่อนุสัญญา Code First ต่างๆ ด้านล่างนี้คือบางส่วนของพวกเขา
เลขที่ | ชื่อวิธีการและคำอธิบาย |
---|---|
1 | ComplexType<TComplexType> ลงทะเบียนประเภทเป็นประเภทที่ซับซ้อนในโมเดลและส่งคืนอ็อบเจ็กต์ที่สามารถใช้เพื่อกำหนดค่าประเภทที่ซับซ้อน วิธีนี้สามารถเรียกได้หลายครั้งสำหรับประเภทเดียวกันเพื่อดำเนินการกำหนดค่าหลายบรรทัด |
2 | Entity<TEntityType> ลงทะเบียนประเภทเอนทิตีเป็นส่วนหนึ่งของโมเดลและส่งคืนอ็อบเจ็กต์ที่สามารถใช้เพื่อกำหนดค่าเอนทิตี วิธีนี้สามารถเรียกได้หลายครั้งเพื่อให้เอนทิตีเดียวกันดำเนินการกำหนดค่าหลายบรรทัด |
3 | HasKey<TKey> กำหนดค่าคุณสมบัติคีย์หลักสำหรับประเภทเอนทิตีนี้ |
4 | HasMany<TTargetEntity> กำหนดค่าความสัมพันธ์จำนวนมากจากชนิดเอนทิตีนี้ |
5 | HasOptional<TTargetEntity> กำหนดค่าความสัมพันธ์ทางเลือกจากประเภทเอนทิตีนี้ อินสแตนซ์ของประเภทเอนทิตีจะสามารถบันทึกลงในฐานข้อมูลได้โดยไม่ต้องระบุความสัมพันธ์นี้ คีย์ภายนอกในฐานข้อมูลจะเป็นโมฆะ |
6 | HasRequired<TTargetEntity> กำหนดค่าความสัมพันธ์ที่ต้องการจากประเภทเอนทิตีนี้ อินสแตนซ์ของประเภทเอนทิตีจะไม่สามารถบันทึกลงในฐานข้อมูลได้เว้นแต่จะระบุความสัมพันธ์นี้ คีย์ภายนอกในฐานข้อมูลจะไม่เป็นโมฆะ |
7 | Ignore<TProperty> ไม่รวมคุณสมบัติจากโมเดลเพื่อไม่ให้แมปกับฐานข้อมูล (สืบทอดมาจาก StructuralTypeConfiguration <TStructuralType>) |
8 | Property<T> กำหนดค่าคุณสมบัติโครงสร้างที่กำหนดไว้ในชนิดนี้ (สืบทอดมาจาก StructuralTypeConfiguration <TStructuralType>) |
9 | ToTable(String) กำหนดค่าชื่อตารางที่แมปชนิดเอนทิตีนี้ |
Fluent API ช่วยให้คุณกำหนดค่าเอนทิตีหรือคุณสมบัติของพวกเขาได้ไม่ว่าคุณจะต้องการเปลี่ยนแปลงบางอย่างเกี่ยวกับวิธีที่พวกเขาแมปกับฐานข้อมูลหรือความสัมพันธ์ระหว่างกัน มีการแมปและการสร้างแบบจำลองมากมายที่คุณสามารถส่งผลกระทบได้โดยใช้การกำหนดค่า ต่อไปนี้เป็นประเภทหลักของการทำแผนที่ที่ Fluent API รองรับ -
- การแมปเอนทิตี
- การแมปคุณสมบัติ
การแมปเอนทิตี
การแมปเอนทิตีเป็นเพียงการแมปง่ายๆที่จะส่งผลต่อความเข้าใจของ Entity Framework เกี่ยวกับวิธีการแมปคลาสกับฐานข้อมูล ทั้งหมดนี้เราได้พูดถึงในคำอธิบายประกอบข้อมูลและที่นี่เราจะดูวิธีการบรรลุสิ่งเดียวกันโดยใช้ Fluent API
ดังนั้นแทนที่จะเข้าไปในคลาสโดเมนเพื่อเพิ่มการกำหนดค่าเหล่านี้เราสามารถทำได้ภายในบริบท
สิ่งแรกคือการแทนที่เมธอด OnModelCreating ซึ่งทำให้ modelBuilder ทำงานได้
สคีมาเริ่มต้น
สคีมาเริ่มต้นคือ dbo เมื่อสร้างฐานข้อมูล คุณสามารถใช้เมธอด HasDefaultSchema บน DbModelBuilder เพื่อระบุสกีมาฐานข้อมูลที่จะใช้สำหรับตารางทั้งหมดโพรซีเดอร์ที่เก็บไว้ ฯลฯ
ลองดูตัวอย่างต่อไปนี้ที่ใช้สคีมาของผู้ดูแลระบบ
public class MyContext : DbContext {
public MyContext() : base("name = MyContextDB") {}
protected override void OnModelCreating(DbModelBuilder modelBuilder) {
//Configure default schema
modelBuilder.HasDefaultSchema("Admin");
}
public virtual DbSet<Course> Courses { get; set; }
public virtual DbSet<Enrollment> Enrollments { get; set; }
public virtual DbSet<Student> Students { get; set; }
}
แมปเอนทิตีกับตาราง
ด้วยรูปแบบเริ่มต้น Code First จะสร้างตารางฐานข้อมูลด้วยชื่อของคุณสมบัติ DbSet ในคลาสบริบทเช่นหลักสูตรการลงทะเบียนและนักเรียน แต่ถ้าคุณต้องการชื่อตารางที่แตกต่างกันคุณสามารถแทนที่หลักการนี้และสามารถระบุชื่อตารางที่แตกต่างจากคุณสมบัติ DbSet ดังที่แสดงในโค้ดต่อไปนี้
protected override void OnModelCreating(DbModelBuilder modelBuilder) {
//Configure default schema
modelBuilder.HasDefaultSchema("Admin");
//Map entity to table
modelBuilder.Entity<Student>().ToTable("StudentData");
modelBuilder.Entity<Course>().ToTable("CourseDetail");
modelBuilder.Entity<Enrollment>().ToTable("EnrollmentInfo");
}
เมื่อสร้างฐานข้อมูลคุณจะเห็นชื่อตารางตามที่ระบุในเมธอด OnModelCreating
การแยกเอนทิตี (แมปเอนทิตีเป็นตารางหลายตาราง)
การแยกเอนทิตีช่วยให้คุณรวมข้อมูลที่มาจากตารางหลายตารางเป็นคลาสเดียวและสามารถใช้ได้เฉพาะกับตารางที่มีความสัมพันธ์แบบหนึ่งต่อกลุ่มเท่านั้น ลองดูตัวอย่างต่อไปนี้ที่ข้อมูลของนักเรียนถูกจับคู่เป็นสองตาราง
protected override void OnModelCreating(DbModelBuilder modelBuilder) {
//Configure default schema
modelBuilder.HasDefaultSchema("Admin");
//Map entity to table
modelBuilder.Entity<Student>().Map(sd ⇒ {
sd.Properties(p ⇒ new { p.ID, p.FirstMidName, p.LastName });
sd.ToTable("StudentData");
})
.Map(si ⇒ {
si.Properties(p ⇒ new { p.ID, p.EnrollmentDate });
si.ToTable("StudentEnrollmentInfo");
});
modelBuilder.Entity<Course>().ToTable("CourseDetail");
modelBuilder.Entity<Enrollment>().ToTable("EnrollmentInfo");
}
ในโค้ดด้านบนคุณจะเห็นว่าเอนทิตี Student ถูกแบ่งออกเป็นสองตารางต่อไปนี้โดยการแมปคุณสมบัติบางอย่างกับตาราง StudentData และคุณสมบัติบางอย่างกับตาราง StudentEnrollmentInfo โดยใช้เมธอด Map
StudentData - ประกอบด้วย Student FirstMidName และนามสกุล
StudentEnrollmentInfo - ประกอบด้วย EnrollmentDate
เมื่อสร้างฐานข้อมูลคุณจะเห็นตารางต่อไปนี้ในฐานข้อมูลของคุณดังแสดงในภาพต่อไปนี้
การแมปคุณสมบัติ
วิธีคุณสมบัติใช้เพื่อกำหนดค่าแอตทริบิวต์สำหรับคุณสมบัติแต่ละรายการที่เป็นของเอนทิตีหรือประเภทที่ซับซ้อน เมธอดคุณสมบัติถูกใช้เพื่อรับอ็อบเจ็กต์คอนฟิกูเรชันสำหรับคุณสมบัติที่กำหนด คุณยังสามารถแมปและกำหนดค่าคุณสมบัติของคลาสโดเมนของคุณโดยใช้ Fluent API
การกำหนดค่าคีย์หลัก
หลักการเริ่มต้นสำหรับคีย์หลักคือ -
- Class กำหนดคุณสมบัติที่มีชื่อเป็น“ ID” หรือ“ Id”
- ชื่อคลาสตามด้วย“ ID” หรือ“ Id”
ถ้าชั้นเรียนของคุณไม่เป็นไปตามแบบแผนเริ่มต้นสำหรับคีย์หลักดังที่แสดงในรหัสต่อไปนี้ของคลาสนักเรียน -
public class Student {
public int StdntID { get; set; }
public string LastName { get; set; }
public string FirstMidName { get; set; }
public DateTime EnrollmentDate { get; set; }
public virtual ICollection<Enrollment> Enrollments { get; set; }
}
จากนั้นในการตั้งค่าคุณสมบัติให้เป็นคีย์หลักอย่างชัดเจนคุณสามารถใช้เมธอด HasKey ดังที่แสดงในโค้ดต่อไปนี้ -
protected override void OnModelCreating(DbModelBuilder modelBuilder) {
//Configure default schema
modelBuilder.HasDefaultSchema("Admin");
// Configure Primary Key
modelBuilder.Entity<Student>().HasKey<int>(s ⇒ s.StdntID);
}
กำหนดค่าคอลัมน์
ใน Entity Framework โดยค่าเริ่มต้น Code First จะสร้างคอลัมน์สำหรับคุณสมบัติที่มีชื่อลำดับและประเภทข้อมูลเดียวกัน แต่คุณยังสามารถแทนที่หลักการนี้ได้ดังแสดงในรหัสต่อไปนี้
protected override void OnModelCreating(DbModelBuilder modelBuilder) {
//Configure default schema
modelBuilder.HasDefaultSchema("Admin");
//Configure EnrollmentDate Column
modelBuilder.Entity<Student>().Property(p ⇒ p.EnrollmentDate)
.HasColumnName("EnDate")
.HasColumnType("DateTime")
.HasColumnOrder(2);
}
กำหนดค่า MaxLength Property
ในตัวอย่างต่อไปนี้คุณสมบัติชื่อหลักสูตรควรมีความยาวไม่เกิน 24 อักขระ เมื่อผู้ใช้ระบุค่ายาวกว่า 24 อักขระผู้ใช้จะได้รับข้อยกเว้น DbEntityValidationException
protected override void OnModelCreating(DbModelBuilder modelBuilder) {
//Configure default schema
modelBuilder.HasDefaultSchema("Admin");
modelBuilder.Entity<Course>().Property(p ⇒ p.Title).HasMaxLength(24);
}
กำหนดค่าคุณสมบัติ Null หรือ NotNull
ในตัวอย่างต่อไปนี้จำเป็นต้องมีคุณสมบัติชื่อหลักสูตรเพื่อใช้เมธอด IsRequired เพื่อสร้างคอลัมน์ NotNull ในทำนองเดียวกัน Student EnrollmentDate เป็นทางเลือกดังนั้นเราจะใช้วิธี IsOptional เพื่อให้ค่า null ในคอลัมน์นี้ดังแสดงในรหัสต่อไปนี้
protected override void OnModelCreating(DbModelBuilder modelBuilder) {
//Configure default schema
modelBuilder.HasDefaultSchema("Admin");
modelBuilder.Entity<Course>().Property(p ⇒ p.Title).IsRequired();
modelBuilder.Entity<Student>().Property(p ⇒ p.EnrollmentDate).IsOptional();
//modelBuilder.Entity<Student>().Property(s ⇒ s.FirstMidName)
//.HasColumnName("FirstName");
}
การกำหนดค่าความสัมพันธ์
ความสัมพันธ์ในบริบทของฐานข้อมูลคือสถานการณ์ที่มีอยู่ระหว่างตารางฐานข้อมูลเชิงสัมพันธ์สองตารางเมื่อตารางหนึ่งมีคีย์ต่างประเทศที่อ้างอิงคีย์หลักของตารางอื่น เมื่อทำงานกับ Code First คุณกำหนดโมเดลของคุณโดยกำหนดคลาส CLR ของโดเมนของคุณ ตามค่าเริ่มต้น Entity Framework จะใช้ข้อตกลง Code First เพื่อแม็พคลาสของคุณกับสกีมาฐานข้อมูล
หากคุณใช้หลักการตั้งชื่อ Code First ในกรณีส่วนใหญ่คุณสามารถใช้ Code First เพื่อตั้งค่าความสัมพันธ์ระหว่างตารางของคุณตามคีย์ต่างประเทศและคุณสมบัติการนำทาง
หากไม่ตรงตามอนุสัญญาเหล่านั้นนอกจากนี้ยังมีการกำหนดค่าที่คุณสามารถใช้เพื่อส่งผลกระทบต่อความสัมพันธ์ระหว่างคลาสและวิธีที่ความสัมพันธ์เหล่านั้นรับรู้ในฐานข้อมูลเมื่อคุณเพิ่มการกำหนดค่าใน Code First
บางส่วนมีอยู่ในคำอธิบายประกอบข้อมูลและคุณสามารถใช้คำอธิบายประกอบที่ซับซ้อนยิ่งขึ้นได้ด้วย Fluent API
กำหนดค่าความสัมพันธ์แบบหนึ่งต่อหนึ่ง
เมื่อคุณกำหนดความสัมพันธ์แบบหนึ่งต่อกลุ่มในโมเดลของคุณคุณจะใช้คุณสมบัติการนำทางอ้างอิงในแต่ละคลาส ในฐานข้อมูลทั้งสองตารางสามารถมีระเบียนเดียวที่ด้านใดด้านหนึ่งของความสัมพันธ์ ค่าคีย์หลักแต่ละค่าเกี่ยวข้องกับเรกคอร์ดเดียว (หรือไม่มีเรกคอร์ด) ในตารางที่เกี่ยวข้อง
ความสัมพันธ์แบบหนึ่งต่อหนึ่งจะถูกสร้างขึ้นหากทั้งสองคอลัมน์ที่เกี่ยวข้องเป็นคีย์หลักหรือมีข้อ จำกัด เฉพาะ
ในความสัมพันธ์แบบหนึ่งต่อกลุ่มคีย์หลักจะทำหน้าที่เป็นคีย์ต่างประเทศเพิ่มเติมและไม่มีคอลัมน์คีย์ต่างประเทศแยกสำหรับตารางใดตารางหนึ่ง
ความสัมพันธ์ประเภทนี้ไม่ธรรมดาเนื่องจากข้อมูลส่วนใหญ่ที่เกี่ยวข้องในลักษณะนี้ทั้งหมดจะอยู่ในตารางเดียว
ลองดูตัวอย่างต่อไปนี้ที่เราจะเพิ่มคลาสอื่นในโมเดลของเราเพื่อสร้างความสัมพันธ์แบบหนึ่งต่อกลุ่ม
public class Student {
public int ID { get; set; }
public string LastName { get; set; }
public string FirstMidName { get; set; }
public DateTime EnrollmentDate { get; set; }
public virtual StudentLogIn StudentLogIn { get; set; }
public virtual ICollection<Enrollment> Enrollments { get; set; }
}
public class StudentLogIn {
[Key, ForeignKey("Student")]
public int ID { get; set; }
public string EmailID { get; set; }
public string Password { get; set; }
public virtual Student Student { get; set; }
}
ดังที่คุณเห็นในโค้ดด้านบนว่าแอตทริบิวต์ Key และ ForeignKey ใช้สำหรับคุณสมบัติ ID ในคลาส StudentLogIn เพื่อทำเครื่องหมายเป็นคีย์หลักและ Foreign Key
ในการกำหนดค่าความสัมพันธ์แบบหนึ่งต่อศูนย์หรือหนึ่งระหว่าง Student และ StudentLogIn โดยใช้ Fluent API คุณต้องแทนที่เมธอด OnModelCreating ดังที่แสดงในรหัสต่อไปนี้
protected override void OnModelCreating(DbModelBuilder modelBuilder) {
//Configure default schema
modelBuilder.HasDefaultSchema("Admin");
// Configure ID as PK for StudentLogIn
modelBuilder.Entity<StudentLogIn>()
.HasKey(s ⇒ s.ID);
// Configure ID as FK for StudentLogIn
modelBuilder.Entity<Student>()
.HasOptional(s ⇒ s.StudentLogIn) //StudentLogIn is optional
.WithRequired(t ⇒ t.Student); // Create inverse relationship
}
ในกรณีส่วนใหญ่ Entity Framework สามารถอนุมานได้ว่าประเภทใดขึ้นอยู่กับและชนิดใดเป็นตัวหลักในความสัมพันธ์ อย่างไรก็ตามเมื่อจำเป็นต้องใช้จุดจบทั้งสองข้างหรือทั้งสองด้านเป็นทางเลือก Entity Framework ไม่สามารถระบุการพึ่งพาและหลักได้ เมื่อต้องการความสัมพันธ์ทั้งสองด้านคุณสามารถใช้ HasRequired ดังแสดงในโค้ดต่อไปนี้
protected override void OnModelCreating(DbModelBuilder modelBuilder) {
//Configure default schema
modelBuilder.HasDefaultSchema("Admin");
// Configure ID as PK for StudentLogIn
modelBuilder.Entity<StudentLogIn>()
.HasKey(s ⇒ s.ID);
// Configure ID as FK for StudentLogIn
modelBuilder.Entity<Student>()
.HasRequired(r ⇒ r.Student)
.WithOptional(s ⇒ s.StudentLogIn);
}
เมื่อสร้างฐานข้อมูลคุณจะเห็นความสัมพันธ์นั้นถูกสร้างขึ้นดังที่แสดงในภาพต่อไปนี้
กำหนดค่าความสัมพันธ์แบบหนึ่งต่อกลุ่ม
ตารางคีย์หลักประกอบด้วยเรกคอร์ดเดียวที่เกี่ยวข้องกับไม่มีหนึ่งเรกคอร์ดในตารางที่เกี่ยวข้อง นี่คือประเภทของความสัมพันธ์ที่ใช้บ่อยที่สุด
ในความสัมพันธ์ประเภทนี้แถวในตาราง A สามารถมีแถวที่ตรงกันได้หลายแถวในตาราง B แต่แถวในตาราง B สามารถมีแถวที่ตรงกันเพียงแถวเดียวในตาราง A
คีย์ต่างประเทศถูกกำหนดไว้บนตารางซึ่งแสดงถึงจุดสิ้นสุดของความสัมพันธ์
ตัวอย่างเช่นในแผนภาพด้านบนตารางนักเรียนและการลงทะเบียนมีความสัมพันธ์แบบเดียวกันนักเรียนแต่ละคนอาจมีการลงทะเบียนหลายครั้ง แต่การลงทะเบียนแต่ละครั้งเป็นของนักเรียนเพียงคนเดียว
ด้านล่างนี้คือ Student and Enrollment ซึ่งมีความสัมพันธ์แบบหนึ่งต่อกลุ่ม แต่คีย์ต่างประเทศในตารางการลงทะเบียนไม่เป็นไปตามข้อตกลงเริ่มต้นของ Code First
public class Enrollment {
public int EnrollmentID { get; set; }
public int CourseID { get; set; }
//StdntID is not following code first conventions name
public int StdntID { get; set; }
public Grade? Grade { get; set; }
public virtual Course Course { get; set; }
public virtual Student Student { get; set; }
}
public class Student {
public int ID { get; set; }
public string LastName { get; set; }
public string FirstMidName { get; set; }
public DateTime EnrollmentDate { get; set; }
public virtual StudentLogIn StudentLogIn { get; set; }
public virtual ICollection<Enrollment> Enrollments { get; set; }
}
ในกรณีนี้ในการกำหนดค่าความสัมพันธ์แบบหนึ่งต่อกลุ่มโดยใช้ Fluent API คุณต้องใช้เมธอด HasForeignKey ดังที่แสดงในโค้ดต่อไปนี้
protected override void OnModelCreating(DbModelBuilder modelBuilder) {
//Configure default schema
modelBuilder.HasDefaultSchema("Admin");
//Configure FK for one-to-many relationship
modelBuilder.Entity<Enrollment>()
.HasRequired<Student>(s ⇒ s.Student)
.WithMany(t ⇒ t.Enrollments)
.HasForeignKey(u ⇒ u.StdntID);
}
เมื่อสร้างฐานข้อมูลคุณจะเห็นว่าความสัมพันธ์ถูกสร้างขึ้นดังที่แสดงในภาพต่อไปนี้
ในตัวอย่างข้างต้นเมธอด HasRequired ระบุว่าคุณสมบัติการนำทางของนักเรียนต้องเป็น Null ดังนั้นคุณต้องกำหนด Student ที่มีเอนทิตีการลงทะเบียนทุกครั้งที่คุณเพิ่มหรืออัปเดตการลงทะเบียน ในการจัดการสิ่งนี้เราจำเป็นต้องใช้เมธอด HasOptional แทนวิธี HasRequired
กำหนดค่าความสัมพันธ์แบบกลุ่มต่อกลุ่ม
แต่ละเร็กคอร์ดในทั้งสองตารางสามารถเกี่ยวข้องกับจำนวนเรกคอร์ด (หรือไม่มีเรกคอร์ด) ในตารางอื่น
คุณสามารถสร้างความสัมพันธ์ดังกล่าวได้โดยการกำหนดตารางที่สามเรียกว่าตารางทางแยกซึ่งคีย์หลักประกอบด้วยคีย์ต่างประเทศจากทั้งตาราง A และตาราง B
ตัวอย่างเช่นตารางนักเรียนและตารางหลักสูตรมีความสัมพันธ์แบบกลุ่มต่อกลุ่ม
ต่อไปนี้เป็นชั้นเรียนของนักเรียนและหลักสูตรที่นักเรียนและหลักสูตรมีความสัมพันธ์กันหลายรูปแบบเนื่องจากทั้งสองชั้นเรียนมีคุณสมบัติการนำทางนักเรียนและหลักสูตรที่เป็นคอลเล็กชัน กล่าวอีกนัยหนึ่งเอนทิตีหนึ่งมีคอลเลกชันเอนทิตีอื่น
public class Student {
public int ID { get; set; }
public string LastName { get; set; }
public string FirstMidName { get; set; }
public DateTime EnrollmentDate { get; set; }
public virtual ICollection<Course> Courses { get; set; }
public virtual ICollection<Enrollment> Enrollments { get; set; }
}
public class Course {
public int CourseID { get; set; }
public string Title { get; set; }
public int Credits { get; set; }
public virtual ICollection<Student> Students { get; set; }
public virtual ICollection<Enrollment> Enrollments { get; set; }
}
ในการกำหนดค่าความสัมพันธ์แบบกลุ่มต่อกลุ่มระหว่างนักเรียนและหลักสูตรคุณสามารถใช้ Fluent API ดังที่แสดงในรหัสต่อไปนี้
protected override void OnModelCreating(DbModelBuilder modelBuilder) {
//Configure default schema
modelBuilder.HasDefaultSchema("Admin");
// Configure many-to-many relationship
modelBuilder.Entity<Student>()
.HasMany(s ⇒ s.Courses)
.WithMany(s ⇒ s.Students);
}
หลักการเริ่มต้นของ Code First ถูกใช้เพื่อสร้างตารางการรวมเมื่อสร้างฐานข้อมูล ด้วยเหตุนี้ตาราง StudentCourses จึงถูกสร้างขึ้นด้วยคอลัมน์ Course_CourseID และ Student_ID ดังที่แสดงในภาพต่อไปนี้
หากคุณต้องการระบุชื่อตารางการรวมและชื่อของคอลัมน์ในตารางคุณต้องทำการกำหนดค่าเพิ่มเติมโดยใช้เมธอดแผนที่
protected override void OnModelCreating(DbModelBuilder modelBuilder) {
//Configure default schema
modelBuilder.HasDefaultSchema("Admin");
// Configure many-to-many relationship
modelBuilder.Entity<Student>()
.HasMany(s ⇒ s.Courses)
.WithMany(s ⇒ s.Students)
.Map(m ⇒ {
m.ToTable("StudentCoursesTable");
m.MapLeftKey("StudentID");
m.MapRightKey("CourseID");
});
}
คุณสามารถดูได้ว่าเมื่อใดที่สร้างฐานข้อมูลชื่อตารางและคอลัมน์จะถูกสร้างขึ้นตามที่ระบุไว้ในโค้ดด้านบน
เราขอแนะนำให้คุณดำเนินการตามตัวอย่างข้างต้นในลักษณะทีละขั้นตอนเพื่อความเข้าใจที่ดีขึ้น
ใน Entity Framework Seed ถูกนำมาใช้ใน EF 4.1 และทำงานร่วมกับตัวเริ่มต้นฐานข้อมูล แนวคิดทั่วไปของไฟล์Seed Methodคือการเริ่มต้นข้อมูลลงในฐานข้อมูลที่ Code First สร้างขึ้นหรือพัฒนาโดย Migrations ข้อมูลนี้มักเป็นข้อมูลการทดสอบ แต่อาจเป็นข้อมูลอ้างอิงเช่นรายชื่อนักเรียนที่รู้จักหลักสูตร ฯลฯ เมื่อข้อมูลเริ่มต้นข้อมูลจะดำเนินการดังต่อไปนี้ -
- ตรวจสอบว่ามีฐานข้อมูลเป้าหมายอยู่แล้วหรือไม่
- หากเป็นเช่นนั้นโมเดล Code First ปัจจุบันจะถูกเปรียบเทียบกับโมเดลที่เก็บไว้ในข้อมูลเมตาในฐานข้อมูล
- ฐานข้อมูลจะหายไปหากโมเดลปัจจุบันไม่ตรงกับโมเดลในฐานข้อมูล
- ฐานข้อมูลจะถูกสร้างขึ้นหากถูกทิ้งหรือไม่มีอยู่ตั้งแต่แรก
- หากฐานข้อมูลถูกสร้างขึ้นเมธอด initializer Seed จะถูกเรียก
เมธอด Seed ใช้อ็อบเจ็กต์บริบทฐานข้อมูลเป็นพารามิเตอร์อินพุตและโค้ดในเมธอดใช้อ็อบเจ็กต์นั้นเพื่อเพิ่มเอนทิตีใหม่ให้กับฐานข้อมูล ในการเพาะข้อมูลลงในฐานข้อมูลของคุณคุณต้องแทนที่เมธอด Seed มาดูตัวอย่างต่อไปนี้ซึ่งข้อมูลดีฟอลต์บางส่วนเริ่มต้นในฐานข้อมูลในคลาสภายใน
private class UniDBInitializer<T> : DropCreateDatabaseAlways<MyContext> {
protected override void Seed(MyContext context) {
IList<Student> students = new List<Student>();
students.Add(new Student() {
FirstMidName = "Andrew",
LastName = "Peters",
EnrollmentDate = DateTime.Parse(DateTime.Today.ToString())
});
students.Add(new Student() {
FirstMidName = "Brice",
LastName = "Lambson",
EnrollmentDate = DateTime.Parse(DateTime.Today.ToString())
});
students.Add(new Student() {
FirstMidName = "Rowan",
LastName = "Miller",
EnrollmentDate = DateTime.Parse(DateTime.Today.ToString())
});
foreach (Student student in students)
context.Students.Add(student);
base.Seed(context);
}
}
ในรหัสด้านบนตารางนักเรียนจะเริ่มต้น คุณต้องตั้งค่าคลาส DB initializer นี้ในคลาสบริบทดังที่แสดงในโค้ดต่อไปนี้
public MyContext() : base("name=MyContextDB") {
Database.SetInitializer<MyContext>(new UniDBInitializer<MyContext>());
}
ต่อไปนี้คือการใช้คลาส MyContext แบบสมบูรณ์ซึ่งมีคลาส DB initializer ด้วย
public class MyContext : DbContext {
public MyContext() : base("name=MyContextDB") {
Database.SetInitializer<MyContext>(new UniDBInitializer<MyContext>());
}
public virtual DbSet<Course> Courses { get; set; }
public virtual DbSet<Enrollment> Enrollments { get; set; }
public virtual DbSet<Student> Students { get; set; }
private class UniDBInitializer<T> : DropCreateDatabaseAlways<MyContext> {
protected override void Seed(MyContext context) {
IList<Student> students = new List<Student>();
students.Add(new Student() {
FirstMidName = "Andrew",
LastName = "Peters",
EnrollmentDate = DateTime.Parse(DateTime.Today.ToString())
});
students.Add(new Student() {
FirstMidName = "Brice",
LastName = "Lambson",
EnrollmentDate = DateTime.Parse(DateTime.Today.ToString())
});
students.Add(new Student() {
FirstMidName = "Rowan",
LastName = "Miller",
EnrollmentDate = DateTime.Parse(DateTime.Today.ToString())
});
foreach (Student student in students)
context.Students.Add(student);
base.Seed(context);
}
}
}
เมื่อตัวอย่างข้างต้นถูกรวบรวมและดำเนินการคุณจะเห็นข้อมูลในฐานข้อมูลดังที่แสดงในภาพต่อไปนี้
เราขอแนะนำให้คุณดำเนินการตามตัวอย่างข้างต้นในลักษณะทีละขั้นตอนเพื่อความเข้าใจที่ดีขึ้น
Entity Framework 4.3 มีคุณลักษณะ Code First Migrations ใหม่ที่ช่วยให้คุณสามารถพัฒนาสคีมาฐานข้อมูลได้เพิ่มขึ้นเมื่อโมเดลของคุณเปลี่ยนแปลงไปตามกาลเวลา สำหรับนักพัฒนาส่วนใหญ่นี่เป็นการปรับปรุงครั้งใหญ่ของตัวเลือกตัวเริ่มต้นฐานข้อมูลจากรุ่น 4.1 และ 4.2 ที่ทำให้คุณต้องอัปเดตฐานข้อมูลด้วยตนเองหรือวางและสร้างใหม่เมื่อโมเดลของคุณเปลี่ยนไป
ก่อน Entity Framework 4.3 หากคุณมีข้อมูล (นอกเหนือจากข้อมูล seed) หรือ Stored Procedures ทริกเกอร์ ฯลฯ ที่มีอยู่ในฐานข้อมูลของคุณกลยุทธ์เหล่านี้ใช้เพื่อทิ้งฐานข้อมูลทั้งหมดและสร้างใหม่ดังนั้นคุณจะสูญเสียข้อมูลและฐานข้อมูลอื่น ๆ วัตถุ
ด้วยการย้ายข้อมูลจะอัปเดตสคีมาฐานข้อมูลโดยอัตโนมัติเมื่อโมเดลของคุณเปลี่ยนแปลงโดยไม่สูญเสียข้อมูลที่มีอยู่หรือวัตถุฐานข้อมูลอื่น ๆ
ใช้ตัวเริ่มต้นฐานข้อมูลใหม่ที่เรียกว่า MigrateDatabaseToLatestVersion
การย้ายข้อมูลมีสองประเภท -
- การย้ายข้อมูลอัตโนมัติ
- การโยกย้ายตามรหัส
การย้ายข้อมูลอัตโนมัติ
Automated Migration ถูกนำมาใช้ครั้งแรกใน Entity framework 4.3 ในการย้ายข้อมูลอัตโนมัติคุณไม่จำเป็นต้องดำเนินการย้ายฐานข้อมูลด้วยตนเองในไฟล์โค้ด ตัวอย่างเช่นสำหรับการเปลี่ยนแปลงแต่ละครั้งคุณจะต้องเปลี่ยนคลาสโดเมนของคุณด้วย แต่ด้วยการโยกย้ายอัตโนมัติคุณเพียงแค่เรียกใช้คำสั่งใน Package Manager Console เพื่อดำเนินการนี้
มาดูขั้นตอนการย้ายข้อมูลอัตโนมัติแบบทีละขั้นตอนต่อไปนี้
เมื่อคุณใช้แนวทาง Code First คุณจะไม่มีฐานข้อมูลสำหรับแอปพลิเคชันของคุณ
ในตัวอย่างนี้เราจะเริ่มต้นด้วยคลาสพื้นฐาน 3 คลาสเช่นนักเรียนหลักสูตรและการลงทะเบียนตามที่แสดงในโค้ดต่อไปนี้
public class Enrollment {
public int EnrollmentID { get; set; }
public int CourseID { get; set; }
public int StudentID { get; set; }
public Grade? Grade { get; set; }
public virtual Course Course { get; set; }
public virtual Student Student { get; set; }
}
public class Student {
public int ID { get; set; }
public string LastName { get; set; }
public string FirstMidName { get; set; }
public DateTime EnrollmentDate { get; set; }
public virtual ICollection<Enrollment> Enrollments { get; set; }
}
public class Course {
public int CourseID { get; set; }
public string Title { get; set; }
[Index]
public int Credits { get; set; }
public virtual ICollection<Enrollment> Enrollments { get; set; }
}
ต่อไปนี้เป็นคลาสบริบท
public class MyContext : DbContext {
public MyContext() : base("MyContextDB") {}
public virtual DbSet<Course> Courses { get; set; }
public virtual DbSet<Enrollment> Enrollments { get; set; }
public virtual DbSet<Student> Students { get; set; }
}
ก่อนเรียกใช้แอปพลิเคชันคุณต้องเปิดใช้งานการย้ายข้อมูลอัตโนมัติ
Step 1 - เปิดคอนโซล Package Manger จาก Tools → NuGet Package Manger → Package Manger Console
Step 2 - ในการเปิดใช้งานการโอนย้ายอัตโนมัติให้รันคำสั่งต่อไปนี้ใน Package Manager Console
PM> enable-migrations -EnableAutomaticMigrations:$true
Step 3 - เมื่อคำสั่งทำงานสำเร็จคำสั่งจะสร้างคลาส Configuration ที่ปิดผนึกไว้ภายในในโฟลเดอร์ Migration ของโปรเจ็กต์ของคุณดังที่แสดงในโค้ดต่อไปนี้
namespace EFCodeFirstDemo.Migrations {
using System;
using System.Data.Entity;
using System.Data.Entity.Migrations;
using System.Linq;
internal sealed class Configuration : DbMigrationsConfiguration<EFCodeFirstDemo.MyContext> {
public Configuration() {
AutomaticMigrationsEnabled = true;
ContextKey = "EFCodeFirstDemo.MyContext";
}
protected override void Seed(EFCodeFirstDemo.MyContext context) {
// This method will be called after migrating to the latest version.
// You can use the DbSet<T>.AddOrUpdate() helper extension method
// to avoid creating duplicate seed data. E.g.
// context.People.AddOrUpdate(
// p ⇒ p.FullName,
// new Person { FullName = "Andrew Peters" },
// new Person { FullName = "Brice Lambson" },
// new Person { FullName = "Rowan Miller" }
// );
}
}
}
Step 4 - ตั้งค่าตัวเริ่มต้นฐานข้อมูลในคลาสบริบทด้วยกลยุทธ์การเริ่มต้น DB ใหม่ MigrateDatabaseToLatestVersion
public class MyContext : DbContext {
public MyContext() : base("MyContextDB") {
Database.SetInitializer(new MigrateDatabaseToLatestVersion<MyContext,
EFCodeFirstDemo.Migrations.Configuration>("MyContextDB"));
}
public virtual DbSet<Course> Courses { get; set; }
public virtual DbSet<Enrollment> Enrollments { get; set; }
public virtual DbSet<Student> Students { get; set; }
}
Step 5- คุณได้ตั้งค่าการย้ายข้อมูลอัตโนมัติ เมื่อคุณเรียกใช้แอปพลิเคชันของคุณแอปพลิเคชันจะดูแลการโยกย้ายโดยอัตโนมัติเมื่อคุณเปลี่ยนรุ่น
Step 6- ดังที่คุณเห็นว่าตารางระบบ __MigrationHistory หนึ่งตารางถูกสร้างขึ้นในฐานข้อมูลของคุณพร้อมกับตารางอื่นด้วย ใน __MigrationHistory การโอนย้ายอัตโนมัติจะเก็บรักษาประวัติการเปลี่ยนแปลงฐานข้อมูล
Step 7- เมื่อคุณเพิ่มคลาสเอนทิตีอื่นเป็นคลาสโดเมนของคุณและดำเนินการแอปพลิเคชันของคุณมันจะสร้างตารางในฐานข้อมูลของคุณ มาเพิ่มคลาส StudentLogIn ต่อไปนี้
public class StudentLogIn {
[Key, ForeignKey("Student")]
public int ID { get; set; }
public string EmailID { get; set; }
public string Password { get; set; }
public virtual Student Student { get; set; }
}
Step 8 - อย่าลืมเพิ่ม DBSet สำหรับคลาสที่กล่าวถึงข้างต้นในคลาสบริบทของคุณดังที่แสดงในโค้ดต่อไปนี้
public virtual DbSet<StudentLogIn> StudentsLogIn { get; set; }
Step 9 - เรียกใช้แอปพลิเคชันของคุณอีกครั้งและคุณจะเห็นว่ามีการเพิ่มตาราง StudentsLogIn ในฐานข้อมูลของคุณ
ขั้นตอนข้างต้นที่กล่าวถึงสำหรับการย้ายข้อมูลอัตโนมัติจะใช้ได้กับเอนทิตีของคุณเท่านั้น ตัวอย่างเช่นหากต้องการเพิ่มคลาสเอนทิตีอื่นหรือลบคลาสเอนทิตีที่มีอยู่ออกก็จะโอนย้ายได้สำเร็จ แต่ถ้าคุณเพิ่มหรือลบคุณสมบัติใด ๆ ในคลาสเอนทิตีของคุณมันจะทำให้เกิดข้อยกเว้น
Step 10 - ในการจัดการการย้ายคุณสมบัติคุณต้องตั้งค่า AutomaticMigrationDataLossAllowed = true ในคอนสตรัคเตอร์คลาสคอนฟิกูเรชัน
public Configuration() {
AutomaticMigrationsEnabled = true;
AutomaticMigrationDataLossAllowed = true;
ContextKey = "EFCodeFirstDemo.MyContext";
}
การโยกย้ายตามรหัส
เมื่อคุณพัฒนาแอปพลิเคชันใหม่โมเดลข้อมูลของคุณจะเปลี่ยนแปลงบ่อยและทุกครั้งที่โมเดลมีการเปลี่ยนแปลงโมเดลจะไม่ซิงค์กับฐานข้อมูล คุณได้กำหนดค่า Entity Framework ให้วางและสร้างฐานข้อมูลใหม่โดยอัตโนมัติทุกครั้งที่คุณเปลี่ยนโมเดลข้อมูล การโยกย้ายโดยใช้รหัสมีประโยชน์เมื่อคุณต้องการควบคุมการย้ายข้อมูลมากขึ้น
เมื่อคุณเพิ่มลบหรือเปลี่ยนคลาสเอนทิตีหรือเปลี่ยนคลาส DbContext ของคุณในครั้งถัดไปที่คุณเรียกใช้แอปพลิเคชันแอปพลิเคชันจะลบฐานข้อมูลที่มีอยู่ของคุณโดยอัตโนมัติสร้างใหม่ที่ตรงกับโมเดลและขยายด้วยข้อมูลทดสอบ
คุณลักษณะ Code First Migrations สามารถแก้ปัญหานี้ได้โดยการเปิดใช้งาน Code First เพื่ออัปเดตสกีมาฐานข้อมูลแทนที่จะทิ้งและสร้างฐานข้อมูลใหม่ ในการปรับใช้แอปพลิเคชันคุณจะต้องเปิดใช้งานการย้ายข้อมูล
นี่คือกฎพื้นฐานในการย้ายการเปลี่ยนแปลงในฐานข้อมูล -
- เปิดใช้งานการย้ายข้อมูล
- เพิ่มการย้ายข้อมูล
- อัปเดตฐานข้อมูล
มาดูขั้นตอนการโอนย้ายรหัสฐานทีละขั้นตอนต่อไปนี้
เมื่อคุณใช้รหัสแนวทางแรกคุณจะไม่มีฐานข้อมูลสำหรับแอปพลิเคชันของคุณ
ในตัวอย่างนี้เราจะเริ่มต้นอีกครั้งด้วยคลาสพื้นฐาน 3 คลาสเช่นนักเรียนหลักสูตรและการลงทะเบียนตามที่แสดงในโค้ดต่อไปนี้
public class Enrollment {
public int EnrollmentID { get; set; }
public int CourseID { get; set; }
public int StudentID { get; set; }
public Grade? Grade { get; set; }
public virtual Course Course { get; set; }
public virtual Student Student { get; set; }
}
public class Student {
public int ID { get; set; }
public string LastName { get; set; }
public string FirstMidName { get; set; }
public DateTime EnrollmentDate { get; set; }
public virtual ICollection<Enrollment> Enrollments { get; set; }
}
public class Course {
public int CourseID { get; set; }
public string Title { get; set; }
[Index]
public int Credits { get; set; }
public virtual ICollection<Enrollment> Enrollments { get; set; }
}
ต่อไปนี้เป็นคลาสบริบท
public class MyContext : DbContext {
public MyContext() : base("MyContextDB") {
Database.SetInitializer(new MigrateDatabaseToLatestVersion<
MyContext, EFCodeFirstDemo.Migrations.Configuration>("MyContextDB"));
}
public virtual DbSet<Course> Courses { get; set; }
public virtual DbSet<Enrollment> Enrollments { get; set; }
public virtual DbSet<Student> Students { get; set; }
}
Step 1 - ก่อนเรียกใช้แอปพลิเคชันคุณต้องเปิดใช้งานการย้ายข้อมูล
Step 2 - เปิดคอนโซล Package Manager จาก Tools → NuGet Package Manger → Package Manger Console
Step 3 - เปิดใช้งานการย้ายข้อมูลแล้วตอนนี้เพิ่มการย้ายข้อมูลในแอปพลิเคชันของคุณโดยดำเนินการคำสั่งต่อไปนี้
PM> add-migration "UniDB Schema"
Step 4 - เมื่อดำเนินการคำสั่งสำเร็จคุณจะเห็นไฟล์ใหม่ถูกสร้างขึ้นในโฟลเดอร์ Migration พร้อมชื่อของพารามิเตอร์ที่คุณส่งไปยังคำสั่งพร้อมคำนำหน้าเวลาประทับดังที่แสดงในภาพต่อไปนี้
Step 5 - คุณสามารถสร้างหรืออัปเดตฐานข้อมูลโดยใช้คำสั่ง“ update-database”
PM> Update-Database -Verbose
แฟล็ก "-Verbose" ระบุเพื่อแสดงคำสั่ง SQL ที่ใช้กับฐานข้อมูลเป้าหมายในคอนโซล
Step 6 - เพิ่มคุณสมบัติ 'อายุ' อีกหนึ่งรายการในชั้นเรียนของนักเรียนจากนั้นดำเนินการคำสั่งอัปเดต
public class Student {
public int ID { get; set; }
public string LastName { get; set; }
public string FirstMidName { get; set; }
public int Age { get; set; }
public DateTime EnrollmentDate { get; set; }
public virtual ICollection<Enrollment> Enrollments { get; set; }
}
เมื่อคุณรัน PM → Update-Database - Verbose เมื่อคำสั่งถูกเรียกใช้งานสำเร็จคุณจะเห็นว่าคอลัมน์ใหม่ Age ถูกเพิ่มในฐานข้อมูลของคุณ
เราขอแนะนำให้คุณดำเนินการตามตัวอย่างข้างต้นในลักษณะทีละขั้นตอนเพื่อความเข้าใจที่ดีขึ้น
ในบทนี้เราจะเรียนรู้วิธีการย้ายการเปลี่ยนแปลงไปยังฐานข้อมูลเมื่อมี DbContext หลายคลาสในแอปพลิเคชัน
- DbContext หลายตัวถูกนำมาใช้ครั้งแรกใน Entity Framework 6.0
- หลายบริบทคลาสอาจเป็นของฐานข้อมูลเดียวหรือสองฐานข้อมูลที่แตกต่างกัน
ในตัวอย่างของเราเราจะกำหนดคลาสบริบทสองคลาสสำหรับฐานข้อมูลเดียวกัน ในโค้ดต่อไปนี้มีคลาส DbContext สองคลาสสำหรับนักเรียนและครู
public class Student {
public int ID { get; set; }
public string LastName { get; set; }
public string FirstMidName { get; set; }
public DateTime EnrollmentDate { get; set; }
}
public class MyStudentContext : DbContext {
public MyStudentContext() : base("UniContextDB") {}
public virtual DbSet<Student> Students { get; set; }
}
public class Teacher {
public int ID { get; set; }
public string LastName { get; set; }
public string FirstMidName { get; set; }
public DateTime HireDate { get; set; }
}
public class MyTeacherContext : DbContext {
public MyTeacherContext() : base("UniContextDB") {}
public virtual DbSet<Teacher> Teachers { get; set; }
}
ดังที่คุณเห็นในโค้ดด้านบนมีสองโมเดลที่เรียกว่า "นักเรียน" และ "ครู" แต่ละคนเชื่อมโยงกับคลาสบริบทที่เกี่ยวข้องโดยเฉพาะกล่าวคือ Student เชื่อมโยงกับ MyStudentContext และ Teacher เชื่อมโยงกับ MyTeacherContext
นี่คือกฎพื้นฐานในการโอนย้ายการเปลี่ยนแปลงในฐานข้อมูลเมื่อมีคลาสบริบทหลายคลาสภายในโปรเจ็กต์เดียวกัน
enable-migrations -ContextTypeName <DbContext-Name-with-Namespaces> MigrationsDirectory: <Migrations-Directory-Name>
Add-Migration -configuration <DbContext-Migrations-Configuration-Class-withNamespaces> <Migrations-Name>
อัพเดตฐานข้อมูล - คอนฟิกูเรชัน <DbContext-Migrations-Configuration-Class-withNamespaces> -Verbose
มาเปิดใช้งานการโอนย้ายสำหรับ MyStudentContext โดยดำเนินการคำสั่งต่อไปนี้ใน Package Manager Console
PM→ enable-migrations -ContextTypeName:EFCodeFirstDemo.MyStudentContext
เมื่อดำเนินการแล้วเราจะเพิ่มโมเดลในประวัติการย้ายข้อมูลและสำหรับสิ่งนั้นเราต้องเริ่มคำสั่ง add-migration ในคอนโซลเดียวกัน
PM→ add-migration -configuration EFCodeFirstDemo.Migrations.Configuration Initial
ตอนนี้ให้เราเพิ่มข้อมูลลงในตารางนักเรียนและครูในฐานข้อมูล
static void Main(string[] args) {
using (var context = new MyStudentContext()) {
//// Create and save a new Students
Console.WriteLine("Adding new students");
var student = new Student {
FirstMidName = "Alain",
LastName = "Bomer",
EnrollmentDate = DateTime.Parse(DateTime.Today.ToString())
//Age = 24
};
context.Students.Add(student);
var student1 = new Student {
FirstMidName = "Mark",
LastName = "Upston",
EnrollmentDate = DateTime.Parse(DateTime.Today.ToString())
//Age = 30
};
context.Students.Add(student1);
context.SaveChanges();
// Display all Students from the database
var students = (from s in context.Students orderby s.FirstMidName
select s).ToList<Student>();
Console.WriteLine("Retrieve all Students from the database:");
foreach (var stdnt in students) {
string name = stdnt.FirstMidName + " " + stdnt.LastName;
Console.WriteLine("ID: {0}, Name: {1}", stdnt.ID, name);
}
Console.WriteLine("Press any key to exit...");
Console.ReadKey();
}
using (var context = new MyTeacherContext()) {
//// Create and save a new Teachers
Console.WriteLine("Adding new teachers");
var student = new Teacher {
FirstMidName = "Alain",
LastName = "Bomer",
HireDate = DateTime.Parse(DateTime.Today.ToString())
//Age = 24
};
context.Teachers.Add(student);
var student1 = new Teacher {
FirstMidName = "Mark",
LastName = "Upston",
HireDate = DateTime.Parse(DateTime.Today.ToString())
//Age = 30
};
context.Teachers.Add(student1);
context.SaveChanges();
// Display all Teachers from the database
var teachers = (from t in context.Teachers orderby t.FirstMidName
select t).ToList<Teacher>();
Console.WriteLine("Retrieve all teachers from the database:");
foreach (var teacher in teachers) {
string name = teacher.FirstMidName + " " + teacher.LastName;
Console.WriteLine("ID: {0}, Name: {1}", teacher.ID, name);
}
Console.WriteLine("Press any key to exit...");
Console.ReadKey();
}
}
เมื่อดำเนินการโค้ดด้านบนคุณจะเห็นว่ามีการสร้างตารางที่แตกต่างกันสองตารางสำหรับโมเดลที่แตกต่างกันสองแบบดังที่แสดงในภาพต่อไปนี้
เราขอแนะนำให้คุณดำเนินการตามตัวอย่างข้างต้นในลักษณะทีละขั้นตอนเพื่อความเข้าใจที่ดีขึ้น
ก่อนหน้า Entity Framework 6 Entity Framework ไม่รู้จักเอนทิตีหรือประเภทที่ซับซ้อนที่ซ้อนอยู่ภายในเอนทิตีอื่นหรือประเภทที่ซับซ้อน เมื่อ Entity Framework สร้างแบบจำลองชนิดที่ซ้อนกันจะหายไป
ลองมาดูตัวอย่างง่ายๆที่เรามีโมเดลพื้นฐานที่มีสามเอนทิตีนักศึกษาหลักสูตรและการลงทะเบียน
มาเพิ่มคุณสมบัติ Identity ซึ่งเป็นประเภทบุคคล บุคคลคือเอนทิตีอื่นมีคุณสมบัติ BirthDate และ FatherName
ในเงื่อนไขของ Entity Framework เนื่องจากไม่มีข้อมูลประจำตัวและเป็นส่วนหนึ่งของเอนทิตีเป็นประเภทที่ซับซ้อนของ Entity Framework และเราได้รับการสนับสนุนสำหรับประเภทที่ซับซ้อนตั้งแต่เวอร์ชันแรกของ Entity Framework
ประเภทบุคคลไม่ซ้อนกันดังที่แสดงในรหัสต่อไปนี้
public class Student {
public int ID { get; set; }
public string LastName { get; set; }
public string FirstMidName { get; set; }
public DateTime EnrollmentDate { get; set; }
public Person Identity { get; set; }
public virtual ICollection<Enrollment> Enrollments { get; set; }
}
public class Person {
public Person(string fatherName, DateTime birthDate) {
FatherName = fatherName;
BirthDate = birthDate;
}
public string FatherName { get; set; }
public DateTime BirthDate { get; set; }
}
Entity Framework จะรู้วิธีคงประเภทบุคคลเมื่อใช้ในเวอร์ชันก่อนหน้าเช่นกัน
ด้วยการใช้ Entity Framework Power Tool เราจะดูว่า Entity Framework ตีความโมเดลอย่างไร คลิกขวาที่ไฟล์ Program.cs และเลือก Entity Framework → View Entity Data Model (อ่านอย่างเดียว)
ตอนนี้คุณจะเห็นว่าคุณสมบัติ Identity ถูกกำหนดไว้ในคลาสนักเรียน
หากเอนทิตีอื่นไม่ใช้คลาส Person นี้เราสามารถซ้อนไว้ในคลาส Student ได้ แต่ Entity Framework เวอร์ชันก่อนหน้านี้ไม่ยอมรับประเภทที่ซ้อนกัน
ในเวอร์ชันที่เก่ากว่าคุณจะสร้างแบบจำลองอีกครั้งไม่เพียง แต่จะไม่รู้จักประเภท แต่เนื่องจากไม่มีอยู่จึงไม่มีคุณสมบัติดังกล่าวดังนั้น Entity Framework จะไม่คงอยู่ประเภทบุคคลเลย
public class Student {
public int ID { get; set; }
public string LastName { get; set; }
public string FirstMidName { get; set; }
public DateTime EnrollmentDate { get; set; }
public Person Identity { get; set; }
public virtual ICollection<Enrollment> Enrollments { get; set; }
public class Person {
public Person(string fatherName, DateTime birthDate) {
FatherName = fatherName;
BirthDate = birthDate;
}
public string FatherName { get; set; }
public DateTime BirthDate { get; set; }
}
}
ด้วย Entity Framework 6 เอนทิตีที่ซ้อนกันและประเภทที่ซับซ้อนจะได้รับการยอมรับ ในรหัสด้านบนคุณจะเห็นว่าบุคคลนั้นซ้อนอยู่ในชั้นเรียนของนักเรียน
เมื่อคุณใช้ Entity Framework Power Tool เพื่อแสดงว่า Entity Framework ตีความโมเดลในครั้งนี้อย่างไรมีคุณสมบัติ Identity จริงและประเภทที่ซับซ้อนของบุคคล ดังนั้น Entity Framework จะยังคงมีข้อมูลนั้นอยู่
ตอนนี้คุณจะเห็นว่า Identity เป็นประเภทเอนทิตีที่ซ้อนกันซึ่งไม่ได้รับการสนับสนุนก่อน Entity Framework 6
เราขอแนะนำให้คุณดำเนินการตามตัวอย่างข้างต้นในลักษณะทีละขั้นตอนเพื่อความเข้าใจที่ดีขึ้น