Cách nhanh nhất để chèn vào khung thực thể


681

Tôi đang tìm cách nhanh nhất để chèn vào Entity Framework.

Tôi đang hỏi điều này vì kịch bản mà bạn có một Giao dịch viên đang hoạt động và phần chèn rất lớn (4000+). Nó có khả năng có thể kéo dài hơn 10 phút (thời gian chờ giao dịch mặc định) và điều này sẽ dẫn đến một giao dịch không hoàn chỉnh.


1
Làm thế nào bạn đang làm điều đó?
Dustin Laine

Tạo Giao dịch viên, khởi tạo DBContext, Mở kết nối và trong một câu lệnh cho mỗi lần thực hiện các phần chèn và SavingChanges (cho mỗi bản ghi), LƯU Ý: TransactionScope và DBContext đang sử dụng các câu lệnh và cuối cùng tôi sẽ kết nối khối
Bongo Sharp

Một câu trả lời khác để tham khảo: stackoverflow.com/questions/5798646/
Kẻ

2
Cách nhanh nhất để chèn vào cơ sở dữ liệu SQL không liên quan đến EF. AFAIK BCP của nó sau đó TVP + Hợp nhất / chèn.
StingyJack

1
Đối với những người sẽ đọc ý kiến: Hầu hết các câu trả lời hiện đại, có thể áp dụng được.
Tanveer Badar

Câu trả lời:


985

Nhận xét của bạn trong các ý kiến ​​cho câu hỏi của bạn:

"... SavingChanges ( cho mỗi bản ghi ) ..."

Đó là điều tồi tệ nhất bạn có thể làm! Gọi SaveChanges()cho mỗi bản ghi làm chậm chèn số lượng lớn vô cùng. Tôi sẽ làm một vài thử nghiệm đơn giản rất có thể sẽ cải thiện hiệu suất:

  • Gọi SaveChanges()một lần sau TẤT CẢ hồ sơ.
  • Gọi SaveChanges()sau ví dụ 100 hồ sơ.
  • Gọi SaveChanges() sau ví dụ 100 bản ghi và xử lý bối cảnh và tạo một bản mới.
  • Vô hiệu hóa phát hiện thay đổi

Đối với các phần chèn lớn, tôi đang làm việc và thử nghiệm một mẫu như thế này:

using (TransactionScope scope = new TransactionScope())
{
    MyDbContext context = null;
    try
    {
        context = new MyDbContext();
        context.Configuration.AutoDetectChangesEnabled = false;

        int count = 0;            
        foreach (var entityToInsert in someCollectionOfEntitiesToInsert)
        {
            ++count;
            context = AddToContext(context, entityToInsert, count, 100, true);
        }

        context.SaveChanges();
    }
    finally
    {
        if (context != null)
            context.Dispose();
    }

    scope.Complete();
}

private MyDbContext AddToContext(MyDbContext context,
    Entity entity, int count, int commitCount, bool recreateContext)
{
    context.Set<Entity>().Add(entity);

    if (count % commitCount == 0)
    {
        context.SaveChanges();
        if (recreateContext)
        {
            context.Dispose();
            context = new MyDbContext();
            context.Configuration.AutoDetectChangesEnabled = false;
        }
    }

    return context;
}

Tôi có một chương trình thử nghiệm chèn 560.000 thực thể (9 thuộc tính vô hướng, không có thuộc tính điều hướng) vào DB. Với mã này, nó hoạt động trong ít hơn 3 phút.

Đối với hiệu suất, điều quan trọng là gọi SaveChanges()sau "nhiều" bản ghi ("nhiều" khoảng 100 hoặc 1000). Nó cũng cải thiện hiệu suất để xử lý bối cảnh sau SaveChanges và tạo một bối cảnh mới. Điều này xóa bối cảnh khỏi tất cả các mục nhập, SaveChangeskhông làm điều đó, các thực thể vẫn được gắn liền với bối cảnh ở trạng thái Unchanged. Đó là kích thước ngày càng tăng của các thực thể đính kèm trong bối cảnh làm chậm từng bước chèn. Vì vậy, nó là hữu ích để xóa nó sau một thời gian.

Dưới đây là một vài phép đo cho 560000 thực thể của tôi:

  • commitCount = 1, createdeContext = false: nhiều giờ (Đó là thủ tục hiện tại của bạn)
  • commitCount = 100, createdeContext = false: hơn 20 phút
  • commitCount = 1000, createdeContext = false: 242 giây
  • commitCount = 10000, createdeContext = false: 202 giây
  • commitCount = 100000, createdeContext = false: 199 giây
  • commitCount = 1000000, createdeContext = false: ngoại lệ bộ nhớ
  • commitCount = 1, createdeContext = true: hơn 10 phút
  • commitCount = 10, createdeContext = true: 241 giây
  • commitCount = 100, createdeContext = true: 164 giây
  • commitCount = 1000, createdeContext = true: 191 giây

Hành vi trong thử nghiệm đầu tiên ở trên là hiệu suất rất phi tuyến tính và giảm cực kỳ theo thời gian. ("Nhiều giờ" là một ước tính, tôi chưa bao giờ hoàn thành bài kiểm tra này. Tôi đã dừng lại ở 50.000 thực thể sau 20 phút.) Hành vi phi tuyến tính này không quá quan trọng trong tất cả các thử nghiệm khác.


89
@Bongo Sharp: Đừng quên thiết lập AutoDetectChangesEnabled = false;trên DbContext. Nó cũng có hiệu ứng hiệu suất bổ sung lớn: stackoverflow.com/questions/5943394/
Khăn

6
Vâng, vấn đề là tôi đang sử dụng Entity Framework 4 và AutoDetectChangesEnables là một phần của 4.1, tuy nhiên, tôi đã thực hiện bài kiểm tra hiệu năng và tôi đã có KẾT QUẢ TUYỆT VỜI, nó đã đi từ 00:12:00 đến 00:00:22 SavinChanges trên mỗi thực thể đang thực hiện tải olver ... CẢM ƠN rất nhiều cho phần mềm ans của bạn! đây là những gì tôi đang tìm kiếm
Bongo Sharp

10
Cảm ơn bạn vì bối cảnh.Configuration.AutoDetectChangesEnables = false; Mẹo, nó làm cho một sự khác biệt rất lớn .
douglaz

1
@ dahacker89: Bạn có đang sử dụng phiên bản chính xác EF> = 4.1 và DbContextKHÔNG ObjectContext?
Slauma

3
@ dahacker89: Tôi đề nghị bạn tạo một câu hỏi riêng cho vấn đề của bạn với nhiều chi tiết hơn. Tôi không thể tìm ra điều gì sai.
Slauma

176

Sự kết hợp này tăng tốc độ đủ tốt.

context.Configuration.AutoDetectChangesEnabled = false;
context.Configuration.ValidateOnSaveEnabled = false;

