5

Tôi đang gặp sự cố với mã Entity Framework-đầu tiên trong MVC3. Tôi đang nhấn ngoại lệ này:Khuôn khổ thực thể Mã-Đầu tiên: "ObjectStateManager không thể theo dõi nhiều đối tượng có cùng khóa."

Một đối tượng có cùng khóa đã tồn tại trong ObjectStateManager. ObjectStateManager không thể theo dõi nhiều đối tượng với cùng một khóa .

Điều này được giải quyết nhiều lần trên SO, nhưng tôi đang gặp sự cố khi sử dụng bất kỳ giải pháp được đề xuất nào trong trường hợp của tôi.

Đây là một mẫu mã:

FestORM.SaleMethod method = new FestORM.SaleMethod 
{ 
    Id = 2, 
    Name = "Test Sale Method" 
}; 
FestContext context = new FestContext(); 

//everything works without this line: 
string thisQueryWillMessThingsUp = 
    context.SaleMethods.Where(m => m.Id == 2).Single().Name; 

context.Entry(method).State = System.Data.EntityState.Modified; 
context.SaveChanges(); 

chỉnh sửa để làm rõ: Tôi đang cố gắng để cập nhật một đối tượng mà đã tồn tại trong cơ sở dữ liệu.

Mọi thứ hoạt động tốt mà không có truy vấn được ghi chú trong mã. Trong ứng dụng của tôi, bộ điều khiển của tôi đang khởi tạo ngữ cảnh và ngữ cảnh tương tự được chuyển đến một số kho lưu trữ được sử dụng bởi bộ điều khiển - vì vậy tôi không thể sử dụng một ngữ cảnh khác cho thao tác truy vấn ban đầu. Tôi đã cố gắng để loại bỏ các thực thể từ đang được theo dõi trong ObjectStateManager, nhưng tôi dường như không thể nhận được bất cứ nơi nào với điều đó một trong hai. Tôi đang cố gắng tìm ra một giải pháp sẽ làm việc cho cả hai điều kiện: đôi khi tôi sẽ cập nhật một đối tượng được theo dõi bởi ObjectStateManager, và đôi khi nó sẽ xảy ra chưa được theo dõi.

FWIW, chức năng kho thật của tôi trông như thế này, giống như đoạn code trên:

public void Update(T entity) 
{ 
    //works ONLY when entity is not tracked by ObjectStateManager 
    _context.Entry(entity).State = System.Data.EntityState.Modified; 
} 

public void SaveChanges() 
{ 
    _context.SaveChanges(); 
} 

Bất kỳ ý tưởng? Tôi đã chiến đấu này quá lâu ...

Trả lời

12

Vấn đề là truy vấn này

string thisQueryWillMessThingsUp = 
    context.SaleMethods.Where(m => m.Id == 2).Single().Name; 

mang một thể hiện của thực thể SaleMethod vào bối cảnh và sau đó mã này

context.Entry(method).State = System.Data.EntityState.Modified; 

gắn một trường hợp khác nhau với bối cảnh.Cả hai trường hợp đều có cùng khóa chính, vì vậy EF nghĩ rằng bạn đang cố gắng đính kèm hai thực thể khác nhau với cùng một khóa vào ngữ cảnh. Nó không biết rằng cả hai đều được cho là cùng một thực thể.

Nếu vì một lý do nào, bạn chỉ cần truy vấn tên, nhưng không muốn để thực sự mang lại những thực thể đầy đủ vào bối cảnh, sau đó bạn có thể làm điều này:

string thisQueryWillMessThingsUp =   
    context.SaleMethods.Where(m => m.Id == 2).AsNoTracking().Single().Name; 

Nếu những gì bạn đang buộc phải làm là cập nhật một thực thể tồn tại và bạn có giá trị cho tất cả các thuộc tính ánh xạ của thực thể đó, thì điều đơn giản nhất để làm là để không chạy các truy vấn và chỉ sử dụng:

context.Entry(method).State = System.Data.EntityState.Modified; 

Nếu bạn không muốn cập nhật tất cả các thuộc tính, có thể do bạn không có giá trị cho tất cả thuộc tính, sau đó truy vấn cho thực thể và thiết lập các thuộc tính trên nó trước khi gọi SaveChanges là một cách tiếp cận có thể chấp nhận được. Có một số cách để làm điều này tùy thuộc vào yêu cầu chính xác của bạn. Một cách là sử dụng phương pháp tài sản, một cái gì đó giống như vậy:

var salesMethod = context.SaleMethods.Find(2); // Basically equivalent to your query 
context.Entry(salesMethod).Property(e => e.Name).CurrentValue = newName; 
context.Entry(salesMethod).Property(e => e.SomeOtherProp).CurrentValue = newOtherValue; 
context.SaveChanges(); 

