ไม่ใช้รูปแบบพื้นที่เก็บข้อมูลให้ใช้ ORM ตามที่เป็น (EF)


96

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

ตอนนี้ทั้งหมดนี้เริ่มต้นด้วยความคิดเห็นสองสามรายการใน Stackoverflow พร้อมติดตามโพสต์ของ Ayende Rahien ในบล็อกของเขาโดยมี 2 ข้อความที่เจาะจง

สิ่งนี้อาจถูกพูดถึงตลอดไปและตลอดไปและขึ้นอยู่กับการใช้งานที่แตกต่างกัน ฉันอยากรู้อะไร

  1. แนวทางนี้จะเหมาะกับโครงการ Entity Framework หรือไม่
  2. การใช้วิธีนี้เป็นตรรกะทางธุรกิจที่ยังคงอยู่ในชั้นบริการหรือวิธีการขยาย (ตามที่อธิบายไว้ด้านล่างฉันรู้วิธีการขยายกำลังใช้เซสชัน NHib)

ทำได้อย่างง่ายดายโดยใช้วิธีการขยาย สะอาดเรียบง่ายและใช้ซ้ำได้

public static IEnumerable GetAll(
    this ISession instance, Expression<Func<T, bool>> where) where T : class
{
    return instance.QueryOver().Where(where).List();
}

การใช้แนวทางนี้และNinjectในฐานะ DI ฉันจำเป็นต้องสร้างContextอินเทอร์เฟซและฉีดเข้าไปในคอนโทรลเลอร์ของฉันหรือไม่

คำตอบ:


105

ฉันได้ไปหลายเส้นทางและสร้างการใช้งานที่เก็บข้อมูลมากมายในโครงการต่างๆและ ... ฉันโยนผ้าขนหนูและยอมแพ้ต่อไปนี่คือเหตุผล

การเข้ารหัสสำหรับข้อยกเว้น

คุณเขียนโค้ดสำหรับโอกาส 1% ที่ฐานข้อมูลของคุณจะเปลี่ยนจากเทคโนโลยีหนึ่งไปเป็นอีกเทคโนโลยีหนึ่งหรือไม่? หากคุณกำลังคิดถึงสถานะในอนาคตของธุรกิจของคุณและตอบว่าใช่นั่นเป็นไปได้ a) พวกเขาต้องมีเงินจำนวนมากเพื่อที่จะย้ายไปใช้เทคโนโลยีฐานข้อมูลอื่นหรือ b) คุณกำลังเลือกเทคโนโลยี DB เพื่อความสนุกสนานหรือ c ) มีบางอย่างผิดปกติอย่างมากกับเทคโนโลยีแรกที่คุณตัดสินใจใช้

ทำไมต้องทิ้งไวยากรณ์ LINQ ที่สมบูรณ์

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

ฉันไม่ต้องการสร้างเมธอดสำหรับทุกการเรียงสับเปลี่ยนของคิวรีที่ฉันต้องเขียน ฉันอาจเขียนกระบวนงานที่เก็บไว้ด้วยเช่นกัน ฉันไม่ต้องการGetOrder, GetOrderWithOrderItem, GetOrderWithOrderItemWithOrderActivity, GetOrderByUserIdและอื่น ๆ ... ฉันเพียงแค่ต้องการที่จะได้รับนิติบุคคลหลักและการสำรวจและรวมถึงกราฟวัตถุที่ผมดังนั้นโปรด

ตัวอย่างส่วนใหญ่ของที่เก็บเป็นเรื่องไร้สาระ

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

อย่าทดสอบหน่วยฉันเลย

แต่การทดสอบหน่วยจะเป็นอย่างไรถ้าฉันไม่มีที่เก็บ? จะล้อเลียนยังไง ง่ายๆคุณทำไม่ได้ ให้มองจากทั้งสองมุม:

ไม่มีที่เก็บ - คุณสามารถเยาะเย้ยการDbContextใช้IDbContextหรือกลเม็ดอื่น ๆ แต่จากนั้นคุณกำลังทดสอบหน่วยLINQ เป็น Objectsไม่ใช่LINQ to Entitiesเนื่องจากแบบสอบถามถูกกำหนดที่รันไทม์ ... ตกลงดังนั้นจึงไม่ดี! ตอนนี้จึงขึ้นอยู่กับการทดสอบการรวมเพื่อให้ครอบคลุมสิ่งนี้

ด้วยที่เก็บ - ตอนนี้คุณสามารถจำลองที่เก็บของคุณและหน่วยทดสอบเลเยอร์ที่อยู่ระหว่างนั้นได้ ดีใช่มั้ย? ไม่จริง ... ในกรณีข้างต้นที่คุณต้องรั่วไหลลอจิกลงในเลเยอร์พื้นที่เก็บข้อมูลเพื่อให้การสืบค้นมีประสิทธิภาพมากขึ้นและ / หรือเข้าสู่ฐานข้อมูลน้อยลงการทดสอบหน่วยของคุณจะครอบคลุมสิ่งนั้นได้อย่างไร? ตอนนี้อยู่ในเลเยอร์ repo แล้วคุณไม่ต้องการทดสอบIQueryable<T>ใช่ไหม ตามจริงแล้วการทดสอบหน่วยของคุณจะไม่ครอบคลุมคำถามที่มี.Where()ประโยค20 บรรทัดและ.Include()เป็นกลุ่มของความสัมพันธ์และเข้าชมฐานข้อมูลอีกครั้งเพื่อทำสิ่งอื่น ๆ ทั้งหมดนี้บลาบลาบลา ๆ ๆ ๆ ๆ เพราะการสืบค้นถูกสร้างขึ้นที่รันไทม์ นอกจากนี้เนื่องจากคุณสร้างที่เก็บเพื่อไม่ให้เลเยอร์ด้านบนคงอยู่โดยไม่รู้ตัวหากตอนนี้คุณต้องการเปลี่ยนเทคโนโลยีฐานข้อมูลขออภัยการทดสอบหน่วยของคุณจะไม่รับประกันผลลัพธ์เดียวกันในรันไทม์กลับไปที่การทดสอบการรวม ดังนั้นจุดรวมของพื้นที่เก็บข้อมูลจึงดูแปลก ..

