2011-06-28 21 views
13

Tôi đang cố gắng thực hiện điều gì đó thật sự đơn giản và tôi không thể tìm thấy cách thực hiện bằng cách sử dụng Entity Framework 4.1.Tôi làm cách nào để upsert một bản ghi trong ADO.NET EF 4.1?

Tôi muốn một phương pháp điều khiển chấp nhận một đối tượng và sau đó thực hiện một UPSERT (hoặc chèn hoặc cập nhật tùy thuộc vào việc bản ghi có tồn tại trong cơ sở dữ liệu) hay không.

Tôi đang sử dụng khóa tự nhiên, vì vậy không có cách nào để tôi xem POCO của mình và cho biết đó có phải là mới hay không.

Đây là cách tôi làm việc đó, và có vẻ như không đúng với tôi:

[HttpPost] 
public JsonResult SaveMyEntity(MyEntity entity) 
{    
    MyContainer db = new MyContainer(); // DbContext 
    if (ModelState.IsValid) 
    { 
     var existing = 
      db.MyEntitys.Find(entity.MyKey); 
     if (existing == null) 
     { 
      db.MyEntitys.Add(entity); 
     } 
     else 
     { 
      existing.A = entity.A; 
      existing.B = entity.B; 
      db.Entry(existing).State = EntityState.Modified; 
     } 
     db.SaveChanges(); 
     return Json(new { Result = "Success" }); 
    } 
} 

Lý tưởng nhất, toàn bộ điều chỉ sẽ là một cái gì đó như thế này:

db.MyEntities.AddOrModify(entity); 
+0

gì xảy ra khi bạn làm chỉ 'Save'? –

Trả lời

20

Đáng tiếc là không có cách nào để làm điều này mà không cần truy vấn cơ sở dữ liệu hoặc sử dụng thủ tục lưu trữ. Mã Minimalistic nên là:

public void AddOrModify<T>(T entity, string key) where T : class, IEntity // Implements MyKey 
{ 
    using (var context = new MyContainer()) 
    { 
     if (context.Set<T>().Any(e => e.MyKey == key)) 
     { 
       context.Entry(entity).State = EntityState.Modified; 
     } 
     else 
     { 
       context.Entry(entity).State = EntityState.Added; 
     } 

     context.SaveChanges(); 
    } 
} 
+1

Tính năng này hoạt động. Sql được phát ra bởi khung công tác không phải là lý tưởng, nhưng ít nhất nó cũng giúp tôi giải quyết vấn đề khi cài đặt các trường theo cách thủ công khi cần cập nhật. Cảm ơn. –

+2

Tôi khuyên bạn nên nhấn mạnh kiểm tra điều này cho các điều kiện chủng tộc (Xem các đề xuất SP bên dưới). –

+0

Bạn có thể thực hiện dò tìm va chạm ở hai bên của dây miễn là bạn đảm bảo nguyên tử với một giao dịch. Điều này đặc biệt quan trọng khi bạn làm việc trên nhiều cửa hàng dữ liệu. Một giải pháp phía máy chủ phải luôn hiệu quả hơn, nhưng không bắt buộc. Câu trả lời này có lẽ là một trong những ví dụ sạch nhất về việc thực hiện upsert thông qua DbContext API. –

2

Trong hầu hết các trường hợp, bạn làm không cần đặt rõ ràng EntityState.Modified trừ khi bạn đã tắt theo dõi thay đổi.

Các giải pháp chúng tôi đã là để kiểm tra giá trị của nhận dạng thực thể:

if (entity.Id == default(int)) { 
    // transient entity so insert 
} else { 
    // update 
} 
+0

Giải pháp này sẽ không hoạt động đối với tôi vì tôi không sử dụng khóa nhận dạng số nguyên. Tôi đang sử dụng một chìa khóa composite tự nhiên, vì vậy không có cách nào để tôi nhìn vào POCO của tôi và cho biết nếu nó mới hay không. –

+0

Sau đó nhấn vào cơ sở dữ liệu để kiểm tra có vẻ như là giải pháp khả thi duy nhất cho bạn. Nó sẽ là tốt hơn để có hành động xác định rõ ràng cho chèn/cập nhật và thực sự bạn nên sử dụng viewmodels, nhưng tôi đánh giá cao đây có lẽ là một ví dụ đơn giản. –