46
Đừng mù quáng vô hiệu hóa ValidateOnSaveEnables bạn có thể tùy thuộc vào hành vi đó và không nhận ra điều đó cho đến khi quá muộn. Sau đó, một lần nữa, bạn có thể thực hiện xác nhận ở nơi khác trong mã và việc xác nhận lại EF là hoàn toàn không cần thiết.
Jeremy Cook

1
Trong thử nghiệm của tôi, tiết kiệm 20.000 hàng đã giảm từ 101 giây xuống còn 88 giây. Không nhiều và những tác động là gì.
À.

27
@JeremyCook Tôi nghĩ rằng những gì bạn đang cố gắng nhận được là câu trả lời này sẽ tốt hơn nhiều nếu nó giải thích ý nghĩa có thể của việc thay đổi các thuộc tính này từ các giá trị mặc định của chúng (ngoài cải thiện hiệu suất). Tôi đồng ý.
giả mã

1
Điều này hiệu quả với tôi, mặc dù nếu bạn đang cập nhật các bản ghi trong ngữ cảnh, bạn sẽ cần gọi DetectChanges () một cách rõ ràng
hillstuk

2
Những thứ này có thể bị vô hiệu hóa và sau đó được kích hoạt lại với một khối thử cuối cùng: msdn.microsoft.com/en-us/data/jj556205.aspx
yellavon

98

Cách nhanh nhất sẽ là sử dụng tiện ích mở rộng chèn số lượng lớn mà tôi đã phát triển

lưu ý: đây là sản phẩm thương mại, không miễn phí

Nó sử dụng SqlBulkCopy và bộ dữ liệu tùy chỉnh để có được hiệu suất tối đa. Kết quả là nó nhanh hơn 20 lần so với sử dụng chèn hoặc AddRange thông thường EntityFramework.BulkInsert vs EF AddRange

cách sử dụng cực kỳ đơn giản

context.BulkInsert(hugeAmountOfEntities);

10
Nhanh nhưng chỉ làm lớp trên cùng của một hệ thống phân cấp.
CAD bloke

65
Nó không miễn phí.
Amir Saniyan

72
Quảng cáo ngày càng thông minh hơn ... đây là sản phẩm trả phí và rất đắt cho một người làm việc tự do. Được cảnh báo!
JulioQc

35
600 USD cho hỗ trợ và nâng cấp 1 năm? Bạn mất trí rồi à?
Camilo Terevinto

7
tôi không còn là chủ sở hữu của sản phẩm nữa
maxlego

83

Bạn nên nhìn vào việc sử dụng System.Data.SqlClient.SqlBulkCopycho việc này. Đây là tài liệu , và tất nhiên có rất nhiều hướng dẫn trực tuyến.

Xin lỗi, tôi biết bạn đang tìm kiếm một câu trả lời đơn giản để khiến EF thực hiện những gì bạn muốn, nhưng các hoạt động hàng loạt không thực sự là ý nghĩa của ORM.


1
Tôi đã tình cờ gặp SqlBulkCopy một vài lần khi nghiên cứu về vấn đề này, nhưng dường như nó được định hướng nhiều hơn cho các phần chèn trên bàn, thật đáng buồn là tôi không mong đợi các giải pháp dễ dàng, mà là các mẹo về hiệu suất, ví dụ như quản lý Trạng thái của kết nối thủ công, nhờ để cho EF làm điều đó cho bạn
Bongo Sharp

7
Tôi đã sử dụng SqlBulkCopy để chèn một lượng lớn dữ liệu ngay từ ứng dụng của mình. Về cơ bản, bạn phải tạo một DataTable, điền nó lên, sau đó chuyển tới BulkCopy. Có một vài vấn đề khi bạn thiết lập DataTable (đáng buồn là hầu hết tôi đã quên), nhưng nó sẽ hoạt động tốt
Adam Rackis

2
Tôi đã làm bằng chứng về khái niệm và như đã hứa, nó hoạt động rất nhanh, nhưng một trong những lý do tại sao tôi sử dụng EF là vì việc chèn dữ liệu quan hệ dễ dàng hơn, ví dụ: nếu tôi chèn một thực thể đã chứa dữ liệu quan hệ , nó cũng sẽ chèn nó, bạn đã bao giờ tham gia vào kịch bản này chưa? Cảm ơn!
Bongo Sharp

2
Thật không may, việc chèn một trang web của các đối tượng vào DBMS không thực sự là điều mà BulkCopy sẽ làm. Đó là lợi ích của một ORM như EF, chi phí mà nó sẽ không mở rộng để thực hiện hàng trăm biểu đồ đối tượng tương tự một cách hiệu quả.
Adam Rackis

2
SqlBulkCopy chắc chắn là con đường để đi nếu bạn cần tốc độ thô hoặc nếu bạn sẽ chạy lại phần chèn này. Tôi đã chèn vài triệu bản ghi trước đó và nó cực kỳ nhanh. Điều đó nói rằng, trừ khi bạn sẽ cần chạy lại phần chèn này, có thể dễ dàng hơn khi chỉ sử dụng EF.
Neil

49

Tôi đồng ý với Adam Rackis. SqlBulkCopylà cách nhanh nhất để chuyển các bản ghi hàng loạt từ nguồn dữ liệu này sang nguồn dữ liệu khác. Tôi đã sử dụng điều này để sao chép hồ sơ 20K và chỉ mất chưa đến 3 giây. Hãy xem ví dụ dưới đây.

public static void InsertIntoMembers(DataTable dataTable)
{           
    using (var connection = new SqlConnection(@"data source=;persist security info=True;user id=;password=;initial catalog=;MultipleActiveResultSets=True;App=EntityFramework"))
    {
        SqlTransaction transaction = null;
        connection.Open();
        try
        {
            transaction = connection.BeginTransaction();
            using (var sqlBulkCopy = new SqlBulkCopy(connection, SqlBulkCopyOptions.TableLock, transaction))
            {
                sqlBulkCopy.DestinationTableName = "Members";
                sqlBulkCopy.ColumnMappings.Add("Firstname", "Firstname");
                sqlBulkCopy.ColumnMappings.Add("Lastname", "Lastname");
                sqlBulkCopy.ColumnMappings.Add("DOB", "DOB");
                sqlBulkCopy.ColumnMappings.Add("Gender", "Gender");
                sqlBulkCopy.ColumnMappings.Add("Email", "Email");

                sqlBulkCopy.ColumnMappings.Add("Address1", "Address1");
                sqlBulkCopy.ColumnMappings.Add("Address2", "Address2");
                sqlBulkCopy.ColumnMappings.Add("Address3", "Address3");
                sqlBulkCopy.ColumnMappings.Add("Address4", "Address4");
                sqlBulkCopy.ColumnMappings.Add("Postcode", "Postcode");

                sqlBulkCopy.ColumnMappings.Add("MobileNumber", "MobileNumber");
                sqlBulkCopy.ColumnMappings.Add("TelephoneNumber", "TelephoneNumber");

                sqlBulkCopy.ColumnMappings.Add("Deleted", "Deleted");

                sqlBulkCopy.WriteToServer(dataTable);
            }
            transaction.Commit();
        }
        catch (Exception)
        {
            transaction.Rollback();
        }

    }
}