2 เซนต์

เราสูญเสียฟังก์ชันและไวยากรณ์จำนวนมากไปแล้วเมื่อใช้ EF ผ่านขั้นตอนที่จัดเก็บแบบธรรมดา (การแทรกจำนวนมากการลบจำนวนมาก CTEs ฯลฯ ) แต่ฉันยังเขียนโค้ดใน C # ดังนั้นฉันจึงไม่ต้องพิมพ์ไบนารี เราใช้ EF เพื่อให้เรามีความเป็นไปได้ในการใช้ผู้ให้บริการที่แตกต่างกันและทำงานกับกราฟออบเจ็กต์ด้วยวิธีที่ดีในหลาย ๆ สิ่ง นามธรรมบางอย่างมีประโยชน์และบางส่วนไม่มีประโยชน์


18
คุณไม่ได้สร้างที่เก็บเพื่อให้สามารถทดสอบหน่วยได้ คุณสร้างที่เก็บเพื่อให้หน่วยทดสอบตรรกะทางธุรกิจได้ สำหรับการตรวจสอบให้แน่ใจว่าคิวรีทำงานได้: การเขียนการทดสอบการรวมสำหรับที่เก็บนั้นง่ายกว่ามากเนื่องจากมีเฉพาะตรรกะและไม่ใช่ธุรกิจใด ๆ
jgauffin

16
Coding for the exception: การใช้ที่เก็บไม่สามารถเปลี่ยนกลไกจัดการฐานข้อมูลได้ เป็นเรื่องของการแยกธุรกิจออกจากความคงอยู่
jgauffin

2
สิ่งเหล่านี้ล้วนเป็นประเด็นที่ถูกต้องและมีความจริงเบื้องหลัง สิ่งที่ขาดหายไปคือการตระหนักว่า LINQ เกลือกกลั้วกับแอปพลิเคชันแทนที่จะถูก จำกัด ให้อยู่ในตำแหน่งที่สอดคล้องกันจะสร้าง EF เทียบเท่ากับการเรียก SQL ในหน้า codebehind แบบสอบถาม LINQ ทุกรายการเป็นจุดบำรุงรักษาที่เป็นไปได้ในแอปพลิเคชันและยิ่งมี (และแพร่หลายมากขึ้น) ค่าบำรุงรักษาและความเสี่ยงก็จะสูงขึ้น ลองนึกภาพการเพิ่มแฟล็ก 'ลบ' ลงในเอนทิตีและต้องค้นหาทุกที่ในแอปพลิเคชันขนาดใหญ่ที่เอนทิตีถูกสืบค้นโดยต้องแก้ไขแต่ละรายการ ...
DVK

2
ฉันคิดว่านี่คือสายตาสั้นและน่าเบื่อ ทำไมคุณถึงรั่วไหลตรรกะไปสู่ ​​repo? และถ้าคุณทำทำไมมันถึงสำคัญ? เป็นการใช้ข้อมูล สิ่งที่เราทำคือการปิด LINQ จากส่วนที่เหลือของโค้ดโดยซ่อนไว้ด้านหลัง repo คุณบอกว่าอย่าทดสอบ แต่คุณใช้การไม่สามารถทดสอบเป็นข้อโต้แย้งกับการทำ ดังนั้นสร้าง repo อย่าเปิดเผย IQueryable และอย่าทดสอบ อย่างน้อยคุณสามารถทดสอบอย่างอื่นโดยแยกจากการนำข้อมูลไปใช้ และโอกาสที่ 1% ของการเปลี่ยนแปลงฐานข้อมูลนั้นยังคงมีมูลค่ามหาศาลถึงหนึ่งดอลลาร์
Sinaesthetic

5
+1 สำหรับคำตอบนี้ ฉันพบว่าเราไม่ต้องการที่เก็บที่มี Entity Framework Core จริงๆ DbSetเป็นพื้นที่เก็บข้อมูลและDbContextเป็นหน่วยงาน ทำไมต้องใช้รูปแบบพื้นที่เก็บข้อมูลในเมื่อ ORM ทำเพื่อเราแล้ว! InMemoryสำหรับการทดสอบเพียงแค่เปลี่ยนผู้ให้บริการ และทำการทดสอบของคุณ! มีการบันทึกไว้อย่างดีใน MSDN
Mohammed Noureldin

49

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

ปัญหาคือนักพัฒนาจำนวนมากไม่เข้าใจจุดประสงค์ของรูปแบบและสร้างที่เก็บข้อมูลที่รั่วไหลต่อเนื่องข้อมูลเฉพาะไปยังผู้โทร (โดยทั่วไปจะเปิดเผยIQueryable<T>) การทำเช่นนี้จะไม่ได้รับประโยชน์ใด ๆ จากการใช้ OR / M โดยตรง

อัปเดตเพื่อตอบคำตอบอื่น

การเข้ารหัสสำหรับข้อยกเว้น

การใช้ที่เก็บไม่ได้เกี่ยวกับความสามารถในการเปลี่ยนเทคโนโลยีการคงอยู่ (เช่นการเปลี่ยนฐานข้อมูลหรือใช้บริการเว็บ ฯลฯ แทน) เป็นการแยกตรรกะทางธุรกิจออกจากความคงอยู่เพื่อลดความซับซ้อนและการมีเพศสัมพันธ์

การทดสอบหน่วยเทียบกับการทดสอบการรวม