+0

Bạn có thể xem xét việc lưu trữ bộ định danh đã sử dụng và cập nhật bộ đệm sau mỗi lần chèn/xóa (trừ khi bạn có thể thay đổi khóa, nhưng điều đó rất sai trong ý kiến ​​của tôi) để tăng tốc quá trình, nhưng nếu bạn không bằng cách sử dụng autogenerated key (đó là con đường để đi, tại sao bạn không sử dụng chúng, Bạn không cần phải thả Bạn chìa khóa, u thậm chí có thể chỉ số nó với ràng buộc duy nhất) đó là cách duy nhất tôi nhìn thấy (xử lý trùng lặp khóa ngoại lệ là không thực sự theo cách: D) – luckyluke

2

thực sự có một cách để bối cảnh informa db rằng thực thể bạn đang cố gắng để chèn được thay đổi và bây giờ mới một

_context.MyEntity.Attach(entity); 
_context.MyEntity(entity).State = System.Data.EntityState.Modified; 
2

Để thực hiện một hoạt động UPSERT, Bạn có thể muốn xem xét việc tạo một SP thực hiện một MERGE.

http://www.databasejournal.com/features/mssql/article.php/3739131/UPSERT-Functionality-in-SQL-Server-2008.htm

Bất cứ cách nào bạn chọn các hoạt động phải có nguyên tử hoặc bạn sẽ có một điều kiện chủng tộc. SP của bạn có thể sẽ cần một HOLDLOCK để làm mất hiệu lực này ...

http://weblogs.sqlteam.com/dang/archive/2009/01/31/UPSERT-Race-Condition-With-MERGE.aspx

+0

Điều gì sẽ xảy ra khi bạn chỉ lưu 'Save'? –

0

Tôi biết câu hỏi này là cũ và có một câu trả lời được chấp nhận, nhưng tôi nghĩ rằng có một giải pháp tốt hơn: nó không đòi hỏi một thêm giao diện được triển khai hoặc loại khóa được xác định.

public static class DbSetExtensions 
{ 
    public static EntityEntry<TEnt> AddIfNotExists<TEnt, TKey>(this DbSet<TEnt> dbSet, TEnt entity, Func<TEnt, TKey> predicate) where TEnt : class 
    { 
     var exists = dbSet.Any(c => predicate(entity).Equals(predicate(c))); 
     return exists 
      ? null 
      : dbSet.Add(entity); 
    } 

    public static void AddRangeIfNotExists<TEnt, TKey>(this DbSet<TEnt> dbSet, IEnumerable<TEnt> entities, Func<TEnt, TKey> predicate) where TEnt : class 
    { 
     var entitiesExist = from ent in dbSet 
      where entities.Any(add => predicate(ent).Equals(predicate(add))) 
      select ent; 

     dbSet.AddRange(entities.Except(entitiesExist)); 
    } 
} 

Vì vậy, sau đó nó có thể được sử dụng như thế này:

using (var context = new MyDbContext()) 
{ 
    var user1 = new User { Name = "Peter", Age = 32 }; 
    context.Users.AddIfNotExists(user1, u => u.Name); 

    var user2 = new User { Name = "Joe", Age = 25 }; 
    context.Users.AddIfNotExists(user2, u => u.Age); 

    // Adds user1 if there is no user with name "Peter" 
    // Adds user2 if there is no user with age 25 
    context.SaveChanges(); 
} 
+0

Tôi không nghĩ rằng bạn thực sự đã thử điều này, phải không? –

+0

@GertArnold không thực sự tôi đang tìm kiếm "UPSERT" hoặc "INSERT hoặc không làm gì nếu thực thể đã tồn tại", nhưng tất cả các giải pháp tôi đã tìm thấy yêu cầu triển khai giao diện hoặc sử dụng phương pháp chuyên dụng cho mọi loại thực thể, v.v. Vì vậy, tôi đã tạo phương pháp chung này mà tôi đang sử dụng trong ứng dụng của tôi và có, nó hoạt động. – Salaros

+0

Nó hoạt động? Mã này thậm chí không biên dịch! Và nếu có, bạn sẽ nhận được "Kiểu nút biểu thức LINQ 'Gọi' không được hỗ trợ trong LINQ to Entities." –

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