Entity Framework: ฐานข้อมูลเดียว, หลาย DbContexts นี่เป็นความคิดที่ไม่ดีเหรอ? [ปิด]


213

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

อย่างไรก็ตามเพื่อนร่วมงานบางคนต้องการแบ่งหน้าที่การใช้งานออกเป็นDbContextคลาสที่แยกกัน

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

ดังนั้นฉันกำลังมองหา:

A) ตัวอย่างที่เป็นรูปธรรมว่าทำไมสิ่งนี้จึงเป็นความคิดที่ไม่ดี

B) รับรองว่าทั้งหมดนี้จะทำงานได้ดี


ดูคำตอบของฉัน: stackoverflow.com/questions/8244405/…
Mohsen Alikhani

คำตอบ:


168

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

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

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


21
การใช้บริบทเดียวต่อแอปอาจมีราคาแพงหากแอปพลิเคชันมีเอนทิตี / ตารางจำนวนมาก ดังนั้นขึ้นอยู่กับสคีมามันอาจสมเหตุสมผลที่จะมีหลายบริบท
DarthVader

7
เนื่องจากฉันไม่ได้สมัครเป็นสมาชิก pluralsight ฉันจึงพบบทความที่ยอดเยี่ยมนี้โดย Julie Lerman ( ความคิดเห็นของเธอ ) เขียนได้ดีหลังจาก Q / A นี้ แต่เหมาะสมมาก: msdn.microsoft.com/en-us/magazine/jj883952.aspx
Dave T.

ฉันขอแนะนำกรอบงานเอนทิตีเพื่อสนับสนุนหลาย dbcontexts ในฐานข้อมูลเดียวกันโดยการตั้งชื่อแบบแผน ด้วยเหตุนี้ฉันยังคงเขียน ORM ของฉันเองเพื่อจุดประสงค์การใช้งานแบบแยกส่วน มันยากที่จะเชื่อว่ามันบังคับให้แอปพลิเคชั่นเดียวใช้ฐานข้อมูลเดียว โดยเฉพาะอย่างยิ่งในฟาร์มฟาร์มคุณมีฐานข้อมูลจำนวน จำกัด
อิสระที่

นอกจากนี้ฉันรู้ว่าคุณสามารถเปิดใช้งาน - การย้ายข้อมูลสำหรับบริบทเดียวภายในโครงการผ่านทางคอนโซล PM
Piotr Kwiatek

9
@PiotrKwiatek ไม่แน่ใจว่าสิ่งนี้เปลี่ยนไประหว่างความคิดเห็นของคุณและตอนนี้ แต่Enable-Migrations -ContextTypeName MyContext -MigrationsDirectory Migrations\MyContextMigrationsใช้ได้ในขณะนี้
แซค

60

ฉันเขียนคำตอบนี้เมื่อประมาณสี่ปีก่อนและความคิดเห็นของฉันก็ไม่เปลี่ยนแปลง แต่ตั้งแต่นั้นมามีการพัฒนาที่สำคัญในด้านหน้าของบริการไมโคร ฉันเพิ่มบันทึกย่อเฉพาะบริการไมโครเมื่อสิ้นสุด ...

ฉันจะชั่งน้ำหนักกับความคิดที่มีประสบการณ์จริงเพื่อสำรองคะแนนของฉัน

ฉันถูกนำไปใช้กับแอปพลิเคชันขนาดใหญ่ที่มีห้าบริบทสำหรับฐานข้อมูลเดียว ในท้ายที่สุดเราสิ้นสุดการลบบริบททั้งหมดยกเว้นหนึ่ง - ย้อนกลับไปที่บริบทเดียว

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

แต่ในทางปฏิบัติเมื่อใบสมัครของเราเติบโตขึ้นตารางจำนวนมากของเราก็มีความสัมพันธ์ร่วมกันในบริบทต่างๆของเรา ตัวอย่างเช่นการสืบค้นไปยังตาราง A ในบริบท 1 ต้องมีการรวมตาราง B ในบริบท 2 ด้วย

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

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

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

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

เกี่ยวกับบริการไมโครเดียวบริบทเดียวยังคงเหมาะสม อย่างไรก็ตามสำหรับ micro-services แต่ละบริการจะมีบริบทของตัวเองซึ่งรวมเฉพาะตารางฐานข้อมูลที่เกี่ยวข้องกับบริการนั้น กล่าวอีกนัยหนึ่งถ้าบริการ x เข้าถึงตาราง 1 และ 2 และบริการ y เข้าถึงตาราง 3 และ 4 แต่ละบริการจะมีบริบทเฉพาะของตัวเองซึ่งรวมถึงตารางเฉพาะสำหรับบริการนั้น ๆ