คุณไม่ได้เขียนการทดสอบหน่วยสำหรับที่เก็บ งวด.

แต่ด้วยการแนะนำที่เก็บ (หรือชั้นนามธรรมอื่น ๆ ระหว่างความคงอยู่และธุรกิจ) คุณจะสามารถเขียนการทดสอบหน่วยสำหรับตรรกะทางธุรกิจได้ กล่าวคือคุณไม่ต้องกังวลว่าการทดสอบของคุณจะล้มเหลวเนื่องจากฐานข้อมูลที่กำหนดค่าไม่ถูกต้อง

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

ความแตกต่างก็คือหากคุณไม่ได้ผสมผสานธุรกิจของคุณกับคำสั่ง LINQ คุณสามารถมั่นใจได้ 100% ว่านั่นคือรหัสการคงอยู่ของคุณที่ล้มเหลวและไม่ใช่อย่างอื่น

หากคุณวิเคราะห์การทดสอบของคุณคุณจะเห็นว่าการทดสอบนั้นสะอาดกว่ามากหากคุณไม่ได้มีความกังวลหลายอย่าง (เช่น LINQ + Business logic)

ตัวอย่างที่เก็บ

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

การสร้างการใช้งานที่เก็บที่ถูกต้องนั้นง่ายมาก ในความเป็นจริงคุณต้องปฏิบัติตามกฎข้อเดียวเท่านั้น:

อย่าเพิ่มสิ่งใด ๆ ลงในคลาสที่เก็บจนกว่าจะถึงเวลาที่คุณต้องการ

ผู้เขียนโค้ดจำนวนมากขี้เกียจและพยายามสร้างที่เก็บข้อมูลทั่วไปและใช้คลาสฐานที่มีวิธีการมากมายที่พวกเขาอาจต้องการ YAGNI คุณเขียนคลาสที่เก็บหนึ่งครั้งและเก็บไว้ตราบเท่าที่แอปพลิเคชันมีอายุการใช้งาน (อาจเป็นปี) ทำไมต้องมีเพศสัมพันธ์โดยขี้เกียจ รักษาความสะอาดโดยไม่มีการสืบทอดคลาสพื้นฐาน จะช่วยให้อ่านและดูแลรักษาง่ายขึ้นมาก

(ข้อความข้างต้นเป็นแนวทางและไม่ใช่กฎหมายชั้นฐานสามารถกระตุ้นได้ดีเพียงแค่คิดก่อนเพิ่มเพื่อที่คุณจะเพิ่มด้วยเหตุผลที่ถูกต้อง)

ของเก่า

สรุป:

หากคุณไม่ทราบว่ามีคำสั่ง LINQ ในรหัสธุรกิจของคุณหรือไม่สนใจเกี่ยวกับการทดสอบหน่วยฉันไม่เห็นเหตุผลที่จะไม่ใช้ Entity Framework โดยตรง

อัปเดต

ฉันเขียนบล็อกเกี่ยวกับรูปแบบพื้นที่เก็บข้อมูลและความหมายที่แท้จริงของ "นามธรรม": http://blog.gauffin.org/2013/01/repository-pattern-done-right/

อัปเดต 2

สำหรับประเภทเอนทิตีเดียวที่มีฟิลด์มากกว่า 20 ฟิลด์คุณจะออกแบบวิธีการสืบค้นอย่างไรเพื่อรองรับชุดค่าผสมการเรียงสับเปลี่ยน คุณไม่ต้องการ จำกัด การค้นหาด้วยชื่อเท่านั้นสิ่งที่เกี่ยวกับการค้นหาด้วยคุณสมบัติการนำทางแสดงรายการคำสั่งซื้อทั้งหมดพร้อมรายการที่มีรหัสราคาเฉพาะการค้นหาคุณสมบัติการนำทาง 3 ระดับ เหตุผลทั้งหมดIQueryableถูกคิดค้นขึ้นมาเพื่อให้สามารถสร้างชุดค่าผสมของการค้นหากับฐานข้อมูลได้ ทุกอย่างดูดีในทางทฤษฎี แต่ความต้องการของผู้ใช้เหนือกว่าทฤษฎี

อีกครั้ง: เอนทิตีที่มีฟิลด์มากกว่า 20 ฟิลด์ถูกจำลองแบบไม่ถูกต้อง เป็นหน่วยงานของพระเจ้า ทำลายมันลง.

ฉันไม่ได้โต้เถียงที่IQueryableไม่ได้ถูกสร้างขึ้นเพื่อสอบถาม ฉันกำลังบอกว่ามันไม่ถูกต้องสำหรับเลเยอร์ที่เป็นนามธรรมเช่นรูปแบบพื้นที่เก็บข้อมูลเนื่องจากมันรั่ว ไม่มีผู้ให้บริการ LINQ To Sql ที่สมบูรณ์ 100% (เช่น EF)

พวกเขาทั้งหมดมีสิ่งที่เฉพาะเจาะจงในการใช้งานเช่นวิธีใช้การโหลดแบบกระตือรือร้น / ขี้เกียจหรือวิธีการทำคำสั่ง SQL "IN" การเปิดเผยIQueryableในที่เก็บจะบังคับให้ผู้ใช้ทราบสิ่งเหล่านั้นทั้งหมด ดังนั้นความพยายามทั้งหมดในการแยกแหล่งข้อมูลออกไปจึงเป็นความล้มเหลวโดยสิ้นเชิง คุณเพียงแค่เพิ่มความซับซ้อนโดยไม่ได้รับประโยชน์ใด ๆ จากการใช้ OR / M โดยตรง

ใช้รูปแบบ Repository อย่างถูกต้องหรือไม่ใช้เลย

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


