2013-03-07 12 views
17

Tôi đã đọc một số câu hỏi/câu trả lời phải thực hiện với thông báo lỗi cụ thể này, nhưng tôi không hiểu rõ giải pháp thích hợp.Không thể xác định mối quan hệ giữa hai đối tượng vì chúng được gắn với các đối tượng ObjectContext khác nhau

Tôi đã đọc nhiều lần rằng bạn nên tạo ngữ cảnh EF4, sử dụng nó, sau đó vứt bỏ nó. Trong suốt ứng dụng của tôi, tôi đang tải các thực thể ở đây và ở đó bằng cách sử dụng các đối tượng ngữ cảnh khác nhau, và sau đó cuối cùng muốn kết hợp các thực thể lại với nhau.

Tôi đã tạo ứng dụng bảng điều khiển đơn giản dễ gây ra lỗi. Mô hình rất đơn giản được lập sơ đồ theo sau là mã.

Làm cách nào để có hai thực thể khác nhau chia sẻ cùng một ngữ cảnh? Tôi có thực sự phải tạo một bối cảnh mới, tải lại hai thực thể (mặc dù tôi đã có chúng), chỉ cần kết hợp chúng và lưu?

Nếu tôi chỉ đơn giản là bỏ lỡ câu hỏi/câu trả lời thích hợp đã tồn tại, vui lòng chỉ cho tôi đúng địa điểm.

Simple EF4 Model diagram

internal class Program { 

    private static void Main(string[] args) { 
     DeleteAllEntities(); 
     CreateInitialEntities(); 
     Owner o = LoadOwner(); 
     Child c = LoadChild(); 
     AssociateAndSave(o, c); 
    } 

    private static void AssociateAndSave(Owner o, Child c) { 
     using (var context = new ModelEntities()) { 
      // Exception occurs on the following line. 
      o.Children.Add(c); 
      context.Attach(o); 
      context.SaveChanges(); 
     } 
    } 

    private static Owner LoadOwner() { 
     using (var context = new ModelEntities()) { 
      return ((from o in context.Owners 
        select o).First()); 
     } 
    } 

    private static Child LoadChild() { 
     using (var context = new ModelEntities()) { 
      return ((from c in context.Children 
        select c).First()); 
     } 
    } 

    private static void CreateInitialEntities() { 
     using (var context = new ModelEntities()) { 
      Owner owner = new Owner(); 
      Child child = new Child(); 
      context.Owners.AddObject(owner); 
      context.Children.AddObject(child); 
      context.SaveChanges(); 
     } 
    } 

    private static void DeleteAllEntities() { 
     using (var context = new ModelEntities()) { 
      List<Child> children = (from c in context.Children 
            select c).ToList(); 
      foreach (var c in children) 
       context.Children.DeleteObject(c); 
      List<Owner> owners = (from o in context.Owners 
            select o).ToList(); 
      foreach (var o in owners) 
       context.Owners.DeleteObject(o); 
      context.SaveChanges(); 
     } 
    } 
} 
+1

Hmm .. Chỉ cần gọi 'context.Attach (o); context.Attach (c); 'ngăn chặn lỗi. Nhưng khi tôi thử điều này trong ứng dụng thực tế của mình, tôi nhận được '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' mặc dù các thực thể được nạp theo cách tương tự với ví dụ này ... – Steve

Trả lời

15

Bạn sẽ nhận được tham chiếu đến đối tượng con và chủ sở hữu trong cùng một ngữ cảnh. Một cách tiếp cận cho việc này là lấy các id và sau đó chuyển chúng thành các tham số cho phương thức AssociateAndSave(int oId, int cId).

Sau đó, bạn truy xuất tham chiếu đến các đối tượng trong cùng một ngữ cảnh và bạn thực hiện tệp đính kèm.

private static void Main(string[] args) 
{ 
    DeleteAllEntities(); 
    CreateInitialEntities(); 
    int oId = LoadOwnerId(); 
    int cId = LoadChildId(); 
    AssociateAndSave(oId, cId); 
} 

private static int LoadOwnerId() 
{ 
    using (var context = new ModelEntities()) 
    { 
     return (from o in context.Owners 
       select o).First().Id; 
    } 
} 

private static int LoadChildId() 
{ 
    using (var context = new ModelEntities()) 
    { 
     return (from c in context.Children 
       select c).First().Id; 
    } 
} 

private static void AssociateAndSave(int oId, int cId) 
{ 
    using (var context = new ModelEntities()) 
    { 
     var owner = (from o in context.Owners 
         select o).FirstOrDefault(o => o.ID == oId); 
     var child = (from o in context.Children 
         select o).FirstOrDefault(c => c.ID == cId); 

     owner.Children.Add(child); 
     context.Attach(owner); 
     context.SaveChanges(); 
    } 
} 
+1

Đây là một giải pháp tốt, nó sẽ không hoạt động trong EF 4.0 – Freeman

+2

Tôi thực sự đã bắt đầu thực hiện điều này, nhưng tôi thấy nó hơi lãng phí khi truy vấn cơ sở dữ liệu cho thực thể mà tôi đã tải trước đó từ cơ sở dữ liệu. – Steve

+1

Tôi quyết định đi với phần của cả hai câu trả lời về câu hỏi này, nhưng tiếc là tôi không thể đánh dấu cả hai như là câu trả lời .. :) Bất cứ ai tìm kiếm mà thấy điều này cũng nên xem http://stackoverflow.com/a/5695009/425871 . – Steve

3

Bạn không thể xóa các đối tượng từ một bối cảnh, với một thể hiện của bối cảnh khác, vì mỗi bối cảnh theo dõi đối tượng của nó riêng biệt.

Một ý tưởng sẽ là cung cấp cho từng phương pháp của bạn một tham số của loại ngữ cảnh để các hoạt động của bạn được thực hiện trong cùng một ngữ cảnh.

EX:

private static void CrudOperationExample(DBContext contextInstane) 
{ 
    //perform crud operations. 
} 

tôi bạn muốn làm điều đó độc đáo hơn, tôi đề nghị bạn tìm kiếm dependency injection, vì nó là rất nhiều hữu ích trong việc ORM của.

+1

Tôi đã sử dụng để giữ một ngữ cảnh còn sống để tôi có thể làm điều này, nhưng một lần nữa và một lần nữa tôi đọc rằng bạn nên chỉ giữ bối cảnh trong một thời gian rất ngắn, khi bạn cần nó. Có thể trong ứng dụng của tôi rằng bối cảnh có thể được mở trong 5 phút, hoặc thậm chí một giờ. Nó sẽ thực sự phụ thuộc vào bao lâu người dùng giữ cửa sổ mở. – Steve

+1

Ngoài ra - bạn nói mỗi bối cảnh theo dõi riêng các thực thể của nó, nhưng bối cảnh đã được xử lý (tất cả các hoạt động của tôi là trong các khối 'sử dụng'). – Steve