ฉันสนใจในความคิดของคุณ


8
ฉันต้องยอมรับที่นี่โดยเฉพาะเมื่อกำหนดเป้าหมายฐานข้อมูลที่มีอยู่ ตอนนี้ฉันกำลังแก้ไขปัญหานี้อยู่และความรู้สึกของฉันคือ: 1. การมีตารางทางกายภาพที่เหมือนกันในหลายบริบทเป็นความคิดที่ไม่ดี 2. หากเราไม่สามารถตัดสินใจได้ว่าตารางอยู่ในบริบทใดบริบทหนึ่งบริบททั้งสองนั้นไม่แตกต่างกันมากพอที่จะแยกออกจากกันอย่างมีเหตุผล
jkerak

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

1
ฉันรู้สึกถึงความเจ็บปวดที่คุณเผชิญอยู่ลึก! : / ฉันคิดว่าบริบทเดียวเป็นทางเลือกที่ดีกว่าสำหรับความเรียบง่าย
ahmet

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

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

54

เธรดนี้เพิ่งสร้างขึ้นบน StackOverflow และดังนั้นฉันต้องการเสนอ "B) อีกครั้งรับรองว่าทั้งหมดนี้จะดี" :)

ฉันทำสิ่งนี้โดยใช้รูปแบบบริบทที่ จำกัด ของ DDD ฉันได้เขียนเกี่ยวกับเรื่องนี้ในหนังสือของฉัน Programming Entity Framework: DbContext และเป็นจุดเน้นของโมดูล 50 นาทีในหนึ่งในหลักสูตรของฉันใน Pluralsight -> http://pluralsight.com/training/Courses/TableOfContents/efar Architecture


7
วิดีโอการฝึกอบรมเกี่ยวกับเสียงพหูพจน์นั้นทำได้ดีมากในการอธิบายแนวความคิดขนาดใหญ่อย่างไรก็ตามตัวอย่างที่คุณให้นั้นมีความสำคัญมากเมื่อเทียบกับโซลูชันระดับองค์กร (ตัวอย่างเช่น NuGet ของชุดประกอบที่มีคำจำกัดความ DbContext บริบทที่ถูก จำกัด DDD ถูกทำลายอย่างสมบูรณ์โดยตัวอย่างสุดท้ายของคุณที่ DbContext ที่ซ้ำกันถูกกำหนดไว้เพื่อเก็บการประกาศซ้ำกันในแต่ละ DbSet ฉันขอขอบคุณที่คุณถูก จำกัด ด้วยเทคโนโลยี ฉันชอบวิดีโอของคุณ แต่อันนี้ทำให้ฉันต้องการมากกว่านี้
Victor Romeo

5
ฉันมุ่งมั่นที่จะมองหาภาพรวมขนาดใหญ่ ปัญหาเกี่ยวกับแพคเกจ nuget ในแอพขนาดใหญ่นั้นค่อนข้างจะผิดเพี้ยนสำหรับวิดีโอ ef ตัวอย่าง "เสีย" อีกแล้ว ... อาจดีกว่าที่จะนำสิ่งนี้ไปสู่การสนทนาส่วนตัวเนื่องจากบทวิจารณ์ของหลักสูตรของฉันค่อนข้างอยู่นอกขอบเขต (และอาจไม่เหมาะสม) สำหรับฟอรัมนี้ ฉันคิดว่า SO ช่วยให้คุณสามารถติดต่อฉันได้โดยตรง
Julie Lerman

57
คงจะดีถ้า Julie ได้แบ่งปันข้อมูลบางอย่างเกี่ยวกับปัญหา / คำถามของ OP แต่โพสต์เป็นเพียงเพื่อส่งเสริมการสมัครสมาชิกจ่ายให้กับพหูพจน์ หากปลั๊กสำหรับผลิตภัณฑ์อย่างน้อยลิงก์ไปยังข้อมูลเกี่ยวกับวิธีแก้ปัญหาที่แนะนำ (รูปแบบบริบทที่ถูกล้อมรอบ DDD) จะเป็นประโยชน์ 'DDD' คืออะไรที่อธิบายไว้ในหน้า 22 ของ "กรอบงานการเขียนโปรแกรมเอนทิตี: DBContext" เพราะฉันดู (ไม่มีดัชนี) สำหรับ 'DDD' หรือแม้แต่ 'บริบทที่ถูกผูกมัด' และไม่สามารถค้นหาได้ ... ไม่สามารถรอให้คุณทำการแก้ไขใหม่สำหรับ EF6 ...
Compassionate Narcissist

12
ขออภัยฉันไม่ได้พยายามโปรโมตแค่เพิ่มใน OP "ต้องการการรับรอง" Ladislav และคนอื่น ๆ ทำได้ดีพร้อมรายละเอียด ดังนั้นฉันจึงพยายามทำบางสิ่งที่ฉันสร้างขึ้นแล้วซึ่งมีความลึกมากกว่าที่ฉันสามารถถ่ายทอดได้ดังนั้น นี่คือแหล่งข้อมูลอื่น ๆ ที่ฉันได้ครอบคลุมเนื้อหาบางส่วน: msdn.microsoft.com/en-us/magazine/jj883952.aspx & msdn.microsoft.com/en-us/magazine/dn342868.aspx & oredev.org / 2013 / wed-fri-conference / …
Julie Lerman

สรุปคำแนะนำเกี่ยวกับรหัสของ @JulieLerman ดูคำตอบของฉันstackoverflow.com/a/36789520/1586498
OzBob

46

แยกแยะบริบทโดยตั้งค่าสคีมาเริ่มต้น

ใน EF6 คุณสามารถมีหลายบริบทเพียงแค่ระบุชื่อสำหรับสคีมาฐานข้อมูลเริ่มต้นในOnModelCreatingวิธีการที่คุณDbContextได้รับคลาส (โดยที่การตั้งค่า Fluent-API) สิ่งนี้จะทำงานใน EF6:

public partial class CustomerModel : DbContext
{   
    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.HasDefaultSchema("Customer");

        // Fluent API configuration
    }   
}

