Entity Framework 4 - AddObject vs Attach


132

ผมได้ทำงานกับ Entity Framework 4 เมื่อเร็ว ๆ นี้และกำลังสับสนเล็กน้อยว่าเมื่อใช้ObjectSet.AttachและObjectSet.AddObject

จากความเข้าใจของฉัน:

  • ใช้ "แนบ" เมื่อมีเอนทิตีอยู่แล้วในระบบ
  • ใช้ "AddObject" เมื่อสร้างเอนทิตีใหม่

ดังนั้นถ้าฉันกำลังสร้างบุคคลใหม่ฉันจะทำสิ่งนี้

var ctx = new MyEntities();
var newPerson = new Person { Name = "Joe Bloggs" };
ctx.Persons.AddObject(newPerson);
ctx.SaveChanges();

หากฉันแก้ไขบุคคลที่มีอยู่ฉันจะทำสิ่งนี้:

var ctx = new MyEntities();
var existingPerson = ctx.Persons.SingleOrDefault(p => p.Name = "Joe Bloggs" };
existingPerson.Name = "Joe Briggs";
ctx.SaveChanges();

โปรดทราบว่านี่เป็นตัวอย่างง่ายๆ ในความเป็นจริงฉันใช้ Pure POCO (ไม่มีการสร้างรหัส) รูปแบบพื้นที่เก็บข้อมูล (อย่าจัดการกับ ctx.Persons) และ Unit of Work (อย่าจัดการกับ ctx.SaveChanges) แต่ "ภายใต้การครอบคลุม" ข้างต้นคือสิ่งที่เกิดขึ้นในการนำไปใช้

ตอนนี้คำถามของฉัน - ฉันยังไม่พบสถานการณ์ที่ฉันได้มีการใช้แนบ

ฉันพลาดอะไรไปที่นี่? เราต้องใช้ Attach เมื่อไร?

แก้ไข

เพื่อชี้แจงฉันกำลังมองหาตัวอย่างว่าเมื่อใดควรใช้ Attach over AddObject (หรือกลับกัน)

แก้ไข 2

คำตอบด้านล่างนี้ถูกต้อง (ซึ่งฉันยอมรับ) แต่คิดว่าฉันจะเพิ่มอีกตัวอย่างหนึ่งที่ Attach จะเป็นประโยชน์

ในตัวอย่างด้านบนของฉันสำหรับการแก้ไขบุคคลที่มีอยู่มีการดำเนินการค้นหาสองคำค้นหา

หนึ่งในการดึงข้อมูลบุคคล (.ingleOrDefault) และอีกรายการหนึ่งเพื่อทำการอัปเดต (.aveChanges)

ถ้า (ด้วยเหตุผลบางประการ) ฉันรู้แล้วว่ามี "Joe Bloggs" อยู่ในระบบเหตุใดจึงต้องสอบถามเพิ่มเติมเพื่อให้เขาทราบก่อน ฉันสามารถทำได้:

var ctx = new MyEntities();
var existingPerson = new Person { Name = "Joe Bloggs" };
ctx.Persons.Attach(existingPerson);
ctx.SaveChanges();

สิ่งนี้จะส่งผลให้มีการดำเนินการเพียงคำสั่ง UPDATE


Attach ยังใช้ใน MVC วันนี้เมื่อนำโมเดลกลับไปที่ EF โดยตรง ใช้งานได้ดีและช่วยประหยัดโค้ดได้มากมาย
Piotr Kula

คำตอบ:


162

ObjectContext.AddObjectและ ObjectSet.AddObject : addObjectวิธีการสำหรับการเพิ่มวัตถุที่สร้างขึ้นใหม่ที่ไม่ได้อยู่ในฐานข้อมูล กิจการจะได้รับการสร้างขึ้นโดยอัตโนมัติชั่วคราว EntityKeyและ EntityState ของมันจะถูกกำหนดให้เพิ่ม เมื่อมีการเรียกใช้ SaveChanges EF จะต้องมีการแทรกเอนทิตีนี้ลงในฐานข้อมูลอย่างชัดเจน

ObjectContext.Attachและ ObjectSet.Attach :
ในทางกลับกัน Attachใช้สำหรับเอนทิตีที่มีอยู่แล้วในฐานข้อมูล แทนที่จะตั้งค่า EntityState เป็นเพิ่มให้แนบผลลัพธ์ใน Unchanged EntityState ซึ่งหมายความว่าไม่มีการเปลี่ยนแปลงเนื่องจากถูกแนบกับบริบท ออบเจ็กต์ที่คุณกำลังแนบจะถือว่ามีอยู่ในฐานข้อมูล หากคุณแก้ไขวัตถุหลังจากที่แนบมาแล้วเมื่อคุณเรียกใช้ SaveChanges ค่าของ EntityKey จะใช้เพื่ออัปเดต (หรือลบ) แถวที่เหมาะสมโดยการค้นหา ID ที่ตรงกันในตารางฐานข้อมูล

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

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

