Lý do của lỗi trong mã được cung cấp như sau.
Khi bạn nhận được thực thể được tạo A
từ cơ sở dữ liệu, thuộc tính của nó S
được khởi tạo với một bộ sưu tập chứa hai bản ghi mới B
. Id
của mỗi B
thực thể mới này là bằng 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 {} };
Sau khi thực hiện dòng var a = db.Set<A>().Single()
thu thập mã S
của thực thể A
không chứa B
thực thể từ cơ sở dữ liệu, vì DbContext Db
không sử dụng tải lười biếng và không có tải rõ ràng của bộ sưu tập S
. Thực thể A
chỉ chứa các B
thực thể mới được tạo trong quá trình khởi tạo bộ sưu tập S
.
Khi bạn gọi IsModifed = true
cho S
khung thực thể bộ sưu tập, hãy cố gắng thêm hai quyền mới đó B
vào theo dõi thay đổi. Nhưng nó thất bại vì cả hai B
thực thể mới đều giống nhau 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ạn có thể thấy từ theo dõi ngăn xếp mà khung thực thể cố gắng thêm B
các thực thể vào 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)
Và thông báo lỗi cũng cho biết rằng nó không thể theo dõi B
thực thể Id = 0
vì một B
thực thể khác có cùng thực thể Id
đã được theo dõi.
Làm thế nào để giải quyết vấn đề này.
Để giải quyết vấn đề này, bạn nên xóa mã tạo B
các thực thể khi khởi tạo S
bộ sưu tập:
public ICollection<B> S { get; set; } = new List<B>();
Thay vào đó, bạn nên điền vào S
bộ sưu tập tại nơi A
được tạo. Ví dụ:
db.Add(new A {S = {new B(), new B()}});
Nếu bạn không sử dụng tải lười biếng, bạn nên tải S
bộ sưu tập một cách rõ ràng để thêm các mục của nó vào theo dõi thay đổi:
// Use eager loading, for example.
A a = db.Set<A>().Include(x => x.S).Single();
db.Entry(a).Collection(x => x.S).IsModified = true;
Tại sao nó không thêm thay vì đính kèm các thể hiện của B?
Nói tóm lại , chúng được đính kèm khi được thêm vào vì chúng có Detached
trạng thái.
Sau khi thực hiện dòng mã
var a = db.Set<A>().Single();
tạo ra các thực thể B
có trạng thái Detached
. Nó có thể được xác minh bằng cách sử dụng mã tiếp theo:
Console.WriteLine(db.Entry(a.S[0]).State);
Console.WriteLine(db.Entry(a.S[1]).State);
Sau đó khi bạn đặt
db.Entry(a).Collection(x => x.S).IsModified = true;
EF cố gắng thêm B
các thực thể để thay đổi theo dõi. Từ mã nguồn của EFCore, bạn có thể thấy rằng điều này dẫn chúng ta đến phương thức InternalEntityEntry.SetPropertyModified với các giá trị đối số tiếp theo:
property
- một trong những B
thực thể của chúng tôi ,
changeState = true
,
isModified = true
,
isConceptualNull = false
,
acceptChanges = true
.
Phương thức này với các đối số như vậy sẽ thay đổi trạng thái của các mục Detached
B
nhập Modified
và sau đó cố gắng bắt đầu theo dõi chúng (xem các dòng 490 - 506). Bởi vì B
các thực thể hiện có trạng thái Modified
này dẫn đến chúng được đính kèm (không được thêm vào).