Fastest Way of Inserting in Entity Framework

前端 未结 30 2189
鱼传尺愫
鱼传尺愫 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:52

    I agree with Adam Rackis. SqlBulkCopy is the fastest way of transferring bulk records from one data source to another. I used this to copy 20K records and it took less than 3 seconds. Have a look at the example below.

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

    I would recommend this article on how to do bulk inserts using EF.

    Entity Framework and slow bulk INSERTs

    He explores these areas and compares perfomance:

    1. Default EF (57 minutes to complete adding 30,000 records)
    2. Replacing with ADO.NET Code (25 seconds for those same 30,000)
    3. Context Bloat- Keep the active Context Graph small by using a new context for each Unit of Work (same 30,000 inserts take 33 seconds)
    4. Large Lists - Turn off AutoDetectChangesEnabled (brings the time down to about 20 seconds)
    5. Batching (down to 16 seconds)
    6. DbTable.AddRange() - (performance is in the 12 range)
    0 讨论(0)
  • 2020-11-21 05:52

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

    All the solutions written here don't help because when you do SaveChanges(), insert statements are sent to database one by one, that's how Entity works.

    And if your trip to database and back is 50 ms for instance then time needed for insert is number of records x 50 ms.

    You have to use BulkInsert, here is the link: https://efbulkinsert.codeplex.com/

    I got insert time reduced from 5-6 minutes to 10-12 seconds by using it.

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

    I have made an generic extension of @Slauma s example above;

    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;
        }
    }
    

    Usage:

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

    Try to use a Stored Procedure that will get an XML of the data that you want to insert.

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