var existingPerson = ctx.Persons.SingleOrDefault(p => p.Name = "Joe Bloggs" };
var myAddress = ctx.Addresses.First(a => a.PersonID != existingPerson.PersonID);
existingPerson.Addresses.Attach(myAddress);
// OR:
myAddress.PersonReference.Attach(existingPerson)
ctx.SaveChanges();

ขอบคุณสำหรับคำตอบฉันเข้าใจความหมายของทั้งสอง (aka สองย่อหน้าแรก) แต่ฉันไม่เข้าใจสถานการณ์ที่ฉันต้องใช้ Attach ย่อหน้าสุดท้ายของคุณไม่สมเหตุสมผลสำหรับฉันจริงๆ (อ่านโดยพื้นฐานแล้วเหมือนกับการรวมสองย่อหน้าแรก) คุณช่วยยกตัวอย่างได้ไหมว่าฉันจะใช้ "แนบ" ในสถานการณ์ข้างต้นได้อย่างไร นั่นคือสิ่งที่ฉันกำลังมองหา - ตัวอย่างไม่ใช่คำจำกัดความ ขอบคุณเวลาของคุณจริงๆ :)
RPM1984

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

1
ใช่ตอนนี้ฉัน gotcha ฉันเพิ่งดู TechEd vid ใน EF4 (โดย Julie Lerman) ซึ่งแสดงให้เห็นตัวอย่าง คุณอาจมีเอนทิตีที่คุณไม่ได้ดึงข้อมูลจากคิวรี (เช่นมันเชื่อมต่อแบบแยกกัน) แต่คุณรู้ว่ามีอยู่ดังนั้นคุณจึงใช้สิ่งที่แนบเพื่อทำการอัปเดตในเอนทิตีนั้น มีเหตุผลแม้ว่าฉันจะยังคงวางโครงสร้างเพื่อจินตนาการถึงสถานการณ์ที่คุณจะมีเอนทิตี "ตัดการเชื่อมต่อ" ขอบคุณสำหรับความช่วยเหลือของคุณ.
RPM1984

1
ยิ่งใหญ่ ฉันขอให้คุณแชร์ลิงก์ของวิดีโอให้กับนักพัฒนาคนอื่น ๆ ที่อาจอ่านโพสต์นี้ได้ไหม
Morteza Manavi

3
ที่ลิงค์ข้างต้นร่วมกันโดย RPM1984 เสียก็จะเปลี่ยนเส้นทางในขณะนี้เพื่อchannel9.msdn.com/Events/TechEd/NorthAmerica/2010/DEV205 Enjoy
ซ้อน

31

นี่เป็นคำตอบที่ล่าช้า แต่อาจช่วยให้ผู้อื่นพบสิ่งนี้

โดยทั่วไปเอนทิตี "ตัดการเชื่อมต่อ" สามารถเกิดขึ้นได้เมื่อคุณจัดการเอนทิตีภายนอกขอบเขต "ใช้"

Employee e = null;

using (var ctx = new MyModelContainer())
{
     e = ctx.Employees.SingleOrDefault(emp => emp .....);
}

using (var ctx2 = new MyModelContainer())
{
     e; // This entity instance is disconnected from ctx2
}

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

นั่นคือสิ่งที่ฉันเข้าใจ


3
ตัวอย่างของ Tchi เป็นตัวอย่างที่ยอดเยี่ยมและเรียบง่าย - ใช่ตัวแปร Employee ควรได้รับการประกาศภายนอก ลองใช้ e.Address.Street นอกขอบเขตและดูป๊อปอัปข้อยกเว้นการอ้างอิงที่เป็นโมฆะ หากคุณแนบแอปพลิเคชันจะไม่ต้องกลับไปที่ฐานข้อมูลสำหรับพนักงานในขอบเขตที่สอง
Steve

9

นี่คือคำพูดจากProgramming Entity Framework: DbContext

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

private static void TestDeleteDestination()
{
    Destination canyon;
    using (var context = new BreakAwayContext())
    {
        canyon = (from d in context.Destinations
        where d.Name == "Grand Canyon"
        select d).Single();
    }
    DeleteDestination(canyon);
}
private static void DeleteDestination(Destination destination)
{
    using (var context = new BreakAwayContext())
    {
        context.Destinations.Attach(destination);
        context.Destinations.Remove(destination);
        context.SaveChanges();
    }
}

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


-8

แล้วการอ้างถึงคีย์หลักแทนการแนบเท่านั้นล่ะ?

เช่น:

var existingPerson = ctx.Persons.SingleOrDefault(p => p.Name = "Joe Bloggs" };
var myAddress = ctx.Addresses.First(a => a.PersonID != existingPerson.PersonID);
existingPerson.AddressId = myAddress.Id // not -> existingPerson.Addresses.Attach(myAddress);
// OR:
myAddress.Person.Id = existingPerson.Id // not -> myAddress.PersonReference.Attach(existingPerson);
ctx.SaveChanges();
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.