ตัวอย่างนี้จะใช้ "ลูกค้า" เป็นคำนำหน้าสำหรับตารางฐานข้อมูลของคุณ (แทนที่จะเป็น "dbo") ที่สำคัญยังจะคำนำหน้า__MigrationHistoryตาราง (s), Customer.__MigrationHistoryเช่น ดังนั้นคุณสามารถมีมากกว่าหนึ่ง__MigrationHistoryตารางในฐานข้อมูลเดียวหนึ่งตารางสำหรับแต่ละบริบท ดังนั้นการเปลี่ยนแปลงที่คุณทำกับบริบทหนึ่งจะไม่ยุ่งกับสิ่งอื่น

เมื่อเพิ่มการโอนย้ายให้ระบุชื่อแบบเต็มของคลาสการกำหนดค่าของคุณ (มาจากDbMigrationsConfiguration) เป็นพารามิเตอร์ในadd-migrationคำสั่ง:

add-migration NAME_OF_MIGRATION -ConfigurationTypeName FULLY_QUALIFIED_NAME_OF_CONFIGURATION_CLASS


คำศัพท์สั้น ๆ เกี่ยวกับคีย์บริบท

อ้างอิงจากบทความ MSDN นี้ " บทที่ - แบบจำลองหลายตัวที่กำหนดเป้าหมายฐานข้อมูลเดียวกัน " EF 6 อาจจะจัดการกับสถานการณ์แม้ว่าจะมีเพียงMigrationHistoryตารางเดียวเท่านั้นเพราะในตารางนั้นมีคอลัมน์ContextKeyเพื่อแยกความแตกต่างของการย้ายข้อมูล

อย่างไรก็ตามฉันต้องการมีมากกว่าหนึ่งMigrationHistoryตารางโดยระบุ schema เริ่มต้นตามที่อธิบายไว้ข้างต้น


การใช้โฟลเดอร์การโยกย้ายแยกต่างหาก

ในสถานการณ์ดังกล่าวคุณอาจต้องการทำงานกับโฟลเดอร์ "การโยกย้าย" ที่แตกต่างกันในโครงการของคุณ คุณสามารถตั้งค่าDbMigrationsConfigurationคลาสที่ได้รับตามการใช้MigrationsDirectoryคุณสมบัติ:

internal sealed class ConfigurationA : DbMigrationsConfiguration<ModelA>
{
    public ConfigurationA()
    {
        AutomaticMigrationsEnabled = false;
        MigrationsDirectory = @"Migrations\ModelA";
    }
}

internal sealed class ConfigurationB : DbMigrationsConfiguration<ModelB>
{
    public ConfigurationB()
    {
        AutomaticMigrationsEnabled = false;
        MigrationsDirectory = @"Migrations\ModelB";
    }
}