6
การไม่เปิดเผย IQueryable นำไปสู่การค้นหาที่ จำกัด และผู้คนก็สร้างเมธอด Get เพิ่มเติมสำหรับแบบสอบถามประเภทต่างๆและในที่สุดก็ทำให้ที่เก็บซับซ้อนมากขึ้น
Akash Kava

3
คุณยังไม่ได้แก้ไขปัญหาหลักเลย: การเปิดเผย IQueryable ผ่านที่เก็บไม่ใช่สิ่งที่เป็นนามธรรมที่สมบูรณ์
jgauffin

1
การมีออบเจ็กต์แบบสอบถามที่มีโครงสร้างพื้นฐานที่จำเป็นทั้งหมดในการดำเนินการและของตัวมันเองเป็นวิธีที่จะไป imo คุณให้ฟิลด์ซึ่งเป็นคำค้นหาและส่งกลับรายการผลลัพธ์ ภายใน QO คุณสามารถทำอะไรก็ได้ที่คุณต้องการ และเป็นอินเทอร์เฟซที่ทดสอบได้อย่างง่ายดาย ดูโพสต์ของฉันด้านบน มันเป็นสิ่งที่ดีที่สุด
h.alex

2
โดยส่วนตัวแล้วฉันคิดว่ามันสมเหตุสมผลเช่นกันที่จะใช้อินเทอร์เฟซ IQueryable <T> บนคลาส Repository แทนที่จะเปิดเผยชุดพื้นฐานในสมาชิกคนใดคนหนึ่ง
dark_perfect

3
@yat: หนึ่ง repos ต่อรากรวม แต่ IMHO ก็ไม่รากรวมและรวมของตารางแต่เพียงรากรวมและมวล หน่วยเก็บข้อมูลจริงอาจใช้เพียงตารางเดียวหรือหลายตารางกล่าวคืออาจไม่ใช่การแมปแบบหนึ่งเดียวระหว่างการรวมและตาราง ฉันใช้ที่เก็บเพื่อลดความซับซ้อนและลบการอ้างอิงใด ๆ ของหน่วยเก็บข้อมูลพื้นฐาน
jgauffin

29

IMO ทั้งRepositoryนามธรรมและสิ่งที่UnitOfWorkเป็นนามธรรมมีสถานที่ที่มีคุณค่ามากในการพัฒนาที่มีความหมาย ผู้คนจะโต้แย้งเกี่ยวกับรายละเอียดการใช้งาน แต่เช่นเดียวกับที่มีหลายวิธีในการสกินแมวมีหลายวิธีในการใช้นามธรรม

คำถามของคุณคือใช้หรือไม่ใช้และทำไม

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

คุณสามารถล้อเลียนปลอมหรืออะไรก็ได้ด้วยไลบรารีภายนอกโดยเพิ่มเลเยอร์ของการอ้างอิงโค้ด(ที่คุณไม่ได้ควบคุม)ระหว่างตรรกะที่ทำการทดสอบและตรรกะที่กำลังทดสอบ

ประเด็นเล็กน้อยก็คือการมีนามธรรมของคุณเองUnitOfWorkและRepositoryช่วยให้คุณควบคุมและยืดหยุ่นได้สูงสุดเมื่อจำลองการทดสอบหน่วยของคุณ

ทั้งหมดเป็นอย่างดี แต่สำหรับฉันพลังที่แท้จริงของแนวคิดเหล่านี้เป็นสิ่งที่พวกเขาให้เป็นวิธีที่ง่ายที่จะใช้มุมมองเชิงเทคนิคการเขียนโปรแกรมและเป็นไปตามหลักการ SOLID

ดังนั้นคุณมีIRepository:

public interface IRepository<T>
    where T : class
{
    T Add(T entity);
    void Delete(T entity);
    IQueryable<T> AsQueryable();
}

และการนำไปใช้:

public class Repository<T> : IRepository<T>
    where T : class
{
    private readonly IDbSet<T> _dbSet;
    public Repository(PPContext context) 
    {
        _dbSet = context.Set<T>();
    }

    public T Add(T entity)
    { 
        return _dbSet.Add(entity); 
    }

    public void Delete(T entity)
    {
        _dbSet.Remove(entity); 
    }

    public IQueryable<T> AsQueryable() 
    {
        return _dbSet.AsQueryable();
    }
}

ไม่มีอะไรออกจากสามัญเพื่อให้ห่างไกล แต่ตอนนี้เราต้องการที่จะเพิ่มเข้าสู่ระบบบาง - ง่ายกับการเข้าสู่ระบบมัณฑนากร

public class RepositoryLoggerDecorator<T> : IRepository<T>
    where T : class
{
    Logger logger = LogManager.GetCurrentClassLogger();
    private readonly IRepository<T> _decorated;
    public RepositoryLoggerDecorator(IRepository<T> decorated)
    {
        _decorated = decorated;
    }

    public T Add(T entity)
    {
        logger.Log(LogLevel.Debug, () => DateTime.Now.ToLongTimeString() );
        T added = _decorated.Add(entity);
        logger.Log(LogLevel.Debug, () => DateTime.Now.ToLongTimeString());
        return added;
    }

    public void Delete(T entity)
    {
        logger.Log(LogLevel.Debug, () => DateTime.Now.ToLongTimeString());
        _decorated.Delete(entity);
        logger.Log(LogLevel.Debug, () => DateTime.Now.ToLongTimeString());
    }

    public IQueryable<T> AsQueryable()
    {
        return _decorated.AsQueryable();
    }
}

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

หลายครั้งที่ฉันเห็นคำถามนี้ใน StackOverflow -“ คุณจะทำให้ Entity Framework ทำงานในสภาพแวดล้อมแบบผู้เช่าหลายคนได้อย่างไร”

https://stackoverflow.com/search?q=%5Bentity-framework%5D+multi+tenant

