ในระหว่างการสัมภาษณ์งานฉันถูกขอให้อธิบายว่าทำไมรูปแบบที่เก็บไม่ใช่รูปแบบที่ดีในการทำงานกับ ORMs เช่น Entity Framework เหตุใดจึงเป็นเช่นนี้
ในระหว่างการสัมภาษณ์งานฉันถูกขอให้อธิบายว่าทำไมรูปแบบที่เก็บไม่ใช่รูปแบบที่ดีในการทำงานกับ ORMs เช่น Entity Framework เหตุใดจึงเป็นเช่นนี้
คำตอบ:
ฉันไม่เห็นเหตุผลใด ๆ ที่รูปแบบ Repository จะไม่ทำงานกับ Entity Framework รูปแบบพื้นที่เก็บข้อมูลเป็นชั้นนามธรรมที่คุณใส่ในชั้นการเข้าถึงข้อมูลของคุณ ชั้นการเข้าถึงข้อมูลของคุณสามารถเป็นอะไรก็ได้ตั้งแต่ขั้นตอนการเก็บ ADO.NET บริสุทธิ์ไปจนถึง Entity Framework หรือไฟล์ XML
ในระบบขนาดใหญ่ที่คุณมีข้อมูลมาจากแหล่งต่าง ๆ (บริการฐานข้อมูล / XML / เว็บ) จะเป็นการดีที่จะมีเลเยอร์นามธรรม รูปแบบที่เก็บทำงานได้ดีในสถานการณ์นี้ ฉันไม่เชื่อว่า Entity Framework เป็นนามธรรมเพียงพอที่จะซ่อนสิ่งที่เกิดขึ้นเบื้องหลัง
ฉันใช้รูปแบบพื้นที่เก็บข้อมูลกับ Entity Framework เป็นวิธีการเข้าถึงชั้นข้อมูลของฉันและยังพบปัญหา
ประโยชน์จากการสรุปอีกประการหนึ่งDbContext
ที่มีพื้นที่เก็บข้อมูลเป็นหน่วยตรวจสอบได้ คุณสามารถมีIRepository
อินเทอร์เฟซของคุณซึ่งมีการใช้งาน 2 รายการหนึ่งรายการ (พื้นที่เก็บข้อมูลจริง) ซึ่งใช้DbContext
เพื่อพูดคุยกับฐานข้อมูลและที่สองFakeRepository
ซึ่งสามารถส่งคืนออบเจ็กต์ในหน่วยความจำ / ข้อมูลจำลอง นี้จะทำให้คุณIRepository
ชิ้นส่วนอื่น ๆ IRepository
ทำให้หน่วยทดสอบรหัสที่ใช้
public interface IRepository
{
IEnumerable<CustomerDto> GetCustomers();
}
public EFRepository : IRepository
{
private YourDbContext db;
private EFRepository()
{
db = new YourDbContext();
}
public IEnumerable<CustomerDto> GetCustomers()
{
return db.Customers.Select(f=>new CustomerDto { Id=f.Id, Name =f.Name}).ToList();
}
}
public MockRepository : IRepository
{
public IEnumerable<CustomerDto> GetCustomers()
{
// to do : return a mock list of Customers
// Or you may even use a mocking framework like Moq
}
}
ตอนนี้ใช้ DI คุณจะได้รับการติดตั้ง
public class SomeService
{
IRepository repo;
public SomeService(IRepository repo)
{
this.repo = repo;
}
public void SomeMethod()
{
//use this.repo as needed
}
}
เหตุผลเดียวที่ดีที่สุดที่จะไม่ใช้รูปแบบพื้นที่เก็บข้อมูลกับ Entity Framework? Entity Framework แล้วดำเนินการรูปแบบการเก็บข้อมูล DbContext
คือ UoW ของคุณ (หน่วยงาน) และDbSet
ที่เก็บคือ การนำเลเยอร์อื่นไปใช้งานด้านบนนี้ไม่เพียง แต่ซ้ำซ้อนเท่านั้น แต่ยังทำให้การบำรุงรักษาทำได้ยากขึ้น
ผู้คนติดตามรูปแบบโดยไม่ตระหนักถึงวัตถุประสงค์ของรูปแบบ ในกรณีของรูปแบบที่เก็บข้อมูลวัตถุประสงค์เพื่อสรุปตรรกะการสืบค้นฐานข้อมูลระดับล่าง ในสมัยก่อนที่จะเขียนคำสั่ง SQL ในรหัสของคุณรูปแบบที่เก็บเป็นวิธีที่จะย้าย SQL นั้นออกจากวิธีการแต่ละวิธีที่กระจัดกระจายไปทั่วฐานรหัสของคุณและแปลเป็นภาษาท้องถิ่นในที่เดียว การมี ORM เช่น Entity Framework, NHibernate และอื่น ๆ เป็นสิ่งที่ใช้แทนรหัสนี้ได้ดังนั้นจึงไม่จำเป็นต้องใช้รูปแบบดังกล่าว
อย่างไรก็ตามมันไม่ใช่ความคิดที่ดีที่จะสร้างสิ่งที่เป็นนามธรรมด้านบน ORM ของคุณไม่ใช่แค่สิ่งที่ซับซ้อนเท่ากับ UoW / repostitory ฉันจะไปกับรูปแบบการบริการที่คุณสร้าง API ที่แอปพลิเคชันของคุณสามารถใช้โดยไม่ทราบหรือไม่สนใจว่าข้อมูลนั้นมาจาก Entity Framework, NHibernate หรือ Web API สิ่งนี้ง่ายกว่ามากเนื่องจากคุณเพียงเพิ่มวิธีในคลาสบริการของคุณเพื่อส่งคืนข้อมูลที่แอปพลิเคชันของคุณต้องการ หากคุณกำลังเขียนแอปที่ต้องทำตัวอย่างเช่นคุณอาจได้รับบริการโทรกลับรายการที่ถึงกำหนดส่งในสัปดาห์นี้และยังไม่เสร็จสมบูรณ์ แอปทั้งหมดของคุณรู้ว่าถ้ามันต้องการข้อมูลนี้มันจะเรียกวิธีการนั้น ในวิธีการนี้และในบริการของคุณโดยทั่วไปคุณจะต้องโต้ตอบกับ Entity Framework หรืออะไรก็ตามที่คุณใช้ จากนั้นหากคุณตัดสินใจเปลี่ยน ORM ในภายหลังหรือดึงข้อมูลจาก Web API
อาจดูเหมือนว่าเป็นข้อโต้แย้งที่เป็นไปได้สำหรับการใช้รูปแบบพื้นที่เก็บข้อมูล แต่ความแตกต่างที่สำคัญที่นี่คือบริการเป็นชั้นที่บางและมุ่งสู่การส่งคืนข้อมูลที่ได้รับการอบอย่างเต็มที่มากกว่าสิ่งที่คุณทำการสืบค้นต่อ กรุ
DbContext
ใน EF6 + (ดู: msdn.microsoft.com/en-us/data/dn314429.aspx ) แม้แต่ในรุ่นที่น้อยกว่าคุณสามารถใช้DbContext
คลาสที่เหมือนของปลอมกับ mocked DbSet
s ได้เนื่องจากDbSet
ใช้ตัววนIDbSet
ซ้ำ
นี่คือสิ่งหนึ่งที่นำมาจาก Ayende Rahien: สถาปัตย์ในหลุมแห่งความพินาศ: ความชั่วร้ายของเลเยอร์สิ่งที่เป็นนามธรรม
ฉันยังไม่แน่ใจว่าฉันเห็นด้วยกับข้อสรุปของเขาหรือไม่ มันเป็น catch-22 - ในอีกด้านหนึ่งถ้าฉันใส่เนื้อหาบริบทของ EF ลงในที่เก็บชนิดเฉพาะด้วยวิธีการดึงข้อมูลเฉพาะแบบสอบถามฉันจริง ๆ แล้วฉันสามารถทดสอบหน่วยของฉัน (เรียงลำดับ) ซึ่งเกือบเป็นไปไม่ได้กับ Entity กรอบงานเพียงอย่างเดียว ในทางกลับกันฉันสูญเสียความสามารถในการทำแบบสอบถามที่หลากหลายและการบำรุงรักษาความสัมพันธ์แบบ semantic (แม้ว่าฉันจะสามารถเข้าถึงคุณลักษณะเหล่านั้นได้อย่างเต็มที่ฉันมักจะรู้สึกว่าฉันกำลังเดินอยู่บนเปลือกไข่รอบ ๆ EF หรือ ORM อื่น ๆ เนื่องจากฉันไม่เคยรู้ว่าวิธีการใช้งานแบบ IQueryable นั้นอาจจะสนับสนุนหรือไม่ก็ตามไม่ว่ามันจะตีความการเพิ่มของฉันไปยังคอลเลกชันคุณสมบัติการนำทางเป็นการสร้างหรือเพียงแค่การเชื่อมโยงไม่ว่ามันจะขี้เกียจหรือกระตือรือร้นหรือไม่โหลดเลย ค่าเริ่มต้น ฯลฯ ดังนั้นนี่อาจเป็นการดีกว่า "การแมป" ที่ไม่มีอิมพีแดนซ์เป็นวัตถุเชิงวัตถุเป็นสิ่งมีชีวิตในตำนาน - นั่นอาจเป็นสาเหตุว่าทำไม Entity Framework รุ่นล่าสุดจึงมีชื่อรหัสว่า "Magic Unicorn")
อย่างไรก็ตามการดึงเอนทิตีของคุณผ่านวิธีการดึงข้อมูลเฉพาะแบบสอบถามหมายความว่าการทดสอบหน่วยของคุณตอนนี้เป็นการทดสอบกล่องสีขาวเป็นหลักและคุณไม่มีทางเลือกในเรื่องนี้เนื่องจากคุณต้องรู้ล่วงหน้าว่าวิธีการเก็บข้อมูลใด โทรเพื่อเยาะเย้ยมัน และคุณยังไม่ได้ทำการทดสอบคิวรี่เองนอกจากคุณจะเขียนการทดสอบการรวม
สิ่งเหล่านี้เป็นปัญหาที่ซับซ้อนซึ่งต้องการวิธีแก้ปัญหาที่ซับซ้อน คุณไม่สามารถแก้ไขได้โดยเพียงแค่แสร้งทำเป็นว่าเอนทิตีของคุณทั้งหมดเป็นประเภทที่แยกกันโดยไม่มีความสัมพันธ์ระหว่างพวกเขาและทำให้เป็นอะตอมในแต่ละที่เก็บของตัวเอง คุณทำได้ดีแต่มันแย่มาก
อัปเดต:ฉันประสบความสำเร็จในการใช้ผู้ให้บริการความพยายามสำหรับ Entity Framework ความพยายามเป็นผู้ให้บริการในหน่วยความจำ (โอเพ่นซอร์ส) ที่ให้คุณใช้ EF ในการทดสอบอย่างที่คุณจะใช้กับฐานข้อมูลจริง ฉันกำลังพิจารณาเปลี่ยนการทดสอบทั้งหมดในโครงการนี้ฉันกำลังทำงานเพื่อใช้ผู้ให้บริการนี้เนื่องจากดูเหมือนว่าจะทำให้สิ่งต่าง ๆ ง่ายขึ้นมาก มันเป็นทางออกเดียวที่ฉันค้นพบจนถึงตอนนี้ที่จัดการกับปัญหาทั้งหมดที่ฉันคุยโวเกี่ยวกับเรื่องก่อนหน้านี้ สิ่งเดียวที่มีความล่าช้าเล็กน้อยเมื่อเริ่มต้นการทดสอบของฉันเนื่องจากกำลังสร้างฐานข้อมูลในหน่วยความจำ (ใช้แพ็คเกจอื่นที่เรียกว่า NMemory เพื่อทำสิ่งนี้) แต่ฉันไม่เห็นว่านี่เป็นปัญหาจริง มีบทความของCode Projectที่พูดถึงการใช้ความพยายาม (กับ SQL CE) สำหรับการทดสอบ
DbContext
ได้ ไม่ว่าคุณจะล้อเลียนอยู่ตลอดDbSet
และนั่นก็เป็นส่วนสำคัญของ Entity Framework อยู่แล้ว DbContext
น้อยกว่าชั้นเรียนที่จะDbSet
เก็บคุณสมบัติของคุณ(ที่เก็บข้อมูล) ไว้ในที่เดียว (หน่วยของงาน) โดยเฉพาะอย่างยิ่งในบริบทการทดสอบหน่วยที่การเตรียมใช้งานฐานข้อมูลและการเชื่อมต่อทั้งหมดนั้นไม่ต้องการหรือจำเป็น
เหตุผลที่คุณอาจจะทำเช่นนั้นก็เพราะซ้ำซ้อนเล็กน้อย Entity Framework ช่วยให้คุณได้รับรหัสและประโยชน์จากการใช้งานมากมายนั่นเป็นเหตุผลว่าทำไมคุณถึงใช้มันถ้าคุณใช้มันและห่อมันในรูปแบบพื้นที่เก็บข้อมูลที่คุณทิ้งข้อดีเหล่านั้นออกไปคุณอาจใช้เลเยอร์การเข้าถึงข้อมูลอื่น ๆ
ในทางทฤษฎีแล้วฉันคิดว่ามันสมเหตุสมผลที่จะสรุปเหตุผลในการเชื่อมต่อฐานข้อมูลเพื่อให้สามารถนำกลับมาใช้ใหม่ได้ง่ายขึ้น แต่เมื่อลิงก์ด้านล่างระบุว่ากรอบงานที่ทันสมัยของเราจะดูแลเรื่องนี้เป็นหลัก
ISessionFactory
และISession
เยาะเย้ยได้อย่างง่ายดาย) มันไม่ง่ายอย่างนั้นด้วยDbContext
โชคไม่ดี ...
เหตุผลที่ดีมากในการใช้รูปแบบพื้นที่เก็บข้อมูลคือการอนุญาตให้แยกตรรกะทางธุรกิจและ / หรือ UI ของคุณจาก System.Data.Entity มีข้อดีหลายข้อในเรื่องนี้รวมถึงประโยชน์ที่แท้จริงในการทดสอบหน่วยโดยอนุญาตให้เขาใช้ Fakes หรือ Mocks
เรามีปัญหากับอินสแตนซ์ DbContext Entity Framework ที่ซ้ำกัน แต่แตกต่างกันเมื่อคอนเทนเนอร์ IoC ที่ใหม่ () ขึ้นที่เก็บต่อประเภท (ตัวอย่างเช่น UserRepository และอินสแตนซ์ GroupRepository ที่แต่ละครั้งเรียก IDbSet ของตนเองจาก DBContext) บางครั้งอาจทำให้บริบทหลายรายการต่อคำขอ (ในบริบทของ MVC / เว็บ)
เวลาส่วนใหญ่ยังคงใช้งานได้ แต่เมื่อคุณเพิ่มชั้นบริการด้านบนและบริการเหล่านั้นถือว่าวัตถุที่สร้างขึ้นด้วยบริบทหนึ่งจะถูกแนบเป็นคอลเลกชันของเด็กไปยังวัตถุใหม่ในบริบทอื่นบางครั้งก็ล้มเหลวและบางครั้งก็ไม่ ขึ้นอยู่กับความเร็วของการส่งมอบ
หลังจากลองใช้รูปแบบพื้นที่เก็บข้อมูลในโครงการขนาดเล็กฉันขอแนะนำไม่ให้ใช้มัน ไม่ใช่เพราะมันทำให้ระบบของคุณซับซ้อนและไม่ใช่เพราะการเยาะเย้ยข้อมูลเป็นฝันร้าย แต่เพราะการทดสอบของคุณไร้ประโยชน์ !!
ข้อมูลการเยาะเย้ยช่วยให้คุณเพิ่มรายละเอียดโดยไม่มีส่วนหัวเพิ่มระเบียนที่ละเมิดข้อ จำกัด ของฐานข้อมูลและลบเอนทิตีที่ฐานข้อมูลจะปฏิเสธที่จะลบ ในโลกแห่งความเป็นจริงการอัปเดตครั้งเดียวอาจส่งผลต่อหลายตารางบันทึกประวัติสรุป ฯลฯ รวมถึงคอลัมน์เช่นฟิลด์วันที่แก้ไขครั้งล่าสุดคีย์ที่สร้างโดยอัตโนมัติฟิลด์ที่คำนวณ
ในระยะสั้นการทดสอบของคุณกับฐานข้อมูลจริงให้ผลลัพธ์ที่แท้จริงและคุณสามารถทดสอบไม่เพียง แต่บริการและส่วนต่อประสาน แต่ยังรวมถึงพฤติกรรมของฐานข้อมูลด้วย คุณสามารถตรวจสอบว่าขั้นตอนการจัดเก็บของคุณทำสิ่งที่ถูกต้องด้วยข้อมูลส่งคืนผลลัพธ์ที่คาดหวังหรือบันทึกที่คุณส่งเพื่อลบจริง ๆ หรือไม่ การทดสอบดังกล่าวอาจทำให้เกิดปัญหาเช่นการลืมที่จะเพิ่มข้อผิดพลาดจากกระบวนงานที่เก็บไว้และสถานการณ์จำลองหลายพันรายการ
ฉันคิดว่าเฟรมเวิร์กเอนทิตีใช้รูปแบบที่เก็บได้ดีกว่าบทความใด ๆ ที่ฉันได้อ่านมาและมันไปไกลเกินกว่าที่พวกเขาพยายามจะทำ
พื้นที่เก็บข้อมูลเป็นแนวปฏิบัติที่ดีที่สุดในสมัยนั้นที่เราใช้ XBase, AdoX และ Ado.Net แต่ใช้เอนทิตี้ !! (พื้นที่เก็บข้อมูลบนพื้นที่เก็บข้อมูล)
สุดท้ายฉันคิดว่าคนจำนวนมากเกินไปใช้เวลามากในการเรียนรู้และใช้รูปแบบพื้นที่เก็บข้อมูลและพวกเขาปฏิเสธที่จะปล่อยมันไป ส่วนใหญ่เพื่อพิสูจน์ตัวเองว่าพวกเขาไม่เสียเวลา
มันเกิดจากการโยกย้าย: มันเป็นไปไม่ได้ที่จะได้รับการโยกย้ายในการทำงานเพราะสตริงการเชื่อมต่ออยู่ใน web.config แต่ DbContext อยู่ในเลเยอร์พื้นที่เก็บข้อมูล IDbContextFactory จำเป็นต้องมีสตริงการกำหนดค่าไปยังฐานข้อมูล แต่ไม่มีวิธีใดที่การย้ายข้อมูลจะได้รับสตริงการเชื่อมต่อจาก web.config
มีวิธีแก้ไขอยู่บ้าง แต่ฉันยังไม่พบวิธีแก้ปัญหาที่ดีสำหรับเรื่องนี้!