2011-11-01 27 views
27

Tôi có một ứng dụng mà bạn có thể tạo một loại sản phẩm mới và thêm vào sản phẩm đó một số thành phần. Sản phẩm và nguyên liệu là cả hai thực thể được lưu trong cơ sở dữ liệu. Thực thể sản phẩm có một tập hợp các thực thể thành phần.Thực thể 4.1 Cập nhật thực thể cha mẹ hiện tại với các thực thể con mới

(phiên bản đơn giản)

public class Product 
    Public Sub New() 
    Me.Ingredients = New List(Of Ingredient)() 
    End Sub 

    Property Ingredients as ICollection(Of Ingredient) 
end class 

Khi tôi lưu các sản phẩm cho lần đầu tiên, mọi việc suôn sẻ: Tôi chỉ cần thêm nó vào bối cảnh và gọi SaveChanges.

myDataContext.Products.Add(product) 
myDataContext.SaveChanges() 

Cả sản phẩm (bố mẹ) và thành phần (trẻ em) được lưu và liên kết với nhau. Tất cả đều tốt.

Tuy nhiên khi tôi thêm/xóa một thành phần vào sản phẩm hiện có, tôi bắt đầu gặp sự cố. Lần đầu tiên tôi xóa bộ sưu tập thành phần hiện có trong thực thể sản phẩm và sau đó thêm danh sách các thành phần được cập nhật lại (tôi không sử dụng lại thành phần thêm vào thời điểm này). Sau đó tôi thay đổi trạng thái của thực thể sản phẩm thành sửa đổi và gọi savechang. Tuy nhiên, trên trạng thái thay đổi, hãy nhận ngoại lệ "Một đối tượng có cùng khóa đã tồn tại trong ObjectStateManager".

myDataContext.Entry(product).State = EntityState.Modified 

Sau khi "một số" tìm kiếm tôi đã tìm ra rằng vấn đề là tất cả các thành phần có một khóa chính từ 0 (như họ không được thêm vào nào) và khi bạn thay đổi trạng thái của thực thể cha mẹ (sản phẩm), tất cả các thực thể con (thành phần) được gắn vào ngữ cảnh bằng khóa 0, gây ra vấn đề vì các khóa không còn là duy nhất nữa.

Tôi đã tìm kiếm giải pháp nhưng không thể tìm ra cách giải quyết vấn đề này. Tôi đã thử thêm các thành phần vào bối cảnh trước khi thay đổi trạng thái, nhưng sau đó liên kết giữa sản phẩm và thành phần bị thiếu ... Làm cách nào để cập nhật thực thể cha mẹ hiện có với các thực thể con chưa được thêm mới?

Tôi sử dụng Khung pháp nhân 4.1 và Mã đầu tiên.

Hy vọng bạn có thể giúp tôi!

Trả lời

36

Lần đầu tiên tôi xóa bộ sưu tập thành phần hiện có trong sản phẩm thực thể và thêm danh sách cập nhật thành phần một lần nữa.

Vâng, đây là loại tấn công bạo lực để cập nhật bộ sưu tập con. EF không có bất kỳ phép thuật nào để cập nhật trẻ em - điều đó có nghĩa là: thêm trẻ em mới, xóa trẻ đã xóa, cập nhật trẻ em hiện có - bằng cách chỉ đặt trạng thái của phụ huynh là Modified. Về cơ bản thủ tục này buộc bạn phải xóa các trẻ em cũ cũng từ cơ sở dữ liệu và chèn một hình mới, như vậy:

// product is the detached product with the detached new children collection 
using (var context = new MyContext()) 
{ 
    var productInDb = context.Products.Include(p => p.Ingredients) 
     .Single(p => p.Id == product.Id); 

    // Update scalar/complex properties of parent 
    context.Entry(productInDb).CurrentValues.SetValues(product); 

    foreach (var ingredient in productInDb.Ingredients.ToList()) 
     context.Ingredients.Remove(ingredient); 

    productInDb.Ingredients.Clear(); // not necessary probably 

    foreach (var ingredient in product.Ingredients) 
     productInDb.Ingredients.Add(ingredient); 

    context.SaveChanges(); 
} 

Thủ tục tốt hơn là để cập nhật các bộ sưu tập trẻ em trong bộ nhớ mà không xóa tất cả trẻ em trong cơ sở dữ liệu:

