2012-10-05 18 views
40

Cách tốt nhất để xử lý các cập nhật hàng loạt bằng cách sử dụng (Entity Framework) EF5 là gì? tôi có 2 trường hợp đặc biệt tôi mong muốn:Cập nhật hàng loạt/xóa EF5

  1. Cập nhật một lĩnh vực (ví dụ UpdateDate) cho một danh sách (danh sách) từ 100 đến 100.000 Id, mà khóa chính. Việc gọi từng bản cập nhật riêng biệt dường như có nhiều chi phí và mất nhiều thời gian.

  2. Chèn nhiều, cũng nằm trong khoảng từ 100 đến 100.000, của cùng một đối tượng (ví dụ: Người dùng) chỉ bằng một lần.

Bạn có lời khuyên hay không?

Trả lời

35
  1. Có hai dự án nguồn mở cho phép điều này: EntityFramework.ExtendedEntity Framework Extensions. Bạn cũng có thể kiểm tra discussion về cập nhật hàng loạt trên trang web codeplex của EF.
  2. Chèn 100k bản ghi qua EF là ở vị trí đầu tiên cấu trúc ứng dụng sai. Bạn nên chọn công nghệ nhẹ khác nhau để nhập dữ liệu. Ngay cả hoạt động nội bộ của EF với bộ hồ sơ lớn như vậy sẽ khiến bạn mất nhiều thời gian xử lý. Hiện tại không có giải pháp cho chèn hàng loạt cho EF nhưng có broad discussion về tính năng này trên trang web mã của EF.
+1

Bạn có biết hỗ trợ EF v4 trong EntityFramework.Extended? Và có vẻ như Entity Framework Extensions là dự án đã chết (bản phát hành cuối cùng là vào năm 2010) –

+1

Có một phần mở rộng cho chèn số lượng lớn ef trên codeplex http://stackoverflow.com/a/27983392/937411 –

+0

Việc ghép vẫn còn quan trọng? – Jordan

3

Bạn có thể không muốn nghe, nhưng lựa chọn tốt nhất của bạn là không sử dụng EF cho hoạt động hàng loạt. Để cập nhật một trường trên một bảng các bản ghi, sử dụng một câu lệnh Update trong cơ sở dữ liệu (có thể được gọi thông qua một proc được lưu trữ ánh xạ tới một hàm EF). Bạn cũng có thể sử dụng phương thức Context.ExecuteStoreQuery để phát hành một câu lệnh Update cho cơ sở dữ liệu.

Để chèn lớn, đặt cược tốt nhất của bạn là sử dụng Bulk Copy hoặc SSIS. EF sẽ yêu cầu một lần truy cập riêng biệt vào cơ sở dữ liệu cho mỗi hàng được chèn vào.

21

tôi thấy các tùy chọn sau:

1. Cách đơn giản nhất - tạo yêu cầu SQL của bạn bằng tay và thực hiện thông qua ObjectContext.ExecuteStoreCommand

context.ExecuteStoreCommand("UPDATE TABLE SET FIELD1 = {0} WHERE FIELD2 = {1}", value1, value2); 

2. Sử dụng EntityFramework.Extended

context.Tasks.Update(
    t => t.StatusId == 1, 
    t => new Task {StatusId = 2}); 

3. Làm cho phần mở rộng của riêng bạn cho EF. Có một bài viết Bulk Delete nơi mục tiêu này đạt được bằng cách kế thừa ObjectContext lớp. Đó là giá trị để có một cái nhìn. Chèn/cập nhật hàng loạt có thể được triển khai theo cách tương tự.

+1

'bContext.Database.ExecuteSqlCommand()' trong EF6? – Jess

+0

Sử dụng tùy chọn này thay vì ctx.Database.ExecuteSqlCommand – Ovis

2

Chèn hàng loạt phải được thực hiện bằng cách sử dụng lớp SqlBulkCopy. Vui lòng xem StackOverflow có sẵn từ trước Q & A khi tích hợp hai: SqlBulkCopy and Entity Framework

SqlBulkCopy thân thiện với người dùng hơn bcp (tiện ích dòng lệnh sao chép hàng loạt) hoặc thậm chí OPW ROWSET.

+0

không có điều gì như cập nhật hàng loạt với SqlBulkCopy, chỉ BulkInsert – Pleun

+0

Tôi có thể trả lời phần thứ hai của câu hỏi và vô tình nhập "cập nhật hàng loạt" thay vì "chèn hàng loạt". Đã sửa. –

0

Tôi đồng ý với câu trả lời được chấp nhận rằng ef có thể là công nghệ sai cho chèn số lượng lớn. Tuy nhiên, tôi nghĩ rằng nó có giá trị có một cái nhìn tại EntityFramework.BulkInsert.

0

Đây là những gì tôi đã thực hiện thành công:

private void BulkUpdate() 
{ 
    var oc = ((IObjectContextAdapter)_dbContext).ObjectContext; 
    var updateQuery = myIQueryable.ToString(); // This MUST be above the call to get the parameters. 
    var updateParams = GetSqlParametersForIQueryable(updateQuery).ToArray(); 
    var updateSql = [email protected]"UPDATE dbo.myTable 
         SET col1 = x.alias2 
         FROM dbo.myTable 
         JOIN ({updateQuery}) x(alias1, alias2) ON x.alias1 = dbo.myTable.Id"; 
    oc.ExecuteStoreCommand(updateSql, updateParams); 
} 

private void BulkInsert() 
{ 
    var oc = ((IObjectContextAdapter)_dbContext).ObjectContext; 
    var insertQuery = myIQueryable.ToString(); // This MUST be above the call to get the parameters. 
    var insertParams = GetSqlParametersForIQueryable(insertQuery).ToArray(); 
    var insertSql = [email protected]"INSERT INTO dbo.myTable (col1, col2) 
         SELECT x.alias1, x.alias2 
         FROM ({insertQuery}) x(alias1, alias2)"; 
    oc.ExecuteStoreCommand(insertSql, insertParams.ToArray()); 
}  

private static IEnumerable<SqlParameter> GetSqlParametersForIQueryable<T>(IQueryable<T> queryable) 
{ 
    var objectQuery = GetObjectQueryFromIQueryable(queryable); 
    return objectQuery.Parameters.Select(x => new SqlParameter(x.Name, x.Value)); 
} 

private static ObjectQuery<T> GetObjectQueryFromIQueryable<T>(IQueryable<T> queryable) 
{ 
    var dbQuery = (DbQuery<T>)queryable; 
    var iqProp = dbQuery.GetType().GetProperty("InternalQuery", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public); 
    var iq = iqProp.GetValue(dbQuery, null); 
    var oqProp = iq.GetType().GetProperty("ObjectQuery", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public); 
    return (ObjectQuery<T>)oqProp.GetValue(iq, null); 
} 
0
public static bool BulkDelete(string tableName, string columnName, List<object> val) 
    { 
     bool ret = true; 

     var max = 2000; 
     var pages = Math.Ceiling((double)val.Count/max); 
     for (int i = 0; i < pages; i++) 
     { 
      var count = max; 
      if (i == pages - 1) { count = val.Count % max; } 

      var args = val.GetRange(i * max, count); 
      var cond = string.Join("", args.Select((t, index) => $",@p{index}")).Substring(1); 
      var sql = $"DELETE FROM {tableName} WHERE {columnName} IN ({cond}) "; 

      ret &= Db.ExecuteSqlCommand(sql, args.ToArray()) > 0; 
     } 

     return ret; 
    }