หากคุณมีRepositoryนามธรรมคำตอบก็คือ“ เพิ่มมัณฑนากรได้ง่ายๆ”

public class RepositoryTennantFilterDecorator<T> : IRepository<T>
    where T : class
{
    //public for Unit Test example
    public readonly IRepository<T> _decorated;
    public RepositoryTennantFilterDecorator(IRepository<T> decorated)
    {
        _decorated = decorated;
    }

    public T Add(T entity)
    {
        return _decorated.Add(entity);
    }

    public void Delete(T entity)
    {
        _decorated.Delete(entity);
    }

    public IQueryable<T> AsQueryable()
    {
        return _decorated.AsQueryable().Where(o => true);
    }
}

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

คำตอบที่มักจะนึกขึ้นได้เมื่อมีคนพูดว่า "ทำไมฉันจึงควรมีนามธรรม (เช่นRepository) บนสิ่งนี้หรือห้องสมุดของบุคคลที่สาม" คือ "ทำไมคุณไม่ทำล่ะ"

PS ตกแต่งที่เรียบง่ายมากที่จะนำไปใช้โดยใช้ภาชนะ IOC เช่นSimpleInjector

[TestFixture]
public class IRepositoryTesting
{
    [Test]
    public void IRepository_ContainerRegisteredWithTwoDecorators_ReturnsDecoratedRepository()
    {
        Container container = new Container();
        container.RegisterLifetimeScope<PPContext>();
        container.RegisterOpenGeneric(
            typeof(IRepository<>), 
            typeof(Repository<>));
        container.RegisterDecorator(
            typeof(IRepository<>), 
            typeof(RepositoryLoggerDecorator<>));
        container.RegisterDecorator(
            typeof(IRepository<>), 
            typeof(RepositoryTennantFilterDecorator<>));
        container.Verify();

        using (container.BeginLifetimeScope())
        {
            var result = container.GetInstance<IRepository<Image>>();

            Assert.That(
                result, 
                Is.InstanceOf(typeof(RepositoryTennantFilterDecorator<Image>)));
            Assert.That(
                (result as RepositoryTennantFilterDecorator<Image>)._decorated,
                Is.InstanceOf(typeof(RepositoryLoggerDecorator<Image>)));
        }
    }
}

ที่น่าสนใจคือคำตอบนี้ยังคงมีความเกี่ยวข้องในปี 2020 ไม่มีการเปลี่ยนแปลงมากนักตั้งแต่นั้นมา มีวิธีการไม่มากในการแทนที่ RP
Volkan Güven

11

ก่อนอื่นตามที่คำตอบบางคำแนะนำ EF เองก็เป็นรูปแบบที่เก็บไม่จำเป็นต้องสร้างนามธรรมเพิ่มเติมเพียงเพื่อตั้งชื่อเป็นที่เก็บ

Mockable Repository สำหรับการทดสอบหน่วยเราต้องการหรือไม่?

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

Abstraction ที่ไม่จำเป็น

คุณต้องการสร้างที่เก็บเพื่อที่ในอนาคตคุณจะสามารถแทนที่ EF ด้วย NHbibernate และอื่น ๆ ได้อย่างง่ายดายหรือไม่? ฟังดูเป็นแผนการที่ดี แต่คุ้มค่าจริงหรือ?

Linq ฆ่าการทดสอบหน่วย?

ฉันต้องการดูตัวอย่างว่ามันสามารถฆ่าได้อย่างไร

Dependency Injection, IoC

ว้าวคำเหล่านี้เป็นคำที่ยอดเยี่ยมแน่นอนว่าพวกเขาดูดีในทางทฤษฎี แต่บางครั้งคุณต้องเลือกแลกระหว่างการออกแบบที่ยอดเยี่ยมและโซลูชันที่ยอดเยี่ยม เราใช้ทั้งหมดนั้นและสุดท้ายเราก็ทิ้งขยะทั้งหมดและเลือกแนวทางอื่น ขนาดเทียบกับความเร็ว (ขนาดของรหัสและความเร็วในการพัฒนา) มีความสำคัญอย่างมากในชีวิตจริง ผู้ใช้ต้องการความยืดหยุ่นพวกเขาไม่สนใจว่าโค้ดของคุณจะออกแบบได้ดีในแง่ของ DI หรือ IoC

เว้นแต่คุณกำลังสร้าง Visual Studio

การออกแบบที่ยอดเยี่ยมเหล่านี้เป็นสิ่งจำเป็นหากคุณกำลังสร้างโปรแกรมที่ซับซ้อนเช่น Visual Studio หรือ Eclipse ซึ่งจะได้รับการพัฒนาโดยผู้คนจำนวนมากและจำเป็นต้องปรับแต่งได้สูง รูปแบบการพัฒนาที่ยอดเยี่ยมทั้งหมดเกิดขึ้นหลังจากการพัฒนา IDE เหล่านี้ผ่านไปหลายปีและพวกเขาได้พัฒนาขึ้นในสถานที่ที่รูปแบบการออกแบบที่ยอดเยี่ยมเหล่านี้มีความสำคัญมาก แต่ถ้าคุณกำลังทำบัญชีเงินเดือนบนเว็บแบบธรรมดาหรือแอปธุรกิจแบบธรรมดาจะเป็นการดีกว่าที่คุณจะพัฒนาตามเวลาแทนที่จะใช้เวลาในการสร้างมันสำหรับผู้ใช้หลายล้านคนซึ่งจะใช้งานได้กับผู้ใช้ 100 รายเท่านั้น

ที่เก็บเป็นมุมมองที่กรอง - ISecureRepository

ในอีกด้านหนึ่งที่เก็บควรเป็นมุมมองที่กรอง EF ซึ่งป้องกันการเข้าถึงข้อมูลโดยใช้ฟิลเลอร์ที่จำเป็นตามผู้ใช้ / บทบาทปัจจุบัน

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

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

