2010-05-07 30 views
15

Tôi có mối quan hệ nhiều-nhiều giữa IssuesScopes trong ngữ cảnh EF của tôi. Trong ASP.NET MVC, tôi đưa ra một hình thức chỉnh sửa cho phép người dùng chỉnh sửa một vấn đề cụ thể. Ở dưới cùng của biểu mẫu, là danh sách các hộp kiểm cho phép họ chọn phạm vi áp dụng cho vấn đề này. Khi chỉnh sửa một vấn đề, nó có khả năng sẽ luôn luôn có một số phạm vi liên kết với nó đã - những hộp này sẽ được kiểm tra rồi. Tuy nhiên, người dùng có cơ hội kiểm tra nhiều phạm vi hơn hoặc xóa một số phạm vi được kiểm tra hiện tại. Mã của tôi nhìn một cái gì đó như thế này để tiết kiệm chỉ Issue:Thực thể cập nhật đối tượng khung cùng với các thực thể con (thêm/cập nhật nếu cần)

  using (var edmx = new MayflyEntities()) 
      { 
       Issue issue = new Issue { IssueID = id, TSColumn = formIssue.TSColumn }; 
       edmx.Issues.Attach(issue); 

       UpdateModel(issue); 

       if (ModelState.IsValid) 
       { 
        //if (edmx.SaveChanges() != 1) throw new Exception("Unknown error. Please try again."); 
        edmx.SaveChanges(); 
        TempData["message"] = string.Format("Issue #{0} successfully modified.", id); 
       } 
      } 

Vì vậy, khi tôi cố gắng thêm trong logic để lưu các phạm vi liên quan, tôi đã cố gắng một vài điều, nhưng cuối cùng, đây là những gì làm cho ý nghĩa nhất đối với tôi:

  using (var edmx = new MayflyEntities()) 
      { 
       Issue issue = new Issue { IssueID = id, TSColumn = formIssue.TSColumn }; 
       edmx.Issues.Attach(issue); 

       UpdateModel(issue); 

       foreach (int scopeID in formIssue.ScopeIDs) 
       { 
        var thisScope = new Scope { ID = scopeID }; 
        edmx.Scopes.Attach(thisScope); 
        thisScope.ProjectID = formIssue.ProjectID; 
        if (issue.Scopes.Contains(thisScope)) 
        { 
         issue.Scopes.Attach(thisScope); //the scope already exists 
        } 
        else 
        { 
         issue.Scopes.Add(thisScope); // the scope needs to be added 
        } 
       } 

       if (ModelState.IsValid) 
       { 
        //if (edmx.SaveChanges() != 1) throw new Exception("Unknown error. Please try again."); 
        edmx.SaveChanges(); 
        TempData["message"] = string.Format("Issue #{0} successfully modified.", id); 
       } 
      } 

Nhưng, thật không may, mà chỉ ném ngoại lệ sau đây:

An object with the same key already exists in the ObjectStateManager. The ObjectStateManager cannot track multiple objects with the same key. 

tôi đang làm gì sai?

Trả lời

12

Sơ khai thường chỉ có hiệu lực cho các mối quan hệ 1-*. *-* các mối quan hệ giới thiệu một loạt các thách thức khác nhau.

Cụ thể là khi bạn đính kèm cả hai đầu - không giống như 1-* - bạn vẫn không biết liệu mối quan hệ đã tồn tại hay chưa.

Vì vậy, đó có nghĩa là mã này:

if (issue.Scopes.Contains(thisScope)) 

có lẽ sẽ trả về false mỗi lần.

edmx.Issues.Attach(issue); 
UpdateModel(issue); 
// or ctx.LoadProperty(issue, "Scopes") if it is a POCO class. 
issue.Scopes.Load(); // hit the database to load the current state. 

Bây giờ bạn cần phải tìm hiểu những gì bạn cần phải thêm & loại bỏ từ phát hành:

Những gì tôi sẽ làm điều này là.Phạm vi. Bạn có thể thực hiện việc này bằng cách so sánh dựa trên ID.

ví dụ: nếu bạn có một bộ Scope ID bạn muốn có liên quan đến các vấn đề (relatedScopes)

Sau đó, mã này làm việc ra những gì để thêm và xóa cái nào.

int[] toAdd = relatedScopes.Except(issue.Scopes.Select(s => s.ID)).ToArray(); 
int[] toRemove = issue.Scopes.Select(s => s.ID).Except(relatedScopes).ToArray(); 

Bây giờ cho TOADD bạn làm điều này:

foreach(int id in toAdd) 
{ 
    var scope = new Scope{Id = id}; 
    edmx.Scopes.Attach(scope); 
    issue.Scopes.Add(scope); 
} 