// product is the detached product with the detached new children collection 
using (var context = new MyContext()) 
{ 
    var productInDb = context.Products.Include(p => p.Ingredients) 
     .Single(p => p.Id == product.Id); 

    // Update scalar/complex properties of parent 
    context.Entry(productInDb).CurrentValues.SetValues(product); 

    var ingredientsInDb = productInDb.Ingredients.ToList(); 
    foreach (var ingredientInDb in ingredientsInDb) 
    { 
     // Is the ingredient still there? 
     var ingredient = product.Ingredients 
      .SingleOrDefault(i => i.Id == ingredientInDb.Id); 

     if (ingredient != null) 
      // Yes: Update scalar/complex properties of child 
      context.Entry(ingredientInDb).CurrentValues.SetValues(ingredient); 
     else 
      // No: Delete it 
      context.Ingredients.Remove(ingredientInDb); 
    } 

    foreach (var ingredient in product.Ingredients) 
    { 
     // Is the child NOT in DB? 
     if (!ingredientsInDb.Any(i => i.Id == ingredient.Id)) 
      // Yes: Add it as a new child 
      productInDb.Ingredients.Add(ingredient); 
    } 

    context.SaveChanges(); 
} 
+0

Điều này hoạt động hoàn toàn tuyệt đối !! Cảm ơn nhiều! – DirkDooms

+0

Mất nhiều thời gian để tìm cách cập nhật thực thể đúng cách. Cảm ơn cho context.Entry (productInDb) .CurrentValues.SetValues ​​(sản phẩm); –

+0

Điều này thật tuyệt, bây giờ tôi chỉ cần tìm ra cách làm cho nó hoạt động thông qua sự phản chiếu để tôi có thể lặp qua tất cả các bộ sưu tập trên một thực thể mà tôi đang lưu. – Nikkoli

-2

sau nhiều tháng đấu tranh với sự hiểu biết toàn bộ khuôn khổ thực thể này. Tôi hy vọng điều này có thể giúp ai đó và không trải qua bất kỳ sự thất vọng nào mà tôi đã chịu đựng.

public void SaveOrder(SaleOrder order) 
     { 
      using (var ctx = new CompanyContext()) 
      { 
       foreach (var orderDetail in order.SaleOrderDetails) 
       { 
        if(orderDetail.SaleOrderDetailId == default(int)) 
        { 
         orderDetail.SaleOrderId = order.SaleOrderId; 
         ctx.SaleOrderDetails.Add(orderDetail); 
        }else 
        { 
         ctx.Entry(orderDetail).State = EntityState.Modified; 
        } 
       } 

       ctx.Entry(order).State = order.SaleOrderId == default(int) ? EntityState.Added : EntityState.Modified; 
       ctx.SaveChanges();     

      } 

     } 
+0

có vẻ như với tôi rằng bạn không xem xét trật tự đã xóaChi tiết –

4

tôi thấy gần đây article này trên phần mở rộng GraphDiff cho DbContext.

Dường như đây là biến thể chung, có thể sử dụng lại của Slauma 's solution.

Ví dụ mã:

using (var context = new TestDbContext()) 
{ 
    // Update DBcompany and the collection the company and state that the company 'owns' the collection Contacts. 
    context.UpdateGraph(company, map => map.OwnedCollection(p => p.Contacts));  
    context.SaveChanges(); 
} 

Trên một mặt lưu ý; Tôi thấy tác giả đã đề xuất với nhóm EF sử dụng mã của mình trong số phát hành #864 Provide better support for working with disconnected entities.

-1

Tôi nghĩ, Đây là giải pháp đơn giản hơn.

public Individual 
{ 
..... 

public List<Address> Addresses{get;set;} 


} 

//where base.Update from Generic Repository 
public virtual void Update(T entity) 
     { 
      _dbset.Attach(entity); 
      _dataContext.Entry(entity).State = EntityState.Modified; 
     } 

//overridden update 
public override void Update(Individual entity) 
     { 


      var entry = this.DataContext.Entry(entity); 
      var key = Helper.GetPrimaryKey(entry); 
      var dbEntry = this.DataContext.Set<Individual>().Find(key); 

      if (entry.State == EntityState.Detached) 
      { 
       if (dbEntry != null) 
       { 
        var attachedEntry = this.DataContext.Entry(dbEntry); 
        attachedEntry.CurrentValues.SetValues(entity); 
       } 
       else 
       { 
        base.Update(entity); 
       } 
      } 
      else 
      { 
       base.Update(entity); 
      } 
      if (entity.Addresses.Count > 0) 
      { 
       foreach (var address in entity.Addresses) 
       { 
        if (address != null) 
        { 
         this.DataContext.Set<Address>().Attach(address); 
         DataContext.Entry(address).State = EntityState.Modified; 
        } 
       } 
      } 
     } 
Các vấn đề liên quan