1
Tôi đã thử nhiều giải pháp được cung cấp trong bài đăng này và SqlBulkCopy là nhanh nhất. Pure EF mất 15 phút, nhưng với sự pha trộn giữa giải pháp và SqlBulkCopy, tôi đã có thể giảm xuống còn 1,5 phút! Đây là với 2 triệu hồ sơ! Nếu không có bất kỳ tối ưu hóa chỉ số DB.
jonas

Danh sách dễ dàng hơn DataTable. Có một AsDataReader()phương pháp mở rộng, được giải thích trong câu trả lời này: stackoverflow.com/a/36817205/1507899
RJB

Nhưng nó chỉ dành cho Thực thể hàng đầu không liên quan
Zahid Mustafa

1
@ZahidMustafa: vâng. Nó đang thực hiện BulkInsert, không phải Phân tích hàng loạt-Phân tích và Truy tìm-Truy tìm-Đối tượng-Đồ thị .. nếu bạn muốn bao quát các mối quan hệ, bạn phải phân tích và xác định thứ tự chèn và sau đó chèn hàng loạt cấp độ riêng lẻ và có thể cập nhật một số khóa như cần thiết, và bạn sẽ nhận được giải pháp tùy chỉnh nhanh chóng. Hoặc, bạn có thể dựa vào EF để làm điều đó, không có việc gì về phía bạn, nhưng chậm hơn khi chạy.
quetzalcoatl

23

Tôi muốn giới thiệu bài viết này về cách thực hiện chèn số lượng lớn bằng cách sử dụng EF.

Khung thực thể và CHỨNG MINH số lượng lớn chậm

Anh khám phá những lĩnh vực này và so sánh sự hoàn hảo:

  1. Mặc định EF (57 phút để hoàn thành thêm 30.000 hồ sơ)
  2. Thay thế bằng Mã ADO.NET (25 giây cho cùng 30.000)
  3. Bối cảnh bối cảnh- Giữ đồ thị ngữ cảnh hoạt động nhỏ bằng cách sử dụng bối cảnh mới cho mỗi Đơn vị công việc (cùng 30.000 lần chèn mất 33 giây)
  4. Danh sách lớn - Tắt AutoDetectChangesEnables (giảm thời gian xuống còn khoảng 20 giây)
  5. Batching (giảm xuống 16 giây)
  6. DbTable.AddRange () - (hiệu suất nằm trong phạm vi 12)

21

vì nó chưa bao giờ được đề cập ở đây, tôi muốn giới thiệu EFCore.BulkExtensions tại đây

context.BulkInsert(entitiesList);                 context.BulkInsertAsync(entitiesList);
context.BulkUpdate(entitiesList);                 context.BulkUpdateAsync(entitiesList);
context.BulkDelete(entitiesList);                 context.BulkDeleteAsync(entitiesList);
context.BulkInsertOrUpdate(entitiesList);         context.BulkInsertOrUpdateAsync(entitiesList);         // Upsert
context.BulkInsertOrUpdateOrDelete(entitiesList); context.BulkInsertOrUpdateOrDeleteAsync(entitiesList); // Sync
context.BulkRead(entitiesList);                   context.BulkReadAsync(entitiesList);

1
Tôi thứ hai gợi ý này. Sau khi thử nhiều giải pháp homebrew, điều này đã cắt phần chèn của tôi xuống còn 1 giây từ hơn 50 giây. Và, đó là giấy phép MIT rất dễ kết hợp.
SouthShoreAK

đây có phải là lợi ích của ef 6.x
Alok

việc này chỉ hiệu quả hơn so với sử dụng AddRange nếu có trên 10 thực thể
Jackal

5
10 000 chèn đã đi từ 9 phút đến 12 giây. Điều này xứng đáng được chú ý nhiều hơn!
callisto

2
Nếu có bất kỳ cách nào để thay đổi câu trả lời được chấp nhận, thì đây sẽ là câu trả lời được chấp nhận hiện đại. Và tôi muốn đội EF cung cấp điều này ra khỏi hộp.
Tanveer Badar

18

Tôi đã điều tra câu trả lời của Slauma (thật tuyệt vời, cảm ơn người đàn ông ý tưởng) và tôi đã giảm kích thước lô cho đến khi tôi đạt tốc độ tối ưu. Nhìn vào kết quả của Slauma:

  • commitCount = 1, createdeContext = true: hơn 10 phút
  • commitCount = 10, createdeContext = true: 241 giây
  • commitCount = 100, createdeContext = true: 164 giây
  • commitCount = 1000, createdeContext = true: 191 giây

Có thể thấy rằng có sự tăng tốc độ khi di chuyển từ 1 đến 10 và từ 10 đến 100, nhưng từ 100 đến 1000 tốc độ chèn lại giảm xuống.

Vì vậy, tôi đã tập trung vào những gì đang xảy ra khi bạn giảm kích thước lô xuống giá trị ở đâu đó trong khoảng từ 10 đến 100 và đây là kết quả của tôi (Tôi đang sử dụng các nội dung hàng khác nhau, vì vậy thời gian của tôi có giá trị khác nhau):

Quantity    | Batch size    | Interval
1000    1   3
10000   1   34
100000  1   368

1000    5   1
10000   5   12
100000  5   133

1000    10  1
10000   10  11
100000  10  101

1000    20  1
10000   20  9
100000  20  92

1000    27  0
10000   27  9
100000  27  92

1000    30  0
10000   30  9
100000  30  92

1000    35  1
10000   35  9
100000  35  94

1000    50  1
10000   50  10
100000  50  106

1000    100 1
10000   100 14
100000  100 141

Dựa trên kết quả của tôi, tối ưu thực tế là khoảng 30 cho kích thước lô. Nó ít hơn cả 10 và 100. Vấn đề là, tôi không biết tại sao 30 tối ưu, tôi cũng không thể tìm thấy bất kỳ lời giải thích hợp lý nào cho nó.


2
Tôi thấy điều tương tự với Postrges và SQL thuần túy (nó phụ thuộc vào SQL không phải trên EF) mà 30 là tối ưu.
Kamil Gareev

Kinh nghiệm của tôi là sự khác biệt tối ưu cho tốc độ kết nối và kích thước hàng khác nhau. Để kết nối nhanh và các hàng nhỏ tối ưu có thể thậm chí> 200 hàng.
jing

18

Như những người khác đã nói SqlBulkCopy là cách để làm điều đó nếu bạn muốn hiệu suất chèn thực sự tốt.