สรุป

โดยรวมแล้วคุณสามารถพูดได้ว่าทุกอย่างแยกจากกันอย่างสมบูรณ์: บริบทโฟลเดอร์การย้ายข้อมูลในโครงการและตารางในฐานข้อมูล

ฉันจะเลือกวิธีการแก้ปัญหาดังกล่าวหากมีกลุ่มของหน่วยงานที่เป็นส่วนหนึ่งของหัวข้อที่ใหญ่กว่า แต่ไม่เกี่ยวข้อง (ผ่านคีย์ต่างประเทศ) ซึ่งกันและกัน

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


คุณจะทำอย่างไรเมื่อคุณจำเป็นต้องอัพเดตเอนทิตี 2 แห่งที่อยู่ในบริบทที่แตกต่างกัน
sotn

ฉันจะสร้างคลาสใหม่ (บริการ) ที่รู้ทั้งบริบทพิจารณาชื่อที่ดีและความรับผิดชอบของคลาสนี้และทำการอัปเดตนี้ด้วยวิธีใดวิธีหนึ่ง
Martin

7

ตัวอย่างง่ายๆเพื่อให้บรรลุดังต่อไปนี้:

    ApplicationDbContext forumDB = new ApplicationDbContext();
    MonitorDbContext monitor = new MonitorDbContext();

เพียงกำหนดขอบเขตคุณสมบัติในบริบทหลัก: (ใช้เพื่อสร้างและบำรุงรักษา DB) หมายเหตุ: เพียงใช้การป้องกัน: (ไม่เปิดเผยเอนทิตีที่นี่)

public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
    public ApplicationDbContext()
        : base("QAForum", throwIfV1Schema: false)
    {

    }
    protected DbSet<Diagnostic> Diagnostics { get; set; }
    public DbSet<Forum> Forums { get; set; }
    public DbSet<Post> Posts { get; set; }
    public DbSet<Thread> Threads { get; set; }
    public static ApplicationDbContext Create()
    {
        return new ApplicationDbContext();
    }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);
    }
}

MonitorContext: เปิดเผยเอนทิตีแยกที่นี่

public class MonitorDbContext: DbContext
{
    public MonitorDbContext()
        : base("QAForum")
    {

    }
    public DbSet<Diagnostic> Diagnostics { get; set; }
    // add more here
}

รูปแบบการวินิจฉัย:

public class Diagnostic
{
    [Key]
    public Guid DiagnosticID { get; set; }
    public string ApplicationName { get; set; }
    public DateTime DiagnosticTime { get; set; }
    public string Data { get; set; }
}

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

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


2
สิ่งนี้ช่วยได้มาก บริบท "รอง" ไม่จำเป็นต้องประกาศตารางที่ใช้ร่วมกัน เพียงเพิ่มDbSet<x>คำจำกัดความด้วยตนเอง ฉันทำอย่างนั้นในชั้นเรียนบางส่วนที่ตรงกับสิ่งที่ EF Designer ทำ
เกลน Little

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

6

คำเตือน: หากคุณไม่รวมบริบทหลายให้แน่ใจว่าคุณตัด n วางทุกฟังก์ชั่นต่าง ๆ ของคุณลงในซิงเกิ้ลของคุณRealContexts.OnModelCreating()CombinedContext.OnModelCreating()

ฉันเพิ่งเสียเวลาไปกับการค้นหาว่าทำไมความสัมพันธ์แบบลบของฉันไม่ได้ถูกเก็บไว้เพียงเพื่อค้นพบว่าฉันไม่ได้เปลี่ยนmodelBuilder.Entity<T>()....WillCascadeOnDelete();รหัสจากบริบทจริงของฉันไปยังบริบทที่รวมกันของฉัน


6
คุณสามารถโทรOtherContext.OnModelCreating()มาจากบริบทที่รวมกันของคุณใช่หรือไม่?
AlexFoxGill

4

ไส้ของฉันบอกฉันแบบเดียวกันเมื่อฉันเจอแบบนี้

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

ฉันจะถามคำถาม "เพียงเพราะคุณทำได้คุณควร? "

ดูบทความนี้สำหรับปัญหาที่ฉันเจอซึ่งเกี่ยวข้องกับการออกแบบนี้ นิพจน์ LINQ ที่ระบุมีการอ้างอิงถึงข้อความค้นหาที่เกี่ยวข้องกับบริบทที่แตกต่างกัน


