เหตุผลของข้อผิดพลาดในรหัสที่ให้ไว้มีดังนี้
เมื่อคุณได้รับการสร้างขึ้นนิติบุคคลA
จากฐานข้อมูลสถานที่ให้บริการจะเริ่มต้นด้วยการเก็บที่มีสองระเบียนใหม่S
ของแต่ละใหม่นี้หน่วยงานที่มีค่าเท่ากับB
Id
B
0
// 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
สิ่งนี้ทำให้พวกเขาถูกแนบ (ไม่เพิ่ม)