ข้อจำกัดความรับผิดชอบ: ฉันเป็นผู้เขียน Entity REST SDK

http://entityrestsdk.codeplex.com

จำไว้ข้างต้นเราได้พัฒนา SDK ซึ่งสร้างที่เก็บของมุมมองที่ถูกกรองตาม SecurityContext ซึ่งมีตัวกรองสำหรับการดำเนินการ CRUD และมีกฎเพียงสองประเภทเท่านั้นที่ทำให้การดำเนินการที่ซับซ้อนง่ายขึ้น อันดับแรกคือการเข้าถึงเอนทิตีและอื่น ๆ คือกฎอ่าน / เขียนสำหรับคุณสมบัติ

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

public class DefaultSecurityContext : BaseSecurityContext {

  public static DefaultSecurityContext Instance = new DefaultSecurityContext();

  // UserID for currently logged in User
  public static long UserID{
       get{
             return long.Parse( HttpContext.Current.User.Identity.Name );
       }
  }

  public DefaultSecurityContext(){
  }

  protected override void OnCreate(){

        // User can access his own Account only
        var acc = CreateRules<Account>();

        acc.SetRead( y => x=> x.AccountID == UserID ) ;
        acc.SetWrite( y => x=> x.AccountID == UserID );

        // User can only modify AccountName and EmailAddress fields
        acc.SetProperties( SecurityRules.ReadWrite, 
              x => x.AccountName,
              x => x.EmailAddress);

        // User can read AccountType field
        acc.SetProperties<Account>( SecurityRules.Read, 
              x => x.AccountType);

        // User can access his own Orders only
        var order = CreateRules<Order>();
        order.SetRead( y => x => x.CustomerID == UserID );

        // User can modify Order only if OrderStatus is not complete
        order.SetWrite( y => x => x.CustomerID == UserID 
            && x.OrderStatus != "Complete" );

        // User can only modify OrderNotes and OrderStatus
        order.SetProperties( SecurityRules.ReadWrite, 
              x => x.OrderNotes,
              x => x.OrderStatus );

        // User can not delete orders
        order.SetDelete(order.NotSupportedRule);
  }
}

กฎ LINQ เหล่านี้ได้รับการประเมินเทียบกับฐานข้อมูลในเมธอด SaveChanges สำหรับทุกการดำเนินการและกฎเหล่านี้ทำหน้าที่เป็นไฟร์วอลล์ที่หน้าฐานข้อมูล


3
การทดสอบหน่วยกับฐานข้อมูลหมายความว่าคุณมีข้อกำหนดภายนอกเพิ่มเติมสำหรับการทดสอบของคุณ หาก DB นั้นหยุดทำงานหรือข้อมูลถูกล้างหรือมีอะไรเกิดขึ้นกับ DB นั้นการทดสอบของคุณจะล้มเหลว สิ่งนี้ไม่ต้องการ ที่เก็บที่เปิดเผย IQueryable ใช้เวลาประมาณ 2 นาทีในการตั้งค่า ไม่เสียเวลาที่นี่ ทำไม DI ถึงใช้เวลานาน ทั้งหมดนี้ใช้เวลานาที ฉันจะบอกว่าทั้งหมดนี้ใช้ได้ผลดีสำหรับการทดสอบหน่วยการสืบค้นที่ซับซ้อนของฉันในชั้นบริการของฉัน มันดีมากที่ไม่ต้องการฐานข้อมูลเพื่อเชื่อมต่อ การได้รับกรอบการเยาะเย้ยจาก nuget ใช้เวลาประมาณหนึ่งนาที สิ่งนี้ไม่ใช้เวลาใด ๆ
user441521

@ user441521 Repositories with IQueryable 2 mins to setup? คุณอาศัยอยู่ในโลกใดคำขอของ asp.net ทุกรายการบนไซต์สดของเราจะให้บริการภายในมิลลิวินาที การล้อเลียนและแกล้งทำ ฯลฯ จะเพิ่มความซับซ้อนให้กับโค้ดทำให้เสียเวลาทั้งหมด การทดสอบหน่วยจะไม่มีประโยชน์เมื่อไม่ได้กำหนดหน่วยเป็นหน่วยตรรกะทางธุรกิจ
Akash Kava

7

มีการถกเถียงกันมากมายว่าวิธีใดถูกต้องดังนั้นฉันจึงมองว่าทั้งสองวิธีนั้นเป็นที่ยอมรับดังนั้นฉันจึงใช้วิธีที่ฉันชอบมากที่สุด (ซึ่งไม่มีที่เก็บ UoW)

ใน EF UoW ใช้งานผ่าน DbContext และ DbSets เป็นที่เก็บ

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

ฉันเชื่อว่า Ayende ยังมีโพสต์เกี่ยวกับการสรุปการดำเนินงาน CUD ที่ไม่ดี

ฉันมักจะสร้างอินเทอร์เฟซและมีบริบทที่สืบทอดมาจากมันดังนั้นฉันจึงสามารถใช้คอนเทนเนอร์ IoC สำหรับ DI ได้


แล้ววิธีการขยายมันกว้างขวางแค่ไหน? สมมติว่าฉันต้องได้รับสถานะของเอนทิตีอื่นในส่วนขยายของฉันหรือไม่ นั่นคือความกังวลที่สุดของฉันในตอนนี้ คุณต้องการแสดงตัวอย่างของวิธีการขยายหรือไม่?
Dejan.S