Nó hơi cồng kềnh khi thực hiện nhưng có những thư viện có thể giúp bạn với nó. Có một vài cái ở ngoài đó nhưng tôi sẽ không biết xấu hổ cắm thư viện của riêng mình lần này: https://github.com/MikaelEliasson/EntityFramework.Utilities#batch-insert-entities

Mã duy nhất bạn cần là:

 using (var db = new YourDbContext())
 {
     EFBatchOperation.For(db, db.BlogPosts).InsertAll(list);
 }

Vậy nó nhanh hơn bao nhiêu? Rất khó để nói vì nó phụ thuộc vào rất nhiều yếu tố, hiệu suất máy tính, mạng, kích thước đối tượng, v.v. Các thử nghiệm hiệu suất tôi đã thực hiện cho thấy các thực thể 25k có thể được chèn vào khoảng 10 giây theo cách tiêu chuẩn trên localhost NẾU bạn tối ưu hóa cấu hình EF của mình như đề cập trong các câu trả lời khác. Với EFUtilities mất khoảng 300ms. Điều thú vị hơn nữa là tôi đã lưu khoảng 3 triệu thực thể trong vòng dưới 15 giây bằng phương pháp này, trung bình khoảng 200 nghìn thực thể mỗi giây.

Một vấn đề là tất nhiên nếu bạn cần chèn dữ liệu liên quan. Điều này có thể được thực hiện một cách hiệu quả vào máy chủ sql bằng phương pháp trên nhưng nó yêu cầu bạn phải có chiến lược tạo Id cho phép bạn tạo id trong mã ứng dụng cho cha mẹ để bạn có thể đặt khóa ngoại. Điều này có thể được thực hiện bằng GUID hoặc một cái gì đó như thế hệ id HiLo.


Hoạt động tốt. Cú pháp là một chút dài dòng mặc dù. Hãy nghĩ rằng sẽ tốt hơn nếu EFBatchOperationcó một hàm tạo mà bạn truyền vào DbContextchứ không truyền cho mọi phương thức tĩnh. Các phiên bản chung InsertAllUpdateAlltự động tìm bộ sưu tập, tương tự DbContext.Set<T>, cũng sẽ tốt.
kjbartel

Chỉ cần một bình luận nhanh để nói lời cảm ơn! Mã này cho phép tôi lưu 170k hồ sơ trong 1,5 giây! Hoàn toàn thổi bất kỳ phương pháp nào khác tôi đã thử ra khỏi nước.
Tom Glenn

@Mikael Một vấn đề là xử lý các trường danh tính. Bạn đã có cách nào để kích hoạt tính năng chèn danh tính chưa?
Joe Phillips

1
Trái ngược với EntityFramework.BulkInsert, thư viện này vẫn miễn phí. +1
Rudey

14

Dispose()bối cảnh tạo ra vấn đề nếu các thực thể bạn Add()dựa vào các thực thể được tải sẵn khác (ví dụ: thuộc tính điều hướng) trong ngữ cảnh

Tôi sử dụng khái niệm tương tự để giữ cho bối cảnh của tôi nhỏ để đạt được hiệu suất tương tự

Nhưng thay vì Dispose()bối cảnh và tạo lại, tôi chỉ cần tách rời các thực thể đãSaveChanges()

public void AddAndSave<TEntity>(List<TEntity> entities) where TEntity : class {

const int CommitCount = 1000; //set your own best performance number here
int currentCount = 0;

while (currentCount < entities.Count())
{
    //make sure it don't commit more than the entities you have
    int commitCount = CommitCount;
    if ((entities.Count - currentCount) < commitCount)
        commitCount = entities.Count - currentCount;

    //e.g. Add entities [ i = 0 to 999, 1000 to 1999, ... , n to n+999... ] to conext
    for (int i = currentCount; i < (currentCount + commitCount); i++)        
        _context.Entry(entities[i]).State = System.Data.EntityState.Added;
        //same as calling _context.Set<TEntity>().Add(entities[i]);       

    //commit entities[n to n+999] to database
    _context.SaveChanges();

    //detach all entities in the context that committed to database
    //so it won't overload the context
    for (int i = currentCount; i < (currentCount + commitCount); i++)
        _context.Entry(entities[i]).State = System.Data.EntityState.Detached;

    currentCount += commitCount;
} }

bọc nó bằng thử bắt và TrasactionScope()nếu bạn cần, không hiển thị chúng ở đây để giữ mã sạch


1
Điều đó đã làm chậm quá trình chèn (AddRange) bằng Entity Framework 6.0. Chèn 20.000 hàng đã tăng từ khoảng 101 giây lên 118 giây.
À.

1
@Stephen Ho: Tôi cũng đang cố gắng tránh xử lý bối cảnh của mình. Tôi có thể hiểu điều này chậm hơn so với việc tạo lại bối cảnh, nhưng tôi muốn biết liệu bạn có thấy điều này đủ nhanh hơn không tạo lại bối cảnh mà bằng một tập xác nhận.
Học viên

@Learner: Tôi nghĩ nó nhanh hơn tái tạo bối cảnh. Nhưng tôi thực sự không nhớ bây giờ vì cuối cùng tôi đã chuyển sang sử dụng SqlBulkCopy.
Stephen Ho

Cuối cùng tôi đã phải sử dụng kỹ thuật này bởi vì, vì một số lý do kỳ lạ, có một số theo dõi còn sót lại xảy ra ở vòng thứ hai trong vòng lặp while, mặc dù tôi có mọi thứ được gói trong một câu lệnh sử dụng và thậm chí được gọi là Dispose () trên DbContext . Khi tôi thêm vào bối cảnh (trên đường chuyền thứ 2), tổng số bối cảnh sẽ tăng lên 6 thay vì chỉ một. Các mục khác được thêm tùy ý đã được chèn trong vòng đầu tiên qua vòng lặp while, vì vậy lệnh gọi SaveChanges sẽ thất bại ở lượt thứ hai (vì lý do rõ ràng).
Hallmanac

9

Tôi biết đây là một câu hỏi rất cũ, nhưng một anh chàng ở đây đã nói rằng đã phát triển một phương pháp mở rộng để sử dụng chèn số lượng lớn với EF và khi tôi kiểm tra, tôi phát hiện ra rằng thư viện có giá $ 599 ngày hôm nay (cho một nhà phát triển). Có thể nó có ý nghĩa đối với toàn bộ thư viện, tuy nhiên đối với việc chèn số lượng lớn thì điều này là quá nhiều.

Đây là một phương pháp mở rộng rất đơn giản tôi đã thực hiện. Tôi sử dụng nó trên cặp với cơ sở dữ liệu trước tiên (không được kiểm tra bằng mã trước, nhưng tôi nghĩ rằng nó hoạt động như nhau). Thay đổi YourEntitiesvới tên của bối cảnh của bạn:

public partial class YourEntities : DbContext
{
    public async Task BulkInsertAllAsync<T>(IEnumerable<T> entities)
    {
        using (var conn = new SqlConnection(Database.Connection.ConnectionString))
        {
            await conn.OpenAsync();

            Type t = typeof(T);

            var bulkCopy = new SqlBulkCopy(conn)
            {
                DestinationTableName = GetTableName(t)
            };

            var table = new DataTable();

            var properties = t.GetProperties().Where(p => p.PropertyType.IsValueType || p.PropertyType == typeof(string));

            foreach (var property in properties)
            {
                Type propertyType = property.PropertyType;
                if (propertyType.IsGenericType &&
                    propertyType.GetGenericTypeDefinition() == typeof(Nullable<>))
                {
                    propertyType = Nullable.GetUnderlyingType(propertyType);
                }

                table.Columns.Add(new DataColumn(property.Name, propertyType));
            }

            foreach (var entity in entities)
            {
                table.Rows.Add(
                    properties.Select(property => property.GetValue(entity, null) ?? DBNull.Value).ToArray());
            }

            bulkCopy.BulkCopyTimeout = 0;
            await bulkCopy.WriteToServerAsync(table);
        }
    }

    public void BulkInsertAll<T>(IEnumerable<T> entities)
    {
        using (var conn = new SqlConnection(Database.Connection.ConnectionString))
        {
            conn.Open();

            Type t = typeof(T);

            var bulkCopy = new SqlBulkCopy(conn)
            {
                DestinationTableName = GetTableName(t)
            };

            var table = new DataTable();

            var properties = t.GetProperties().Where(p => p.PropertyType.IsValueType || p.PropertyType == typeof(string));

            foreach (var property in properties)
            {
                Type propertyType = property.PropertyType;
                if (propertyType.IsGenericType &&
                    propertyType.GetGenericTypeDefinition() == typeof(Nullable<>))
                {
                    propertyType = Nullable.GetUnderlyingType(propertyType);
                }

                table.Columns.Add(new DataColumn(property.Name, propertyType));
            }

            foreach (var entity in entities)
            {
                table.Rows.Add(
                    properties.Select(property => property.GetValue(entity, null) ?? DBNull.Value).ToArray());
            }

            bulkCopy.BulkCopyTimeout = 0;
            bulkCopy.WriteToServer(table);
        }
    }

    public string GetTableName(Type type)
    {
        var metadata = ((IObjectContextAdapter)this).ObjectContext.MetadataWorkspace;
        var objectItemCollection = ((ObjectItemCollection)metadata.GetItemCollection(DataSpace.OSpace));

        var entityType = metadata
                .GetItems<EntityType>(DataSpace.OSpace)
                .Single(e => objectItemCollection.GetClrType(e) == type);

        var entitySet = metadata
            .GetItems<EntityContainer>(DataSpace.CSpace)
            .Single()
            .EntitySets
            .Single(s => s.ElementType.Name == entityType.Name);

        var mapping = metadata.GetItems<EntityContainerMapping>(DataSpace.CSSpace)
                .Single()
                .EntitySetMappings
                .Single(s => s.EntitySet == entitySet);

        var table = mapping
            .EntityTypeMappings.Single()
            .Fragments.Single()
            .StoreEntitySet;

        return (string)table.MetadataProperties["Table"].Value ?? table.Name;
    }
}

Bạn có thể sử dụng nó để chống lại bất kỳ bộ sưu tập nào kế thừa từ đó IEnumerable, như thế:

await context.BulkInsertAllAsync(items);

vui lòng hoàn thành mã ví dụ của bạn Số lượng lớn Bản sao
Seabizkit

1
Nó đã ở đây:await bulkCopy.WriteToServerAsync(table);
Guilherme

Có lẽ tôi đã không rõ ràng, trong bài viết của bạn, bạn đề nghị bạn đã thực hiện một phần mở rộng ... điều đó có nghĩa là tôi không cần lib phần 3, trong khi thực tế trong cả hai phương pháp đều sử dụng lib SqlBulkCopy. Điều này hoàn toàn dựa vào SqlBulkCopy, khi tôi hỏi tại sao massCopy đến từ đâu, đó là một lib mở rộng mà bạn đã viết một lib mở rộng trên đầu trang. Sẽ có ý nghĩa hơn để nói ở đây là cách tôi sử dụng lib SqlBulkCopy.
Seabizkit

nên sử dụng Conn.OpenAsync trong phiên bản async
Robert

6

Cố gắng sử dụng một thủ tục được lưu trữ sẽ nhận được một XML dữ liệu mà bạn muốn chèn.


9
Truyền dữ liệu dưới dạng XML là không cần thiết nếu bạn không muốn lưu trữ chúng dưới dạng XML. Trong SQL 2008, bạn có thể sử dụng tham số có giá trị bảng.
Ladislav Mrnka

tôi đã không làm rõ điều này nhưng tôi cũng cần hỗ trợ SQL 2005
Bongo Sharp

4

Tôi đã thực hiện một phần mở rộng chung của ví dụ @Slauma s ở trên;

public static class DataExtensions
{
    public static DbContext AddToContext<T>(this DbContext context, object entity, int count, int commitCount, bool recreateContext, Func<DbContext> contextCreator)
    {
        context.Set(typeof(T)).Add((T)entity);

        if (count % commitCount == 0)
        {
            context.SaveChanges();
            if (recreateContext)
            {
                context.Dispose();
                context = contextCreator.Invoke();
                context.Configuration.AutoDetectChangesEnabled = false;
            }
        }
        return context;
    }
}

Sử dụng:

public void AddEntities(List<YourEntity> entities)
{
    using (var transactionScope = new TransactionScope())
    {
        DbContext context = new YourContext();
        int count = 0;
        foreach (var entity in entities)
        {
            ++count;
            context = context.AddToContext<TenancyNote>(entity, count, 100, true,
                () => new YourContext());
        }
        context.SaveChanges();
        transactionScope.Complete();
    }
}

4

Tôi đang tìm cách nhanh nhất để chèn vào Entity Framework

Có một số thư viện của bên thứ ba hỗ trợ Chèn hàng loạt có sẵn:

  • Z.EntityFramework.Extensions ( Được khuyến nghị )
  • Khả năng
  • EntityFramework.BulkInsert

Xem: Thư viện Chèn hàng loạt khung thực thể

Hãy cẩn thận, khi chọn một thư viện chèn số lượng lớn. Chỉ phần mở rộng khung thực thể hỗ trợ tất cả các loại liên kết và kế thừa và đó là duy nhất vẫn được hỗ trợ.


Tuyên bố miễn trừ trách nhiệm : Tôi là chủ sở hữu của Phần mở rộng khung thực thể