3
ฉันทำงานในระบบขนาดใหญ่ที่เรามีบริบทหลายอย่าง สิ่งหนึ่งที่ฉันพบคือบางครั้งคุณต้องรวม DbSet เดียวกันในบริบทหลาย ๆ ในอีกด้านหนึ่งสิ่งนี้ทำให้เกิดข้อกังวลเรื่องความบริสุทธิ์ แต่ก็ทำให้คุณสามารถดำเนินการสอบถามได้ สำหรับกรณีที่มีตารางผู้ดูแลระบบบางอย่างที่คุณต้องอ่านคุณสามารถเพิ่มลงในคลาส DbContext ฐานและสืบทอดในบริบทโมดูลแอปของคุณ วัตถุประสงค์ของผู้ดูแลระบบ "ของจริง" คุณอาจถูกกำหนดใหม่เป็น "มอบการบำรุงรักษาสำหรับตารางผู้ดูแลระบบ" แทนที่จะให้สิทธิ์การเข้าถึงทั้งหมดแก่พวกเขา
JMarsch

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

1
คุณไม่จำเป็นต้องรวมเอนทิตีในทั้งสองคุณสามารถรับรหัสและทำแบบสอบถามที่ 2 ไปยังบริบทอื่น สำหรับระบบขนาดเล็กสิ่งนี้ไม่ดีสำหรับฐานข้อมูลขนาดใหญ่ / ระบบที่มีการเชื่อมโยงกันหลาย devs ของโครงสร้างหลายตารางเป็นปัญหาที่ใหญ่และยากกว่าแบบสอบถาม 2 รายการ
user1496062

4

แรงบันดาลใจจาก [@JulieLerman DDD MSDN Mag Article 2013] [1]

    public class ShippingContext : BaseContext<ShippingContext>
{
  public DbSet<Shipment> Shipments { get; set; }
  public DbSet<Shipper> Shippers { get; set; }
  public DbSet<OrderShippingDetail> Order { get; set; } //Orders table
  public DbSet<ItemToBeShipped> ItemsToBeShipped { get; set; }
  protected override void OnModelCreating(DbModelBuilder modelBuilder)
  {
    modelBuilder.Ignore<LineItem>();
    modelBuilder.Ignore<Order>();
    modelBuilder.Configurations.Add(new ShippingAddressMap());
  }
}

public class BaseContext<TContext>
  DbContext where TContext : DbContext
{
  static BaseContext()
  {
    Database.SetInitializer<TContext>(null);
  }
  protected BaseContext() : base("DPSalesDatabase")
  {}
}   

"หากคุณกำลังพัฒนาใหม่และคุณต้องการให้ Code First สร้างหรือย้ายฐานข้อมูลของคุณตามคลาสของคุณคุณจะต้องสร้าง" uber-model "โดยใช้ DbContext ที่รวมคลาสและความสัมพันธ์ทั้งหมดที่จำเป็นในการ สร้างแบบจำลองที่สมบูรณ์ที่แสดงถึงฐานข้อมูลอย่างไรก็ตามบริบทนี้ต้องไม่สืบทอดจาก BaseContext " JL


2

ในรหัสแรกคุณสามารถมีหลาย DBContext และเพียงหนึ่งฐานข้อมูล คุณเพียงแค่ต้องระบุสตริงการเชื่อมต่อในตัวสร้าง

public class MovieDBContext : DbContext
{
    public MovieDBContext()
        : base("DefaultConnection")
    {

    }
    public DbSet<Movie> Movies { get; set; }
}

ใช่คุณทำได้ แต่คุณจะค้นหาจากเอนทิตีที่แตกต่างจากบริบทฐานข้อมูลต่าง ๆ ได้อย่างไร
Reza

2

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


1

ฉันต้องการแบ่งปันกรณีซึ่งฉันคิดว่าเป็นไปได้ของการมีหลาย DBContexts ในฐานข้อมูลเดียวกันทำให้รู้สึกดี

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

ตอนนี้สำหรับฐานข้อมูลผู้ใช้ฉันได้ติดตั้ง schema สองตัวผ่านทาง EF หนึ่งคือค่าเริ่มต้นที่จัดทำโดยเฟรมเวิร์ก AspNet Identity อื่น ๆ เป็นของเราเองดำเนินการสิ่งอื่นที่เกี่ยวข้องกับผู้ใช้ ฉันชอบโซลูชันนี้มากกว่าขยาย ApsNet schema เนื่องจากฉันสามารถจัดการกับการเปลี่ยนแปลงในอนาคตของ AspNet Identity ได้อย่างง่ายดายและในขณะเดียวกันการแยกทำให้ผู้เขียนโปรแกรมชัดเจนว่า "ข้อมูลผู้ใช้ของเรา" ไปในสคีมาผู้ใช้เฉพาะที่เรากำหนดไว้ .