ayende.com/blog/153473/… และayende.com/blog/153569/… . (นี่คือบทวิจารณ์ของสถาปัตยกรรม (Framework?) ที่เรียกว่า s # arp lite ส่วนใหญ่ดี แต่เขาไม่เห็นด้วยกับที่เก็บข้อมูลและนามธรรม CUD)
Josh

ตาม NHibernate คุณไม่มีตัวอย่างการใช้ EF หรือไม่? และอีกครั้งเมื่อฉันต้องการเรียกเอนทิตีอื่นว่าวิธีนี้ทำให้ดีที่สุดในวิธีการขยายแบบคงที่ได้อย่างไร
Dejan.S

3
ทั้งหมดนี้ดีและดีจนกว่าคุณสมบัติของอ็อบเจ็กต์โดเมนของคุณจะต้องได้รับความชุ่มชื้นจากข้อมูลที่ไม่ได้เก็บไว้ในฐานข้อมูลของคุณ หรือคุณต้องเลือกใช้เทคโนโลยีที่มีประสิทธิภาพมากกว่า ORM ที่ป่องของคุณ OOPS! ORM ไม่ได้เป็นเพียงการแทนที่ที่เก็บข้อมูล แต่เป็นรายละเอียดการนำไปใช้งาน
cdaq

2

สิ่งที่ใช้กับ EF มากที่สุดไม่ใช่รูปแบบพื้นที่เก็บข้อมูล เป็นรูปแบบ Facade (การแยกวิธีการเรียกใช้ EF ให้เป็นเวอร์ชันที่ง่ายและใช้งานง่ายกว่า)

EF เป็นรูปแบบที่ใช้ Repository Pattern (และรูปแบบ Unit of Work ด้วย) นั่นคือ EF เป็นตัวแยกชั้นการเข้าถึงข้อมูลเพื่อให้ผู้ใช้ไม่รู้ว่าตนกำลังจัดการกับ SQLServer

และด้วยเหตุนี้ "ที่เก็บ" ส่วนใหญ่บน EF ไม่ใช่ Facades ที่ดีด้วยซ้ำไปเพราะมันเป็นเพียงการทำแผนที่ตรงไปตรงมากับวิธีการเดียวใน EF ถึงจุดที่มีลายเซ็นเดียวกัน

ดังนั้นเหตุผลสองประการในการใช้รูปแบบ "Repository" ที่เรียกว่านี้กับ EF คือเพื่อให้การทดสอบง่ายขึ้นและสร้างการเรียก "กระป๋อง" ชุดย่อย ไม่เลวในตัวเอง แต่ไม่ใช่ Repository อย่างชัดเจน


1

Linq เป็น 'Repository' ในปัจจุบัน

ISession + Linq เป็นที่เก็บอยู่แล้วและคุณไม่จำเป็นต้องใช้ทั้งสองอย่าง GetXByYวิธีการหรือQueryData(Query q)นัยทั่วไป ด้วยความหวาดระแวงเล็กน้อยกับการใช้งาน DAL ฉันยังคงชอบอินเทอร์เฟซที่เก็บข้อมูลมากกว่า (จากมุมมองของการบำรุงรักษาเรายังคงต้องมีส่วนหน้าบนอินเทอร์เฟซการเข้าถึงข้อมูลเฉพาะ)

นี่คือพื้นที่เก็บข้อมูลที่เราใช้ - ซึ่งจะยกเลิกการจับคู่เราจากการใช้งาน nhibernate โดยตรง แต่มีอินเทอร์เฟซ linq (เนื่องจากการเข้าถึง ISession ในกรณีพิเศษซึ่งจะขึ้นอยู่กับ refactor ในที่สุด)

class Repo
{
    ISession _session; //via ioc
    IQueryable<T> Query()
    {
        return _session.Query<T>();
    }
}

คุณทำอะไรสำหรับชั้นบริการ?
Dejan.S

ตัวควบคุมสอบถาม repo สำหรับข้อมูลแบบอ่านอย่างเดียวทำไมต้องเพิ่มเลเยอร์พิเศษ ความเป็นไปได้อื่น ๆ คือการใช้ "ContentService" ซึ่งมีแนวโน้มที่จะเป็นที่เก็บระดับบริการมากขึ้นเรื่อย ๆ : GetXByY ฯลฯ เป็นต้น สำหรับการดำเนินการแก้ไข - บริการแอปพลิเคชันเป็นเพียงสิ่งที่ไม่เกี่ยวข้องกับกรณีการใช้งาน - พวกเขาใช้ BL และ repo ได้อย่างอิสระ ..
mikalai

ฉันเคยทำชั้นบริการสำหรับตรรกะทางธุรกิจ ฉันไม่แน่ใจจริงๆว่าฉันทำตามสิ่งที่คุณใช้กับ ContentService โปรดอธิบายอย่างละเอียด การทำคลาสตัวช่วยเป็น "ชั้นบริการ" จะเป็นการดีหรือไม่
Dejan.S

โดย 'ชั้นบริการ' ฉันหมายถึง 'บริการแอปพลิเคชัน' พวกเขาสามารถใช้ที่เก็บและส่วนสาธารณะอื่น ๆ ของเลเยอร์โดเมน "ชั้นบริการ" ไม่ใช่แนวทางปฏิบัติที่ไม่ดี แต่ฉันจะหลีกเลี่ยงการสร้างคลาส XService เพียงเพื่อให้ผลลัพธ์รายการ <X> ช่องความคิดเห็นดูเหมือนจะสั้นเกินไปที่จะอธิบายรายละเอียดเกี่ยวกับบริการขออภัย
mikalai

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

1

ที่เก็บ (หรือ แต่เลือกที่จะเรียกว่า) ในเวลานี้สำหรับฉันก็คือส่วนใหญ่เกี่ยวกับการสรุปห่างชั้นติดตา

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

ดังนั้นฉันมักจะมี

public interface IRepository : IDisposable
{
    void Save<TEntity>(TEntity entity);
    void SaveList<TEntity>(IEnumerable<TEntity> entities);

    void Delete<TEntity>(TEntity entity);
    void DeleteList<TEntity>(IEnumerable<TEntity> entities);

    IList<TEntity> GetAll<TEntity>() where TEntity : class;
    int GetCount<TEntity>() where TEntity : class;

    void StartConversation();
    void EndConversation();

    //if query objects can be self sustaining (i.e. not need additional configuration - think session), there is no need to include this method in the repository.
    TResult ExecuteQuery<TResult>(IQueryObject<TResult> query);
}

อาจเพิ่มวิธีการ async ด้วยการเรียกกลับเป็นผู้รับมอบสิทธิ์ repo นั้นง่ายต่อการใช้งานทั่วไปดังนั้นฉันจึงไม่สามารถแตะบรรทัดของการใช้งานจากแอพไปยังแอพได้ นี่เป็นเรื่องจริงอย่างน้อยเมื่อใช้ NH ฉันก็ทำกับ EF ด้วย แต่ทำให้ฉันเกลียด EF 4. การสนทนาคือจุดเริ่มต้นของธุรกรรม เจ๋งมากถ้าไม่กี่คลาสแชร์อินสแตนซ์ที่เก็บ นอกจากนี้สำหรับ NH หนึ่ง repo ในการนำไปใช้ของฉันเท่ากับหนึ่งเซสชันที่เปิดตามคำขอแรก

จากนั้น Query Objects

public interface IQueryObject<TResult>
{
    /// <summary>Provides configuration options.</summary>
    /// <remarks>
    /// If the query object is used through a repository this method might or might not be called depending on the particular implementation of a repository.
    /// If not used through a repository, it can be useful as a configuration option.
    /// </remarks>
    void Configure(object parameter);

    /// <summary>Implementation of the query.</summary>
    TResult GetResult();
}

สำหรับการกำหนดค่าฉันใช้ใน NH เพื่อส่งผ่านใน ISession เท่านั้น ใน EF ไม่สมเหตุสมผลไม่มากก็น้อย

ตัวอย่างแบบสอบถามคือ .. (NH)

public class GetAll<TEntity> : AbstractQueryObject<IList<TEntity>>
    where TEntity : class
{
    public override IList<TEntity> GetResult()
    {
        return this.Session.CreateCriteria<TEntity>().List<TEntity>();
    }
}

ในการทำแบบสอบถาม EF คุณจะต้องมีบริบทในฐานบทคัดย่อไม่ใช่เซสชัน แต่แน่นอน ifc จะเหมือนกัน

ด้วยวิธีนี้แบบสอบถามจะถูกห่อหุ้มด้วยตัวเองและสามารถทดสอบได้ง่าย เหนือสิ่งอื่นใดรหัสของฉันอาศัยเฉพาะอินเทอร์เฟซเท่านั้น ทุกอย่างสะอาดมาก อ็อบเจ็กต์โดเมน (ธุรกิจ) เป็นเพียงแค่นั้นเช่นไม่มีการผสมความรับผิดชอบเช่นเมื่อใช้รูปแบบการบันทึกที่ใช้งานอยู่ซึ่งแทบจะไม่สามารถทดสอบได้และผสมรหัสการเข้าถึงข้อมูล (แบบสอบถาม) ในออบเจ็กต์โดเมนและในการทำเช่นนั้นเป็นการผสมความกังวล (วัตถุที่ดึงข้อมูล เอง ??). ทุกคนยังคงมีอิสระในการสร้าง POCO สำหรับการถ่ายโอนข้อมูล

สรุปแล้วการใช้โค้ดซ้ำและความเรียบง่ายนั้นมีให้กับแนวทางนี้โดยที่ไม่สูญเสียอะไรเลยที่ฉันสามารถจินตนาการได้ ความคิดใด ๆ ?

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


1
เอนทิตีการคงอยู่ (POCO ของคุณ) ไม่ใช่เอนทิตีธุรกิจ / โดเมน และจุดประสงค์ของที่เก็บคือการแยกชั้นธุรกิจ (ar any) ออกจากความคงอยู่
MikeSW

ฉันไม่เห็นการมีเพศสัมพันธ์ เห็นด้วยบ้างในส่วนของ POCO แต่ไม่สน ไม่มีอะไรที่จะหยุดคุณจากการมี POCO 'ของแท้' และยังคงใช้แนวทางนี้
h.alex

1
เอนทิตีไม่จำเป็นต้องโง่ POCO เลย ในความเป็นจริงการสร้างแบบจำลองตรรกะทางธุรกิจให้เป็นเอนทิตีคือสิ่งที่ฝูงชน DDD ทำอยู่ตลอดเวลา รูปแบบการพัฒนานี้ผสมผสานกับ NH หรือ EF ได้เป็นอย่างดี
chris

1

สำหรับฉันมันเป็นการตัดสินใจที่ง่ายและมีปัจจัยไม่มากนัก ปัจจัยคือ:

  1. ที่เก็บมีไว้สำหรับคลาสโดเมน
  2. ในบางแอปของฉันคลาสโดเมนจะเหมือนกับคลาสการคงอยู่ (DAL) ของฉัน แต่คลาสอื่น ๆ ไม่ใช่
  3. ในเมื่อมันเหมือนกัน EF ก็ให้ Repositories แก่ฉันแล้ว
  4. EF ให้การโหลดแบบขี้เกียจและ IQueryable ฉันชอบสิ่งเหล่านี้
  5. การสร้างนามธรรม / 'facading' / การนำที่เก็บซ้ำไปใช้บน EF มักจะหมายถึงการสูญเสียความเกียจคร้านและความฉลาดทางสติปัญญา

ดังนั้นหากแอปของฉันไม่สามารถปรับ # 2 แยกโดเมนและโมเดลข้อมูลได้ฉันมักจะไม่กังวลกับ # 5

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.