Thư viện này cho phép bạn thực hiện tất cả các hoạt động hàng loạt bạn cần cho các tình huống của mình:

  • Lưu số lượng lớn
  • Chèn số lượng lớn
  • Xóa hàng loạt
  • Cập nhật hàng loạt
  • Hợp nhất số lượng lớn

Thí dụ

// Easy to use
context.BulkSaveChanges();

// Easy to customize
context.BulkSaveChanges(bulk => bulk.BatchSize = 100);

// Perform Bulk Operations
context.BulkDelete(customers);
context.BulkInsert(customers);
context.BulkUpdate(customers);

// Customize Primary Key
context.BulkMerge(customers, operation => {
   operation.ColumnPrimaryKeyExpression = 
        customer => customer.Code;
});

19
đây là một phần mở rộng tuyệt vời nhưng không miễn phí .
Okan Kocyigit

2
Câu trả lời này khá hay và EntityFramework.BulkInsert thực hiện chèn hàng loạt 15K hàng trong 1,5 giây, hoạt động khá tốt cho quy trình nội bộ như Windows Service.
Mục sư Cortes

4
Vâng, 600 đô la cho chèn số lượng lớn. Tổng giá trị nó.
eocron

1
@eocron Yeat thật đáng giá nếu bạn sử dụng nó một cách thương mại. Tôi không thấy bất kỳ vấn đề nào với 600 đô la cho một thứ mà tôi không phải mất hàng giờ để tự xây dựng nó, điều này sẽ khiến tôi tốn hơn 600 đô la. Có nó tốn tiền nhưng nhìn vào tỷ lệ hàng giờ của tôi đó là tiền chi tiêu tốt!
Jordy van Eijk

3

Sử dụng SqlBulkCopy:

void BulkInsert(GpsReceiverTrack[] gpsReceiverTracks)
{
    if (gpsReceiverTracks == null)
    {
        throw new ArgumentNullException(nameof(gpsReceiverTracks));
    }

    DataTable dataTable = new DataTable("GpsReceiverTracks");
    dataTable.Columns.Add("ID", typeof(int));
    dataTable.Columns.Add("DownloadedTrackID", typeof(int));
    dataTable.Columns.Add("Time", typeof(TimeSpan));
    dataTable.Columns.Add("Latitude", typeof(double));
    dataTable.Columns.Add("Longitude", typeof(double));
    dataTable.Columns.Add("Altitude", typeof(double));

    for (int i = 0; i < gpsReceiverTracks.Length; i++)
    {
        dataTable.Rows.Add
        (
            new object[]
            {
                    gpsReceiverTracks[i].ID,
                    gpsReceiverTracks[i].DownloadedTrackID,
                    gpsReceiverTracks[i].Time,
                    gpsReceiverTracks[i].Latitude,
                    gpsReceiverTracks[i].Longitude,
                    gpsReceiverTracks[i].Altitude
            }
        );
    }

    string connectionString = (new TeamTrackerEntities()).Database.Connection.ConnectionString;
    using (var connection = new SqlConnection(connectionString))
    {
        connection.Open();
        using (var transaction = connection.BeginTransaction())
        {
            using (var sqlBulkCopy = new SqlBulkCopy(connection, SqlBulkCopyOptions.TableLock, transaction))
            {
                sqlBulkCopy.DestinationTableName = dataTable.TableName;
                foreach (DataColumn column in dataTable.Columns)
                {
                    sqlBulkCopy.ColumnMappings.Add(column.ColumnName, column.ColumnName);
                }

                sqlBulkCopy.WriteToServer(dataTable);
            }
            transaction.Commit();
        }
    }

    return;
}

3

Một trong những cách nhanh nhất để lưu danh sách, bạn phải áp dụng mã sau đây

context.Configuration.AutoDetectChangesEnabled = false;
context.Configuration.ValidateOnSaveEnabled = false;

AutoDetectChangesEnables = false

Thêm, AddRange & SaveChanges: Không phát hiện thay đổi.

ValidateOnSaveEnables = false;

Không phát hiện theo dõi thay đổi

Bạn phải thêm nuget

Install-Package Z.EntityFramework.Extensions

Bây giờ bạn có thể sử dụng mã sau đây

var context = new MyContext();

context.Configuration.AutoDetectChangesEnabled = false;
context.Configuration.ValidateOnSaveEnabled = false;

context.BulkInsert(list);
context.BulkSaveChanges();

Tôi có thể sử dụng Mã mẫu của bạn để cập nhật hàng loạt không?
AminGolmahalle

4
Thư viện Z không miễn phí
SHADOW.NET

3

SqlBulkCopy là siêu nhanh

Đây là triển khai của tôi:

// at some point in my calling code, I will call:
var myDataTable = CreateMyDataTable();
myDataTable.Rows.Add(Guid.NewGuid,tableHeaderId,theName,theValue); // e.g. - need this call for each row to insert

var efConnectionString = ConfigurationManager.ConnectionStrings["MyWebConfigEfConnection"].ConnectionString;
var efConnectionStringBuilder = new EntityConnectionStringBuilder(efConnectionString);
var connectionString = efConnectionStringBuilder.ProviderConnectionString;
BulkInsert(connectionString, myDataTable);

private DataTable CreateMyDataTable()
{
    var myDataTable = new DataTable { TableName = "MyTable"};
// this table has an identity column - don't need to specify that
    myDataTable.Columns.Add("MyTableRecordGuid", typeof(Guid));
    myDataTable.Columns.Add("MyTableHeaderId", typeof(int));
    myDataTable.Columns.Add("ColumnName", typeof(string));
    myDataTable.Columns.Add("ColumnValue", typeof(string));
    return myDataTable;
}

private void BulkInsert(string connectionString, DataTable dataTable)
{
    using (var connection = new SqlConnection(connectionString))
    {
        connection.Open();
        SqlTransaction transaction = null;
        try
        {
            transaction = connection.BeginTransaction();

            using (var sqlBulkCopy = new SqlBulkCopy(connection, SqlBulkCopyOptions.TableLock, transaction))
            {
                sqlBulkCopy.DestinationTableName = dataTable.TableName;
                foreach (DataColumn column in dataTable.Columns) {
                    sqlBulkCopy.ColumnMappings.Add(column.ColumnName, column.ColumnName);
                }

                sqlBulkCopy.WriteToServer(dataTable);
            }
            transaction.Commit();
        }
        catch (Exception)
        {
            transaction?.Rollback();
            throw;
        }
    }
}

3

[Cập nhật 2019] EF Core 3.1

Theo những gì đã nói ở trên, việc vô hiệu hóa AutoDetectChangesEnables trong EF Core hoạt động hoàn hảo: thời gian chèn được chia cho 100 (từ nhiều phút đến vài giây, 10k bản ghi với các mối quan hệ bảng chéo)

