Fastest Way of Inserting in Entity Framework

前端 未结 30 2183
鱼传尺愫
鱼传尺愫 2020-11-21 05:23

I\'m looking for the fastest way of inserting into Entity Framework.

I\'m asking this because of the scenario where you have an active TransactionScope a

相关标签:
30条回答
  • 2020-11-21 05:37

    [2019 Update] EF Core 3.1

    Following what have been said above, disabling AutoDetectChangesEnabled in EF Core worked perfectly: the insertion time was divided by 100 (from many minutes to a few seconds, 10k records with cross tables relationships)

    The updated code is :

      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
    
    0 讨论(0)
  • 2020-11-21 05:37

    Yes, SqlBulkUpdate is indeed the fastest tool for this type of task. I wanted to find "least effort" generic way for me in .NET Core so I ended up using great library from Marc Gravell called FastMember and writing one tiny extension method for entity framework DB context. Works lightning fast:

    using System.Collections.Generic;
    using System.Linq;
    using FastMember;
    using Microsoft.Data.SqlClient;
    using Microsoft.EntityFrameworkCore;
    
    namespace Services.Extensions
    {
        public static class DbContextExtensions
        {
            public static void BulkCopyToServer<T>(this DbContext db, IEnumerable<T> collection)
            {
                var messageEntityType = db.Model.FindEntityType(typeof(T));
    
                var tableName = messageEntityType.GetSchema() + "." + messageEntityType.GetTableName();
                var tableColumnMappings = messageEntityType.GetProperties()
                    .ToDictionary(p => p.PropertyInfo.Name, p => p.GetColumnName());
    
                using (var connection = new SqlConnection(db.Database.GetDbConnection().ConnectionString))
                using (var bulkCopy = new SqlBulkCopy(connection))
                {
                    foreach (var (field, column) in tableColumnMappings)
                    {
                        bulkCopy.ColumnMappings.Add(field, column);
                    }
    
                    using (var reader = ObjectReader.Create(collection, tableColumnMappings.Keys.ToArray()))
                    {
                        bulkCopy.DestinationTableName = tableName;
                        connection.Open();
                        bulkCopy.WriteToServer(reader);
                        connection.Close();
                    }
                }
            }
        }
    }
    
    0 讨论(0)
  • 2020-11-21 05:38

    As other people have said SqlBulkCopy is the way to do it if you want really good insert performance.

    It's a bit cumbersome to implement but there are libraries that can help you with it. There are a few out there but I will shamelesslyplug my own library this time: https://github.com/MikaelEliasson/EntityFramework.Utilities#batch-insert-entities

    The only code you would need is:

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

    So how much faster is it? Very hard to say because it depends on so many factors, computer performance, network, object size etc etc. The performance tests I've made suggests 25k entities can be inserted at around 10s the standard way on localhost IF you optimize your EF configuration like mentioned in the other answers. With EFUtilities that takes about 300ms. Even more interesting is that I have saved around 3 millions entities in under 15 seconds using this method, averaging around 200k entities per second.

    The one problem is ofcourse if you need to insert releated data. This can be done efficently into sql server using the method above but it requires you to have an Id generation strategy that let you generate id's in the app-code for the parent so you can set the foreign keys. This can be done using GUIDs or something like HiLo id generation.

    0 讨论(0)
  • 2020-11-21 05:39

    The secret is to insert into an identical blank staging table. Inserts are lightening quick. Then run a single insert from that into your main large table. Then truncate the staging table ready for the next batch.

    ie.

    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
    
    0 讨论(0)
  • 2020-11-21 05:40

    I'm looking for the fastest way of inserting into Entity Framework

    There are some third-party libraries supporting Bulk Insert available:

    • Z.EntityFramework.Extensions (Recommended)
    • EFUtilities
    • EntityFramework.BulkInsert

    See: Entity Framework Bulk Insert library

    Be careful, when choosing a bulk insert library. Only Entity Framework Extensions supports all kind of associations and inheritances and it's the only one still supported.


    Disclaimer: I'm the owner of Entity Framework Extensions

    This library allows you to perform all bulk operations you need for your scenarios:

    • Bulk SaveChanges
    • Bulk Insert
    • Bulk Delete
    • Bulk Update
    • Bulk Merge

    Example

    // 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;
    });
    
    0 讨论(0)
  • 2020-11-21 05:42

    Have you ever tried to insert through a background worker or task?

    In my case, im inserting 7760 registers, distributed in 182 different tables with foreign key relationships ( by NavigationProperties).

    Without the task, it took 2 minutes and a half. Within a Task ( Task.Factory.StartNew(...) ), it took 15 seconds.

    Im only doing the SaveChanges() after adding all the entities to the context. (to ensure data integrity)

    0 讨论(0)
提交回复
热议问题