เหตุผลของข้อผิดพลาดในรหัสที่ให้ไว้มีดังนี้
เมื่อคุณได้รับการสร้างขึ้นนิติบุคคลAจากฐานข้อมูลสถานที่ให้บริการจะเริ่มต้นด้วยการเก็บที่มีสองระเบียนใหม่S ของแต่ละใหม่นี้หน่วยงานที่มีค่าเท่ากับBIdB0
// This line of code reads entity from the database
// and creates new instance of object A from it.
var a = db.Set<A>().Single();
// When new entity A is created its field S initialized
// by a collection that contains two new instances of entity B.
// Property Id of each of these two B entities is equal to 0.
public ICollection<B> S { get; set; } = new List<B>() { new B {}, new B {} };
หลังจากเรียกใช้งานบรรทัดการvar a = db.Set<A>().Single()รวบรวมรหัสSของเอนทิตีAไม่มีเอนทิBตีจากฐานข้อมูลเนื่องจากDbContext Dbไม่ใช้การโหลดแบบสันหลังยาวและไม่มีการโหลดคอลเลกชันที่Sชัดเจน เอนทิตีAมีเอนทิตีใหม่Bที่สร้างขึ้นระหว่างการเริ่มต้นการรวบรวมSเท่านั้น
เมื่อคุณเรียกIsModifed = trueใช้Sเฟรมเวิร์กเอนทิตีของคอลเลกชันพยายามที่จะเพิ่มทั้งสองใหม่Bเข้ามาในการติดตามการเปลี่ยนแปลง แต่มันล้มเหลวเพราะBเอนทิตีใหม่ทั้งสองมีเหมือนId = 0กัน:
// This line tries to add to change tracking two new B entities with the same Id = 0.
// As a result it fails.
db.Entry(a).Collection(x => x.S).IsModified = true;
คุณสามารถเห็นได้จากการติดตามสแต็กที่เฟรมเวิร์กเอนทิตีพยายามเพิ่มBเอนทิตีลงในIdentityMap:
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.IdentityMap`1.ThrowIdentityConflict(InternalEntityEntry entry)
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.IdentityMap`1.Add(TKey key, InternalEntityEntry entry, Boolean updateDuplicate)
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.IdentityMap`1.Add(TKey key, InternalEntityEntry entry)
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.IdentityMap`1.Add(InternalEntityEntry entry)
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.StartTracking(InternalEntityEntry entry)
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.InternalEntityEntry.SetPropertyModified(IProperty property, Boolean changeState, Boolean isModified, Boolean isConceptualNull, Boolean acceptChanges)
at Microsoft.EntityFrameworkCore.ChangeTracking.NavigationEntry.SetFkPropertiesModified(InternalEntityEntry internalEntityEntry, Boolean modified)
at Microsoft.EntityFrameworkCore.ChangeTracking.NavigationEntry.SetFkPropertiesModified(Object relatedEntity, Boolean modified)
at Microsoft.EntityFrameworkCore.ChangeTracking.NavigationEntry.set_IsModified(Boolean value)
และข้อความแสดงข้อผิดพลาดยังบอกว่าไม่สามารถติดตามBเอนทิตีด้วยId = 0เพราะBเอนทิตีอื่นที่มีชื่อเดียวกันIdถูกติดตามอยู่แล้ว
วิธีแก้ไขปัญหานี้
ในการแก้ไขปัญหานี้คุณควรลบรหัสที่สร้างBเอนทิตีเมื่อเริ่มต้นการSรวบรวม:
public ICollection<B> S { get; set; } = new List<B>();
คุณควรเติมSชุดสะสมในสถานที่ที่Aสร้างขึ้นแทน ตัวอย่างเช่น:
db.Add(new A {S = {new B(), new B()}});
หากคุณไม่ใช้การโหลดแบบขี้เกียจคุณควรโหลดSคอลเล็กชันอย่างชัดเจนเพื่อเพิ่มรายการลงในการติดตามการเปลี่ยนแปลง:
// Use eager loading, for example.
A a = db.Set<A>().Include(x => x.S).Single();
db.Entry(a).Collection(x => x.S).IsModified = true;
ทำไมมันไม่เพิ่มแทนที่จะแนบอินสแตนซ์ของ B
ในระยะสั้นพวกเขาจะแนบ insted ของการเพิ่มเพราะพวกเขามีDetachedสถานะ
หลังจากรันโค้ดบรรทัด
var a = db.Set<A>().Single();
กรณีสร้างของกิจการมีสถานะB Detachedสามารถตรวจสอบได้โดยใช้รหัสถัดไป:
Console.WriteLine(db.Entry(a.S[0]).State);
Console.WriteLine(db.Entry(a.S[1]).State);
จากนั้นเมื่อคุณตั้งค่า
db.Entry(a).Collection(x => x.S).IsModified = true;
EF พยายามเพิ่มBเอนทิตีเพื่อเปลี่ยนการติดตาม จากซอร์สโค้ดของEFCoreคุณจะเห็นว่าสิ่งนี้นำเราไปสู่วิธีInternalEntityEntry.SetPropertyModifiedด้วยค่าอาร์กิวเมนต์ถัดไป:
property- หนึ่งในBหน่วยงานของเรา
changeState = true,
isModified = true,
isConceptualNull = false,
acceptChanges = true.
เมธอดนี้ที่มีอาร์กิวเมนต์ดังกล่าวจะเปลี่ยนสถานะของการเข้าDetached Bร่วมModifiedและพยายามเริ่มการติดตามสำหรับพวกเขา (ดูบรรทัดที่490 - 506) เนื่องจากBขณะนี้เอนทิตีมีสถานะModifiedสิ่งนี้ทำให้พวกเขาถูกแนบ (ไม่เพิ่ม)