Và đối với mỗi phạm vi bạn cần phải loại bỏ

foreach(int id in toRemove) 
{ 
    issue.Scopes.Remove(issue.Scopes.Single(s => s.ID == id)); 
} 

Đến nay các mối quan hệ chính xác nên được hình thành.

Hope this helps

Alex

Microsoft

+1

Thật hoàn hảo! Và nhìn vào hồ sơ SQL, nó chỉ là một "thêm" cuộc gọi đến DB cho .Load(), nhưng cách sạch hơn thêm/loại bỏ hơn cách tôi sử dụng để làm điều đó bằng tay với thủ tục được lưu trữ. Cảm ơn! – Jorin

+0

Câu trả lời hay của James. Đây là một vấn đề phổ biến trong nhiều ứng dụng (thêm/xóa thẻ hiệu quả). Không phải là thời gian cho một chức năng cấp hệ thống để làm điều này mà không có một màn hình đầy mã? Một cái gì đó như * issue.Scopes.ReplaceWith (myScopes); * – TFD

+0

@TFD vâng tôi nghe thấy bạn. Nếu không, đội EF có rất nhiều trên đĩa của nó, một phương pháp mở rộng đơn giản sẽ làm các trick mặc dù phải không? –

0

Cảnh báo, đây chỉ là từ đỉnh đầu của tôi, tôi không thử.

Tôi không nghĩ rằng bạn có thể đặt khóa ngoài giống như bạn làm với ProjectID.

bạn cần truy xuất dự án và thêm dự án vào phạm vi. Thuộc tính điều hướng dự án. EF sẽ chăm sóc các mối quan hệ khi bạn chèn nó vào.

Một lần nữa, tôi đã không thử và có thể sai, nhưng đáng để thử. Có lẽ điều này sẽ giúp bạn trên đường đi ..

+0

không, không nghĩ vậy. Tôi vừa thay đổi dòng 'thisScope.ProjectID = formIssue.ProjectID;' thành 'thisScope.Project = issue.Project;' và vẫn gặp lỗi tương tự. đó có phải ý của bạn? – Jorin

+0

Ok, tôi sẽ tự mình thử và đăng mã mà tôi đưa ra nếu bạn không tự mình giải quyết vấn đề. –

+0

nope, tôi đã kiệt sức bản thân mình cố gắng lựa chọn khác nhau. Trong trường hợp thử nghiệm của tôi, tôi có 3 phạm vi, 2 trong số đó đã được liên kết với vấn đề này. Tôi làm cho nó vì vậy tất cả 3 được kiểm tra trên biểu mẫu của tôi, vì vậy 2 hiện có nên được cập nhật và 1 nên được liên kết với vấn đề này. Tôi nhận được kết quả khác nhau dựa trên những gì tôi cố gắng, nhưng không có gì làm những gì nó phải. Kỳ lạ thay, với phương thức '.Add', đôi khi nó cố gắng thêm một phạm vi mới vào cơ sở dữ liệu thay vì chỉ" thêm "một liên kết mới với vấn đề này, nhưng tôi không thể hiểu làm thế nào để nó hoạt động chính xác . – Jorin

0

** Tuyên bố miễn trừ trách nhiệm: Tôi tương đối mới đối với EF, lấy câu trả lời của tôi bằng một xô muối.

Nếu đây là biểu mẫu chỉnh sửa, tôi nghĩ đối tượng vấn đề của bạn không phải là vấn đề "mới", nó sẽ giải quyết vấn đề ra khỏi kho dữ liệu. Theo như tôi có thể thấy bằng cách thực hiện:

Issue issue = new Issue { IssueID = id, TSColumn = formIssue.TSColumn }; edmx.Issues.Attach(issue); bạn đang tạo một vấn đề mới có hiệu lực với Id của tài khoản bạn đang cố chỉnh sửa.

Một lần nữa, tôi ở đây đang tìm cách để hiểu rõ về EF. Đôi khi tôi bỏ lỡ các câu lệnh SQL của tôi.

+0

Cảm ơn, nhưng không. Như tôi đã nói, phần Vấn đề của nó hoạt động tốt, chỉ là các thực thể con mà tôi dường như không thể cập nhật. "mới" bạn thấy chỉ có một cách để tạo ra một thực thể sơ khai hơn là sử dụng EntityKey. Xem bài viết này: http://blogs.msdn.com/alexj/archive/2009/06/19/tip-26-how-to-avoid-database-queries-using-stub-entities.aspx – Jorin

+1

Ooohhh đẹp !! Tôi sẽ phải cố gắng điều này thực thể sơ khai, nó sẽ cắt giảm rất nhiều truy vấn không cần thiết. Cảm ơn bạn! –

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