Tôi đang sử dụng Khuôn khổ thực thể 4.3.1 trong một dự án, sử dụng mã đầu tiên và API DbContext. Ứng dụng của tôi là một ứng dụng n-tier nơi các đối tượng bị ngắt kết nối có thể đến từ một ứng dụng khách. Tôi đang sử dụng SQL Server 2008 R2 nhưng sẽ được chuyển đến SQL Azure sớm. Tôi đang gặp một vấn đề mà tôi không thể giải quyết được.Lưu các đối tượng đơn lẻ với mã Khuôn khổ thực thể đầu tiên
Hãy tưởng tượng tôi có một vài lớp:
class A {
// Random stuff here
}
class B {
// Random stuff here
public A MyA { get; set; }
}
class C {
// Random stuff here
public A MyA { get; set; }
}
Theo mặc định, EF hoạt động trên đồ thị đối tượng. Ví dụ, nếu tôi có một thể hiện của B đóng gói một thể hiện của A và tôi gọi myDbSet.Add(myB);
, nó cũng sẽ đánh dấu cá thể của A khi được thêm vào (giả sử nó chưa được theo dõi).
Tôi có một kịch bản trong ứng dụng của mình, nơi tôi cần phải rõ ràng về những đối tượng nào được lưu vào cơ sở dữ liệu, thay vì theo dõi toàn bộ biểu đồ đối tượng. Trình tự thao tác như sau:
A myA = new A(); // Represents something already in DB that doesn't need to be udpated.
C myC = new C() { // Represents something already in DB that DOES need to be updated.
A = myA;
}
B myB0 = new B() { // Not yet in DB.
A = myA;
}
B myB1 = new B() { // Not yet in DB.
A = myA;
}
myDbSetC.Attach(myC);
context.Entry(myC).State = Modified;
myDbSetB.Add(myB0); // Tries to track myA with a state of Added
myDbSetB.Add(myB1);
context.SaveChanges();
Tại thời điểm này tôi nhận được một lỗi nói AcceptChanges cannot continue because the object's key values conflict with another object in the ObjectStateManager. Make sure that the key values are unique before calling AcceptChanges.
Tôi tin rằng điều này xảy ra bởi vì gọi thêm vào myB0 đánh dấu sự thể hiện của A như là gia tăng, xung đột với trường hợp của A đã đang được theo dõi.
Lý tưởng nhất là tôi có thể làm điều gì đó như gọi số myDbSet.AddOnly(myB)
, nhưng rõ ràng là chúng tôi không có tùy chọn đó.
Tôi đã thử nhiều cách giải quyết:
Cố gắng # 1: Trước tiên, tôi cố gắng tạo ra một phương pháp helper để ngăn chặn MYA từ được thêm một lần thứ hai.
private void MarkGraphAsUnchanged<TEntity>(TEntity entity) where TEntity : class {
DbEntityEntry entryForThis = this.context.Entry<TEntity>(entity);
IEnumerable<DbEntityEntry> entriesItWantsToChange = this.context.ChangeTracker.Entries().Distinct();
foreach (DbEntityEntry entry in entriesItWantsToChange) {
if (!entryForThis.Equals(entry)) {
entry.State = System.Data.EntityState.Unchanged;
}
}
}
...
myDbSetB.Add(myB0);
MarkGraphAsUnchanged(myB0);
Trong khi điều này giải quyết được sự cố khi cố gắng thêm myA, nó vẫn gây ra các vi phạm chính trong ObjectStateManager.
Cố gắng # 2: Tôi đã thử thực hiện tương tự như trên, nhưng đặt trạng thái thành Đã tách rời thay vì Không thay đổi. Điều này làm việc để tiết kiệm, nhưng nó nhấn mạnh vào việc thiết lập myB0.A = null
, trong đó có tác dụng phụ khác trong mã của tôi.
Cố gắng # 3: Tôi đã sử dụng một TransactionScope xung quanh toàn bộ DbContext của mình. Tuy nhiên, ngay cả khi gọi SaveChanges()
giữa mỗi Attach()
và Add()
, trình theo dõi thay đổi không xóa các mục được theo dõi của nó để tôi có cùng một vấn đề như trong nỗ lựC# 1.
Cố gắng # 4: tôi tiếp tục với TransactionScope, ngoại trừ tôi đã sử dụng một/DAO mô hình kho và trong nội bộ tạo ra một DbContext mới và gọi SaveChanges()
cho từng hoạt động riêng biệt tôi làm. Trong trường hợp này, tôi gặp lỗi 'Lưu trữ cập nhật, chèn hoặc xóa tuyên bố đã ảnh hưởng đến số lượng hàng không mong muốn'. Khi sử dụng SQL Profiler, tôi thấy rằng khi gọi SaveChanges()
trên hoạt động thứ hai mà tôi đã thực hiện (Add()
) đầu tiên, nó thực sự gửi UPDATE
SQL đến cơ sở dữ liệu từ hoạt động đầu tiên lần thứ hai - nhưng không thay đổi bất kỳ hàng nào. Điều này cảm thấy giống như một lỗi trong Entity Framework với tôi.
Cố gắng # 5: Thay vì sử dụng TransactionScope, tôi quyết định chỉ sử dụng DbTransaction. Tôi vẫn tạo ra nhiều bối cảnh nhưng vượt qua EntityConnection được dựng sẵn cho mỗi ngữ cảnh mới khi nó được tạo (bằng caching và mở thủ công EntityConnection được xây dựng bởi bối cảnh đầu tiên). Tuy nhiên, khi tôi làm điều này, ngữ cảnh thứ hai chạy một trình khởi tạo mà tôi đã định nghĩa, mặc dù nó sẽ chạy khi ứng dụng khởi động lần đầu tiên. Trong môi trường dev, tôi có một số dữ liệu thử nghiệm, và nó thực sự đã hẹn hò với một khóa cơ sở dữ liệu trên một bảng của tôi đã sửa đổi Attach()
đầu tiên (nhưng vẫn bị khóa do giao dịch vẫn đang mở).
Trợ giúp !! Tôi đã thử tất cả mọi thứ tôi có thể nghĩ đến, và ngắn hoàn toàn tái cấu trúc ứng dụng của tôi để không sử dụng các thuộc tính điều hướng hoặc sử dụng DAO được xây dựng theo cách thủ công để làm các câu lệnh INSERT, UPDATE và DELETE, tôi thua lỗ. Dường như phải có một cách để có được những lợi ích của Khung thực thể đối với ánh xạ O/R nhưng vẫn kiểm soát thủ công các hoạt động trong một giao dịch!
Bạn đã thử gắn myA chưa? Bạn sẽ cần phải làm điều này trước khi đính kèm bất cứ điều gì khác. – cadrell0