Самый быстрый способ вставки в Entity Framework - PullRequest
614 голосов
/ 09 мая 2011

Я ищу самый быстрый способ вставки в Entity Framework.

Я спрашиваю об этом из-за сценария, когда у вас есть активный TransactionScope, и вставка огромна (4000+).Потенциально он может длиться более 10 минут (время ожидания транзакций по умолчанию), что приведет к незавершенной транзакции.

Ответы [ 28 ]

5 голосов
/ 09 мая 2011

Попробуйте использовать хранимую процедуру , которая получит XML-данные, которые вы хотите вставить.

4 голосов
/ 22 февраля 2019

как здесь никогда не упоминалось, я хочу рекомендовать EFCore.BulkExtensions здесь

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);
4 голосов
/ 06 января 2017

Я ищу самый быстрый способ вставки в Entity Framework

Существует несколько сторонних библиотек, поддерживающих групповую вставку:

  • Z.EntityFramework.Extensions ( Рекомендуется )
  • EFUtilities
  • EntityFramework.BulkInsert

См .: Библиотека Entity Framework Bulk Insert

Будьте внимательны при выборе библиотеки массовой вставки. Только Entity Framework Extensions поддерживает все виды ассоциаций и наследований, и это единственное, что все еще поддерживается.


Отказ от ответственности : я владелец Entity Framework Extensions

Эта библиотека позволяет вам выполнять все массовые операции, необходимые для ваших сценариев:

  • Bulk SaveChanges
  • Массовая вставка
  • Массовое удаление
  • Массовое обновление
  • Массовое слияние

Пример

// 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;
});
4 голосов
/ 25 ноября 2015

Я сделал общее расширение примера @Slauma s выше;

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

Использование:

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();
    }
}
2 голосов
/ 02 октября 2018

Один из самых быстрых способов сохранить список Вы должны применить следующий код

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

AutoDetectChangesEnabled = false

Add, AddRange & SaveChanges: не обнаруживает изменений.

ValidateOnSaveEnabled = false;

Не обнаруживает изменения трекер

Вы должны добавить nuget

Install-Package Z.EntityFramework.Extensions

Теперь вы можете использовать следующий код

var context = new MyContext();

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

context.BulkInsert(list);
context.BulkSaveChanges();
2 голосов
/ 05 июня 2017

Использование 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;
}
2 голосов
/ 13 сентября 2016

Другой вариант - использовать SqlBulkTools, доступные от Nuget. Он очень прост в использовании и обладает рядом мощных функций.

Пример:

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

См. документацию для большего количества примеров и расширенного использования. Отказ от ответственности: я являюсь автором этой библиотеки, и любые взгляды имеют собственное мнение.

2 голосов
/ 17 июля 2013

Вот сравнение производительности между использованием Entity Framework и классом SqlBulkCopy на реалистическом примере: Как массово вставить сложные объекты в базу данных SQL Server

Как уже подчеркивали другие, ORM не предназначены для массовых операций. Они предлагают гибкость, разделение проблем и другие преимущества, но массовые операции (кроме массового чтения) не являются одними из них.

1 голос
/ 06 сентября 2017

[НОВОЕ РЕШЕНИЕ ДЛЯ POSTGRESQL] Эй, я знаю, что это довольно старый пост, но недавно я столкнулся с подобной проблемой, но мы использовали Postgresql. Я хотел использовать эффективный булькинсерт, что оказалось довольно сложно. Я не нашел подходящей бесплатной библиотеки для этой базы данных. Я нашел только этого помощника: https://bytefish.de/blog/postgresql_bulk_insert/ который также на Nuget. Я написал небольшой преобразователь, который автоматически сопоставляет свойства способом 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;
        }

Я использую его следующим образом (у меня была сущность с именем Undertaking):

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

Я показал пример с транзакцией, но это также можно сделать с обычным соединением, полученным из контекста. takekingsToAdd является перечисляемым из нормальных записей сущностей, которые я хочу, чтобы оптом вставить в БД.

Это решение, к которому я пришел после нескольких часов исследований и попыток, - это то, что вы могли ожидать гораздо быстрее и, наконец, просты в использовании и бесплатны! Я действительно советую вам использовать это решение не только по причинам, упомянутым выше, но и потому, что оно единственное, с которым у меня не было проблем с самим Postgresql, многие другие решения работают безупречно, например с SqlServer.

1 голос
/ 17 апреля 2017

Вы можете использовать библиотеку Bulk .Версия Bulk Insert 1.0.0 используется в проектах, имеющих Entity Framework> = 6.0.0.

Более подробное описание можно найти здесь - Исходный код Bulkoperation

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...