Mã cập nhật là:

  context.ChangeTracker.AutoDetectChangesEnabled = false;
            foreach (IRecord record in records) {
               //Add records to your database        
            }
            context.ChangeTracker.DetectChanges();
            context.SaveChanges();
            context.ChangeTracker.AutoDetectChangesEnabled = true; //do not forget to re-enable

2

Dưới đây là so sánh hiệu năng giữa việc sử dụng Entity Framework và sử dụng lớp SqlBulkCopy trên một ví dụ thực tế: Cách chèn hàng loạt các đối tượng phức tạp vào cơ sở dữ liệu SQL Server

Như những người khác đã nhấn mạnh, ORM không có nghĩa là được sử dụng trong các hoạt động hàng loạt. Họ cung cấp sự linh hoạt, tách biệt các mối quan tâm và các lợi ích khác, nhưng các hoạt động hàng loạt (ngoại trừ việc đọc số lượng lớn) không phải là một trong số đó.


2

Một tùy chọn khác là sử dụng SqlBulkTools có sẵn từ Nuget. Nó rất dễ sử dụng và có một số tính năng mạnh mẽ.

Thí dụ:

var bulk = new BulkOperations();
var books = GetBooks();

using (TransactionScope trans = new TransactionScope())
{
    using (SqlConnection conn = new SqlConnection(ConfigurationManager
    .ConnectionStrings["SqlBulkToolsTest"].ConnectionString))
    {
        bulk.Setup<Book>()
            .ForCollection(books)
            .WithTable("Books") 
            .AddAllColumns()
            .BulkInsert()
            .Commit(conn);
    }

    trans.Complete();
}

Xem tài liệu để biết thêm ví dụ và sử dụng nâng cao. Tuyên bố miễn trừ trách nhiệm: Tôi là tác giả của thư viện này và mọi quan điểm đều là quan điểm của riêng tôi.


2
Dự án này đã bị xóa khỏi cả NuGet và GitHub.
0xced

1

Theo hiểu biết của tôi có no BulkInserttrong EntityFrameworkđể tăng hiệu suất của chèn khổng lồ.

Trong kịch bản này, bạn có thể đi với SqlBulkCopy trong ADO.netđể giải quyết vấn đề của bạn


Tôi đã xem qua lớp học đó, nhưng nó dường như được định hướng nhiều hơn cho các phần chèn vào bảng, phải không?
Bongo Sharp

Không chắc ý của bạn là gì, nó có quá tải WriteToServermất một DataTable.

không bạn có thể chèn từ các đối tượng .Net vào SQL. Bạn đang tìm kiếm cái gì?
anishMarokey

Một cách để chèn hàng ngàn bản ghi có khả năng vào cơ sở dữ liệu trong khối Giao dịch
Bongo Sharp

bạn có thể sử dụng .Net TransactionScope technet.microsoft.com/en-us/l
Library / bb896149.aspx

1

Bạn đã bao giờ thử chèn thông qua một nhân viên nền hoặc nhiệm vụ?

Trong trường hợp của tôi, tôi đang chèn 7760 thanh ghi, được phân phối trong 182 bảng khác nhau với các mối quan hệ khóa ngoài (theo NavigationProperives).

Không có nhiệm vụ, phải mất 2 phút rưỡi. Trong một Nhiệm vụ ( Task.Factory.StartNew(...)), mất 15 giây.

Tôi chỉ làm SaveChanges()sau khi thêm tất cả các thực thể vào bối cảnh. (để đảm bảo tính toàn vẹn dữ liệu)


2
Tôi khá chắc chắn rằng bối cảnh không phải là chủ đề an toàn. Bạn có kiểm tra để đảm bảo rằng tất cả các thực thể đã được lưu?
Daniel Varod

Tôi biết toàn bộ khung thực thể không phải là chủ đề an toàn, nhưng tôi chỉ thêm các đối tượng vào bối cảnh và lưu ở cuối ... Nó hoạt động hoàn hảo ở đây.
Rafael AMS

Vì vậy, bạn đang gọi DbContext.SaveChanges () trong luồng chính, nhưng việc thêm các thực thể vào ngữ cảnh được thực hiện trong luồng nền, phải không?
Prokurors 31/03 '

1
Có, thêm dữ liệu bên trong các chủ đề; chờ tất cả kết thúc; và lưu các thay đổi trong luồng chính
Rafael AMS

Mặc dù tôi nghĩ rằng cách này là nguy hiểm và dễ mắc sai lầm, tôi thấy nó rất thú vị.
Học viên

1

Tất cả các giải pháp được viết ở đây không giúp ích gì vì khi bạn thực hiện SaveChanges (), các câu lệnh chèn được gửi đến cơ sở dữ liệu từng cái một, đó là cách Entity hoạt động.

Và nếu chuyến đi của bạn đến cơ sở dữ liệu và trở lại là 50 ms chẳng hạn thì thời gian cần thiết để chèn là số lượng bản ghi x 50 ms.

Bạn phải sử dụng BulkInsert, đây là liên kết: https://efbulkinsert.codeplex.com/

Tôi đã giảm thời gian chèn từ 5-6 phút xuống còn 10-12 giây bằng cách sử dụng nó.



1

[GIẢI PHÁP MỚI CHO POSTGRESQL] Này, tôi biết đó là một bài viết khá cũ, nhưng gần đây tôi đã gặp phải vấn đề tương tự, nhưng chúng tôi đã sử dụng Postgresql. Tôi muốn sử dụng bulkinsert hiệu quả, điều này hóa ra khá khó khăn. Tôi chưa tìm thấy bất kỳ thư viện miễn phí thích hợp nào để làm như vậy trên DB này. Tôi chỉ tìm thấy người trợ giúp này: https://bytefish.de/blog/postgresql_bulk_insert/ cũng có trên Nuget. Tôi đã viết một trình ánh xạ nhỏ, tự động ánh xạ các thuộc tính theo cách Entity Framework:

public static PostgreSQLCopyHelper<T> CreateHelper<T>(string schemaName, string tableName)
        {
            var helper = new PostgreSQLCopyHelper<T>("dbo", "\"" + tableName + "\"");
            var properties = typeof(T).GetProperties();
            foreach(var prop in properties)
            {
                var type = prop.PropertyType;
                if (Attribute.IsDefined(prop, typeof(KeyAttribute)) || Attribute.IsDefined(prop, typeof(ForeignKeyAttribute)))
                    continue;
                switch (type)
                {
                    case Type intType when intType == typeof(int) || intType == typeof(int?):
                        {
                            helper = helper.MapInteger("\"" + prop.Name + "\"",  x => (int?)typeof(T).GetProperty(prop.Name).GetValue(x, null));
                            break;
                        }
                    case Type stringType when stringType == typeof(string):
                        {
                            helper = helper.MapText("\"" + prop.Name + "\"", x => (string)typeof(T).GetProperty(prop.Name).GetValue(x, null));
                            break;
                        }
                    case Type dateType when dateType == typeof(DateTime) || dateType == typeof(DateTime?):
                        {
                            helper = helper.MapTimeStamp("\"" + prop.Name + "\"", x => (DateTime?)typeof(T).GetProperty(prop.Name).GetValue(x, null));
                            break;
                        }
                    case Type decimalType when decimalType == typeof(decimal) || decimalType == typeof(decimal?):
                        {
                            helper = helper.MapMoney("\"" + prop.Name + "\"", x => (decimal?)typeof(T).GetProperty(prop.Name).GetValue(x, null));
                            break;
                        }
                    case Type doubleType when doubleType == typeof(double) || doubleType == typeof(double?):
                        {
                            helper = helper.MapDouble("\"" + prop.Name + "\"", x => (double?)typeof(T).GetProperty(prop.Name).GetValue(x, null));
                            break;
                        }
                    case Type floatType when floatType == typeof(float) || floatType == typeof(float?):
                        {
                            helper = helper.MapReal("\"" + prop.Name + "\"", x => (float?)typeof(T).GetProperty(prop.Name).GetValue(x, null));
                            break;
                        }
                    case Type guidType when guidType == typeof(Guid):
                        {
                            helper = helper.MapUUID("\"" + prop.Name + "\"", x => (Guid)typeof(T).GetProperty(prop.Name).GetValue(x, null));
                            break;
                        }
                }
            }
            return helper;
        }

Tôi sử dụng nó theo cách sau (tôi có thực thể tên là Cam kết):

var undertakingHelper = BulkMapper.CreateHelper<Model.Undertaking>("dbo", nameof(Model.Undertaking));
undertakingHelper.SaveAll(transaction.UnderlyingTransaction.Connection as Npgsql.NpgsqlConnection, undertakingsToAdd));

Tôi đã đưa ra một ví dụ với giao dịch, nhưng nó cũng có thể được thực hiện với kết nối bình thường được lấy từ ngữ cảnh. undertakingsToAdd là vô số các bản ghi thực thể bình thường, mà tôi muốn đưa hàng loạt vào DB.

Giải pháp này, mà tôi đã nhận được sau vài giờ nghiên cứu và thử, là vì bạn có thể mong đợi nhanh hơn nhiều và cuối cùng dễ sử dụng và miễn phí! Tôi thực sự khuyên bạn nên sử dụng giải pháp này, không chỉ vì những lý do nêu trên mà còn bởi vì đó là giải pháp duy nhất mà tôi không gặp vấn đề gì với chính Postgresql, nhiều giải pháp khác hoạt động hoàn hảo, ví dụ như với SqlServer.


0

Bí quyết là chèn vào một bảng dàn trống giống hệt nhau. Chèn được làm sáng nhanh chóng. Sau đó chạy một đơn chèn từ đó vào bảng lớn chính của bạn. Sau đó cắt ngắn bảng dàn dựng sẵn sàng cho đợt tiếp theo.

I E.

insert into some_staging_table using Entity Framework.

-- Single insert into main table (this could be a tiny stored proc call)
insert into some_main_already_large_table (columns...)
   select (columns...) from some_staging_table
truncate table some_staging_table

Sử dụng EF, thêm tất cả các bản ghi của bạn vào một bảng phân loại trống. Sau đó sử dụng SQL để chèn vào bảng chính (lớn và chậm) trong một lệnh SQL duy nhất . Sau đó làm trống bàn dàn của bạn. Đó là một cách rất nhanh để chèn nhiều dữ liệu vào một bảng đã lớn.
Simon Hughes

13
Khi bạn nói sử dụng EF, hãy thêm các bản ghi vào bảng phân tầng, bạn đã thực sự thử điều này với EF chưa? Vì EF đưa ra một cuộc gọi riêng đến cơ sở dữ liệu với mỗi lần chèn, tôi nghi ngờ bạn sẽ thấy một cú đánh hoàn hảo tương tự mà OP đang cố tránh. Làm thế nào để bảng dàn dựng tránh vấn đề này?
Jim Wooley

-1

Nhưng, trong hơn (4.000) phần chèn, tôi khuyên bạn nên sử dụng quy trình được lưu trữ. thời gian trôi qua. Tôi đã chèn nó 11.788 hàng trong 20 "nhập mô tả hình ảnh ở đây

đó là mã

 public void InsertDataBase(MyEntity entity)
    {
        repository.Database.ExecuteSqlCommand("sp_mystored " +
                "@param1, @param2"
                 new SqlParameter("@param1", entity.property1),
                 new SqlParameter("@param2", entity.property2));
    }

-1

Sử dụng quy trình được lưu trữ lấy dữ liệu đầu vào dưới dạng xml để chèn dữ liệu.

Từ mã c # của bạn vượt qua chèn dữ liệu dưới dạng xml.

ví dụ: trong c #, cú pháp sẽ như thế này:

object id_application = db.ExecuteScalar("procSaveApplication", xml)

-7

Sử dụng kỹ thuật này để tăng tốc độ chèn bản ghi trong Entity Framework. Ở đây tôi sử dụng một thủ tục lưu trữ đơn giản để chèn các hồ sơ. Và để thực hiện thủ tục được lưu trữ này, tôi sử dụng phương thức EntromSql () của Entity Framework để thực thi SQL thô.

Mã thủ tục được lưu trữ:

CREATE PROCEDURE TestProc
@FirstParam VARCHAR(50),
@SecondParam VARCHAR(50)

AS
  Insert into SomeTable(Name, Address) values(@FirstParam, @SecondParam) 
GO

Tiếp theo, lặp qua tất cả 4000 hồ sơ của bạn và thêm mã Khung thực thể thực thi việc lưu trữ

thủ tục buộc mỗi vòng thứ 100.

Đối với điều này, tôi tạo một truy vấn chuỗi để thực hiện thủ tục này, tiếp tục nối thêm vào nó mỗi bộ bản ghi.

Sau đó kiểm tra nó, vòng lặp đang chạy trong bội số của 100 và trong trường hợp đó thực hiện nó bằng cách sử dụng .FromSql().

Vì vậy, đối với 4000 hồ sơ, tôi chỉ phải thực hiện thủ tục chỉ với 4000/100 = 40 lần .

Kiểm tra mã dưới đây:

string execQuery = "";
var context = new MyContext();
for (int i = 0; i < 4000; i++)
{
    execQuery += "EXEC TestProc @FirstParam = 'First'" + i + "'', @SecondParam = 'Second'" + i + "''";

    if (i % 100 == 0)
    {
        context.Student.FromSql(execQuery);
        execQuery = "";
    }
}

Điều này có thể hiệu quả nhưng tương đương với KHÔNG sử dụng khung thực thể. Câu hỏi của OP là làm thế nào để tối đa hóa hiệu quả trong bối cảnh Entity Framework
kall2sollies
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.