2
ฉันไม่เห็นคำถามใด ๆ ในการตอบกลับ ฉันไม่ได้ถามคำถามเดียว! ค่อนข้างแบ่งปันสถานการณ์ที่หัวข้อของการสนทนาทำให้รู้สึกดี
freilebt

0

หือใช้เวลาค่อนข้างมากกับปัญหากับบริบท DB ที่แยกต่างหากสำหรับแต่ละ DB schema หวังว่ามันจะช่วยคนอื่น ...

ฉันเพิ่งเริ่มทำงานในโครงการที่มีฐานข้อมูลเดียวกับ 3 schema (วิธีแรกของ DB) หนึ่งในนั้นสำหรับการจัดการผู้ใช้ มีบริบทฐานข้อมูลนั่งร้านจากแต่ละสคีมาที่แยกกัน แน่นอนผู้ใช้มีความสัมพันธ์กับสคีมาอื่น ๆ เช่น สคีมา KB มีตารางหัวข้อซึ่งมี "สร้างโดย", "แก้ไขล่าสุดโดย" ฯลฯ FK ให้กับสคีมาตัวตน appuser ตาราง

วัตถุเหล่านี้ถูกโหลดแยกต่างหากใน C # ตอนแรกหัวข้อถูกโหลดจาก 1 บริบทจากนั้นผู้ใช้ถูกโหลดผ่าน ID ผู้ใช้จากบริบท db อื่น ๆ - ไม่ดีต้องแก้ไขสิ่งนี้! (คล้ายกับการใช้หลาย dbcontexts ในฐานข้อมูลเดียวกันกับ EF 6 )

ก่อนอื่นฉันพยายามเพิ่มคำแนะนำ FK ที่หายไปจาก schema เอกลักษณ์ไปยัง KB schema ไปยัง EF modelBuilder ในบริบท KB DB เหมือนกับว่ามีเพียง 1 บริบท แต่ฉันแยกมันเป็น 2

modelBuilder.Entity<Topic>(entity =>
{
  entity.HasOne(d => d.Creator)
    .WithMany(p => p.TopicCreator)
    .HasForeignKey(d => d.CreatorId)
    .HasConstraintName("fk_topic_app_users");

มันไม่ได้ทำงานเพราะบริบทกิโลฐานข้อมูลไม่ได้มีข้อมูลใด ๆ relation "AppUsers" does not existเกี่ยวกับวัตถุผู้ใช้ข้อผิดพลาด คำสั่งเลือกไม่มีข้อมูลที่เหมาะสมเกี่ยวกับสคีมาชื่อฟิลด์ ฯลฯ

ฉันเกือบจะให้ขึ้น แต่แล้วผมสังเกตเห็นสวิทช์ "-d" dotnet ef dbcontext scaffoldเมื่อทำงาน มันสั้นสำหรับ -data-annotations - ใช้แอ็ตทริบิวต์เพื่อกำหนดค่าโมเดล (หากเป็นไปได้) หากไม่ระบุจะใช้เฉพาะ API ที่คล่องแคล่ว ด้วยการระบุสวิตช์นี้คุณสมบัติของวัตถุที่ถูกกำหนดไม่ได้อยู่ในบริบท db OnModelCreating()แต่จะอยู่ในวัตถุด้วยคุณสมบัติ

ด้วยวิธีนี้ EF มีข้อมูลเพียงพอสำหรับการสร้างคำสั่ง SQL ที่เหมาะสมพร้อมชื่อฟิลด์และสกีมาที่เหมาะสม

TL; DR: บริบทฐานข้อมูลแยกกันไม่ได้จัดการความสัมพันธ์ (FKs) ระหว่างกันบริบทแต่ละบริบทมีข้อมูลเกี่ยวกับเอนทิตีของตนเองเท่านั้น เมื่อระบุ "-data-annotations" จะเปิดใช้dotnet ef dbcontext scaffoldงานข้อมูลเหล่านี้จะถูกเก็บไว้ในแต่ละบริบทที่แยกกัน แต่จะอยู่ในออบเจ็กต์ DB ด้วยตนเอง

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