Những bài đăng trên blog chứa một số thông tin bổ sung mà có thể hữu ích:

http://blogs.msdn.com/b/adonet/archive/2011/01/29/using-dbcontext-in-ef-feature-ctp5-part-4-add-attach-and-entity-states.aspx

http://blogs.msdn.com/b/adonet/archive/2011/01/30/using-dbcontext-in-ef-feature-ctp5-part-5-working-with-property-values.aspx

+0

Cảm ơn. (Nó sẽ không cho phép tôi bỏ phiếu cho câu trả lời của bạn vì tôi mới.) Vấn đề với điều này là tôi không biết khi nào tôi đang thực hiện truy vấn trước một bản cập nhật trong tương lai. Truy vấn có thể được chôn ở đâu đó trong lớp dịch vụ, và cuối cùng, khi tôi thực hiện cập nhật trên cùng một thực thể được truy vấn, tôi đã gặp lỗi. Có cách nào để tôi có thể loại bỏ đối tượng khỏi theo dõi ngữ cảnh hay nhận được đối tượng đang được theo dõi bởi ngữ cảnh và giao tiếp với ngữ cảnh mà tôi thực sự đang cập nhật đối tượng này đang được đề cập không? Tôi đã nghiên cứu một số theo hướng này, nhưng chưa có may mắn. – Josh

+0

Nó phụ thuộc vào ý định của truy vấn. Nếu truy vấn nhằm đưa một thực thể vào trong ngữ cảnh để nó có thể được cập nhật và lưu lại, đó là bình thường, thì bạn nên thiết kế cho điều đó, có thể bằng cách đảm bảo trước khi bạn lưu thực thể đó. được đưa vào trước. Nếu mục đích của truy vấn là lấy một số thuộc tính của thực thể từ cơ sở dữ liệu vì lý do nào đó mà không đưa thực thể vào ngữ cảnh, thì hãy sử dụng AsNoTracking trên truy vấn đó như được hiển thị ở trên. –

+0

Để thêm vào nhận xét trước đó của tôi, nếu bạn muốn đảm bảo rằng thực thể đã được đưa vào, sau đó sử dụng phương thức Tìm trước khi cập nhật các thuộc tính trong thực thể và lưu. Bạn cũng có thể sử dụng truy vấn thay vì Tìm - theo mặc định truy vấn sẽ chỉ đưa vào thực thể nếu nó chưa có trong ngữ cảnh. Nhưng Find hiệu quả hơn. –

1

Câu trả lời rõ ràng sẽ là của bạn không thực sự tiết kiệm đối tượng phương pháp để các cơ sở dữ liệu trước khi bạn gọi:

//everything works without this line: 
string thisQueryWillMessThingsUp = context.SaleMethods.Where(m => m.Id == 2).Single().Name; 

Tuy nhiên, tôi nghĩ rằng có lẽ đây là chỉ là một chút mã bạn bỏ ra. Điều gì sẽ xảy ra nếu bạn làm cho các thực thể của bạn kế thừa từ một lớp trừu tượng.

public abstract class BaseClass 
{ 
    public int Id { get; set; } 
} 

Sau đó cập nhật Repository của bạn để

public class Repository<T> where T : BaseClass 
{ 
..... 
    public void Update(T entity) 
    {   
     _context.Entry(entity).State = entity.Id == 0 ? System.Data.EntityState.Added : System.Data.EntityState.Modified; 
    } 
} 

Ngoài ra bạn có thể muốn không được thiết lập ID của SaleMethod của bạn và để cho nó được tạo ra bởi các cơ sở dữ liệu. Vấn đề cũng có thể là do đối tượng SaleMethod trong cơ sở dữ liệu có Id là 2 và sau đó bạn cố gắng thêm đối tượng SaleMethod khác với Id 2. Lỗi bạn thấy bắt nguồn từ việc cố gắng thêm đối tượng SaleMethod khác có ID là 2 vào ObjectStateManager.

+0

Cảm ơn đã phản ứng. Có, các ID được tạo ra bởi cơ sở dữ liệu cho chắc chắn. Đây là một hoạt động ** update ** cụ thể. Tôi đã rời khỏi nơi mà đối tượng được lưu ban đầu (có thể đã được lưu ban đầu bởi một đoạn mã khác năm trước, v.v.). Vấn đề là, bản cập nhật hoạt động tốt, miễn là bạn không thực hiện truy vấn trước. Việc thực hiện truy vấn dường như thêm nó vào theo dõi của ObjectStateManager, tại thời điểm đó, bản cập nhật của tôi sẽ được rình rập. – Josh

Các vấn đề liên quan