Forma mais rápida de inserção na estrutura de entidades

Estou à procura da forma mais rápida de inserir no quadro de entidades.

estou a perguntar isto por causa do cenário em que se tem um TransactionScope activo e a inserção é enorme (4000+). Ele pode potencialmente durar mais de 10 minutos (Tempo Limite padrão das transações), e isso levará a uma transação incompleta.

Author: Jon Schneider, 2011-05-09

25 answers

À sua observação nos comentários à sua pergunta:

"...Mudanças de sabor ( para cada registo )..."

É a pior coisa que podes fazer! Chamando SaveChanges() para cada registo, o volume diminui imenso. Eu faria alguns testes simples que provavelmente melhorariam o desempenho:
    Liga para os registos de uma vez por todas.
  • Ligue SaveChanges() depois, por exemplo, de 100 registos.
  • ligar SaveChanges() depois de, por exemplo, 100 registos e elimine o contexto e crie um novo.
  • desactivar a detecção de alterações

Para inserções a granel estou a trabalhar e a experimentar um padrão como este:

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

Tenho um programa de testes que insere 560.000 entidades (9 Propriedades escalar, sem propriedades de navegação) no DB. Com este código, funciona em menos de 3 minutos.

Para o desempenho é importante chamar SaveChanges() depois de" muitos " registros ("muitos" em torno de 100 ou 1000). Também melhora o desempenho para eliminar o contexto após as SaveChanges e criar um novo. Isto limpa o contexto de todas as entidades, SaveChanges não faz isso, as entidades ainda estão ligadas ao contexto no estado Unchanged. É o tamanho crescente de entidades ligadas no contexto que atrasa a inserção passo a passo. Portanto, é útil esclarecê-lo depois de algum tempo.

Aqui estão algumas medidas para as minhas 560.000 entidades:
  • commitCount = 1, recreateContext = false: muitas horas (Esse é o seu procedimento atual)
  • commitCount = 100, recreateContext = false: mais de 20 minutos
  • commitCount = 1000, recreateContext = false: 242 sec
  • commitCount = 10000, recreateContext = false: 202 sec
  • commitCount = 100000, recreateContext = false: 199 sec
  • Número de commitCount = 1000000, recreateContext = false: fora da memória
  • Número de commitCount = 1, recreateContext = true: mais de 10 minutos
  • commitCount = 10, recreateContext = true: 241 sec
  • commitCount = 100, recreateContext = true: 164 sec
  • commitCount = 1000, recreateContext = true: 191 sec

O comportamento no primeiro ensaio acima é que o desempenho é muito não linear e diminui extremamente ao longo do tempo. ("Muitas horas" é uma estimativa, eu nunca terminei este teste, Parei em 50.000 entidades depois de 20 minutos.) Este comportamento não linear não é tão significativo em todos os outros ensaios.

 857
Author: Slauma, 2011-05-09 20:39:36
Esta combinação aumenta bastante a velocidade.
context.Configuration.AutoDetectChangesEnabled = false;
context.Configuration.ValidateOnSaveEnabled = false;
 164
Author: arkhivania, 2012-08-12 15:52:04

A forma mais rápida seria usando Extensão de inserção a granel, que eu desenvolvi.

Ele usa o SqlBulkCopy e o leitor de dados personalizado para obter o desempenho máximo. Como resultado, é mais de 20 vezes mais rápido do que o uso regular de inserir ou AddRange EntityFramework.BulkInsert vs EF AddRange

A utilização é extremamente simples

context.BulkInsert(hugeAmountOfEntities);
 97
Author: maxlego, 2015-11-09 07:17:54

Você deve olhar para usar o {[[0]} para isto. Aqui está a documentação , e é claro que existem muitos tutoriais online.

Desculpa, eu sei que estavas à procura de uma resposta simples para fazer o EF fazer o que queres, mas as operações a granel não são para o que os ORMs servem.
 68
Author: Adam Rackis, 2011-05-09 17:17:37
Concordo com o Adam Rackis. SqlBulkCopy é a forma mais rápida de transferir os registos de massa de uma fonte de dados para outra. Usei isto para copiar os registos de 20K e demorou menos de 3 segundos. Dê uma olhada no exemplo abaixo.
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();
        }

    }
}
 46
Author: Irfons, 2013-01-15 10:56:44
Investiguei a resposta de Slauma (o que é incrível, obrigado pelo homem das ideias), e reduzi o tamanho do lote até atingir a velocidade máxima. Olhando para os resultados do Slauma:
  • commitCount = 1, recreateContext = true: more than 10 minutes
  • commitCount = 10, recreateContext = true: 241 sec
  • commitCount = 100, recreateContext = true: 164 sec
  • commitCount = 1000, recreateContext = true: 191 sec
É visível que há Velocidade aumentar ao mover-se de 1 para 10, e de 10 para 100, mas de 100 para 1000 Velocidade de inserção está caindo novamente.

Então eu concentrei-me no que está a acontecer quando você reduz o tamanho do lote para um valor algures entre 10 e 100, e aqui estão os meus resultados (estou a usar diferentes conteúdos das linhas, por isso os meus tempos são de valor diferente):

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

Com base nos meus resultados, o óptimo real é cerca de 30 para o tamanho do lote. São menos de 10 e 100. O problema é que não faço ideia porque é que 30 é o ideal., nem poderia ter encontrado uma explicação lógica para isso.

 19
Author: Admir Tuzović, 2013-03-26 08:46:42

Eu recomendaria este artigo sobre como fazer inserções em massa usando EF.

Estrutura da entidade e inserções lentas a granel

Ele explora estas áreas e compara a perfeição:

  1. default EF (57 minutos para completar a adição de 30.000 registos)
  2. substituição por: ADO.NET Código (25 segundos para os mesmos 30.000)
  3. aumento do contexto-manter o gráfico de contexto activo pequeno usando um novo contexto para cada unidade de trabalho (os mesmos 30 000 inserções tomam 33 segundos)
  4. listas grandes-desligue o AutoDetectChangesEnabled (reduz o tempo para cerca de 20 segundos)
  5. Resistência (até 16 segundos)
  6. DbTable.AddRange () - (o desempenho está no intervalo de 12)
 16
Author: ShaTin, 2014-06-20 03:10:55

Como outras pessoas disseram SqlBulkCopy é a maneira de fazê-lo se você quer realmente bom inserir desempenho.

É um pouco complicado de implementar, mas há bibliotecas que podem ajudá-lo com isso. Há alguns por aí, mas desta vez vou mostrar a minha própria biblioteca. https://github.com/MikaelEliasson/EntityFramework.Utilities#batch-insert-entities

O único código que precisarias é:

 using (var db = new YourDbContext())
 {
     EFBatchOperation.For(db, db.BlogPosts).InsertAll(list);
 }
Então, é muito mais rápido? Muito difícil de dizer porque é depende de tantos fatores, desempenho do computador, rede, Tamanho do objeto etc. Os testes de desempenho que fiz sugerem que as Entidades de 25k podem ser inseridas em cerca de 10s da maneira padrão em localhost se você otimizar a sua configuração de EF como mencionado nas outras respostas. Com o EFUtilities que leva cerca de 300ms. ainda mais interessante é que eu salvei cerca de 3 milhões de entidades em menos de 15 segundos usando este método, com média de cerca de 200k entidades por segundo.

O tal o problema é claro se você precisar inserir dados relegados. Isto pode ser feito de forma eficaz no servidor sql usando o método acima, mas requer que você tenha uma estratégia de Geração de Id que lhe permita gerar id's no código app para o pai para que você possa definir as chaves estrangeiras. Isto pode ser feito usando GUIDs ou algo como hilo id geração.

 14
Author: Mikael Eliasson, 2014-12-19 10:44:11

Dispose() contexto criar problemas se as entidades que você Add() conta com outras entidades pré-carregadas (por exemplo, propriedades de navegação) no contexto

Uso um conceito semelhante para manter o meu contexto pequeno para conseguir o mesmo desempenho

Mas em vez de Dispose() o contexto e recriar, eu simplesmente destaco as entidades que já 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;
} }

Enrola - o com tenta apanhar e TrasactionScope() Se precisares, não mostrá-los aqui para manter o código limpo

 14
Author: Stephen Ho, 2018-07-24 13:06:42

Tente usar um procedimento armazenado que irá obter um XML dos dados que deseja inserir.

 7
Author: Maxim, 2011-05-09 17:18:23

Fiz uma extensão genérica do exemplo de @Slauma s acima;

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

Utilização:

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
Author: Sgedda, 2015-11-25 13:35:24

Eu sei que esta é uma pergunta muito antiga, mas um cara aqui disse que desenvolveu um método de extensão para usar insert em massa com EF, e quando eu verifiquei, eu descobri que a biblioteca custa US $599 hoje (para um desenvolvedor). Talvez faça sentido para toda a biblioteca, no entanto, para apenas a inserção em massa isso é demais.

Aqui está um método de extensão muito simples que eu fiz. Eu uso isso em par com o banco de dados primeiro (não testar com o código primeiro, mas eu acho que funciona o mesmo). Alteração YourEntities com o nome do seu contexto:
public partial class YourEntities : DbContext
{
    public async Task BulkInsertAllAsync<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;
            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;
    }
}

Podes usá-lo contra qualquer colecção que herde de IEnumerable, assim:

await context.BulkInsertAllAsync(items);
 4
Author: Guilherme, 2018-01-22 04:48:05
Estou à procura da forma mais rápida de inserir no quadro da entidade.

Existem algumas bibliotecas de terceiros que suportam a introdução A Granel disponíveis:

  • Z. EntityFramework.Extensões (Recomendadas)
  • EFUtilities
  • Quadro de direitos.BulkInsert

Ver: Entity Framework Bulk Insert library

Tenha cuidado, ao escolher uma biblioteca de insert bulk. Apenas as extensões da estrutura da entidade suportam todos os tipos de associações e heranças e é a única ainda suportada.


Declaração de exoneração de Responsabilidade : sou o proprietário da Entity Framework Extensions

Esta biblioteca permite-lhe efectuar todas as operações em massa de que necessita para os seus cenários:

  • Grandes Savecanges
  • Inserir A Granel
  • Eliminação A Granel
  • Actualização A Granel
  • Junção A Granel

Exemplo

// 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;
});
 3
Author: Jonathan Magnan, 2018-08-18 16:25:46

De acordo com o meu conhecimento há {[[0]} em EntityFramework para aumentar o desempenho das enormes inserções.

Neste cenário, você pode ir com SqlBulkCopy em ADO.net para resolver o seu problema

 2
Author: anishMarokey, 2011-05-09 17:20:03

Aqui está uma comparação de desempenho entre usar o Entity Framework e usar a classe SqlBulkCopy num exemplo realista: Como inserir objectos complexos na Base de dados do servidor SQL

Como outros já sublinharam, os ORMs não se destinam a ser utilizados em operações a granel. Oferecem flexibilidade, separação de preocupações e outros benefícios, mas as operações a granel (excepto a leitura a granel) não são um deles.

 2
Author: Zoran Horvat, 2015-07-14 14:13:50

Outra opção é usar o SqlBulkTools disponível no Nuget. É muito fácil de usar e tem algumas características poderosas.

Exemplo:

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

Veja a documentação para mais exemplos e utilização avançada. Disclaimer: eu sou o autor desta biblioteca e quaisquer opiniões são da minha própria opinião.

 2
Author: Greg R Taylor, 2016-10-29 23:16:30

Uso 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
Author: Amir Saniyan, 2017-06-04 22:06:11
Uma das formas mais rápidas de salvar uma lista deve aplicar o seguinte código
context.Configuration.AutoDetectChangesEnabled = false;
context.Configuration.ValidateOnSaveEnabled = false;

AutoDetectChangesEnabled = false

Add, AddRange & SaveChanges: não detecta alterações.

ValidateOnSaveEnabled = false;

Não detecta o localizador de alterações

Deve adicionar nuget

Install-Package Z.EntityFramework.Extensions

Agora pode usar o seguinte código

var context = new MyContext();

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

context.BulkInsert(list);
context.BulkSaveChanges();
 2
Author: Reza Jenabi, 2018-10-02 10:05:11

O segredo é inserir numa tabela idêntica em branco. As inserções estão a iluminar-se rapidamente. Em seguida, execute um Único inserir a partir daí na sua tabela principal grande. Em seguida, truncar a mesa de preparação pronto para o próximo lote.

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
 1
Author: Simon Hughes, 2012-07-18 10:43:30
Já tentou inserir através de um assistente ou tarefa? ([3]) no meu caso, inserindo 7760 registos, distribuídos em 182 quadros diferentes com relações de chave estrangeiras (por propostas de navegação). Sem a tarefa, demorou 2 minutos e meio. Dentro de uma tarefa ([[[0]} ), levou 15 segundos.

Só estou a fazer o SaveChanges() depois de adicionar todas as Entidades ao contexto. (para garantir a integridade dos dados)

 1
Author: Rafael A. M. S., 2013-05-27 20:19:58

Todas as soluções aqui escritas não ajudam porque quando você faz SaveChanges (), inserir declarações são enviadas para o banco de dados um a um, é assim que a entidade funciona.

E se a sua viagem de ida e volta à base de dados for de 50 ms, por exemplo, o tempo necessário para inserir é o número de registos x 50 ms.

Você tem que usar BulkInsert, aqui está a ligação: https://efbulkinsert.codeplex.com/

O tempo de inserção foi reduzido de 5-6 minutos para 10-12 segundos usando-o.
 1
Author: Aleksa, 2015-10-01 13:05:03

Pode utilizar pacote a granel biblioteca. Bulk Insert 1.0.0 version is used in projects having Entity framework > = 6.0.0 .

Mais Descrição Pode ser encontrada aqui- Código-Fonte da Bulkoperation

 1
Author: Mohd Nadeem, 2017-04-17 13:54:31

[NOVA SOLUÇÃO PARA POSTGRESQL] Ei, eu sei que é um post bastante antigo, mas eu encontrei recentemente um problema semelhante, mas nós estávamos usando Postgresql. Eu queria usar bulkinsert eficaz, o que acabou por ser muito difícil. Não encontrei nenhuma biblioteca livre para o fazer neste cadáver. Só encontrei este ajudante.: https://bytefish.de/blog/postgresql_bulk_insert/ que também está em Nuget. Eu escrevi um pequeno mapeador, que auto mapeou propriedades da forma como entidade Enquadramento:

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

USO - o da seguinte forma (tinha uma entidade com nome de Empresa):

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

Mostrei um exemplo com a transacção, mas também pode ser feito com a ligação normal recuperada do contexto. undertakingsToAdd é enumerável de registos de entidades normais, que eu quero transformar em DB.

Esta solução, para a qual eu tenho após algumas horas de pesquisa e tentativa, é como você poderia esperar muito mais rápido e finalmente fácil de usar e livre! Aconselho-te a usar isto. solução, não só pelas razões mencionadas acima, mas também porque é o único com o qual eu não tinha problemas com o Postgresql em si, muitas outras soluções funcionam perfeitamente, por exemplo, com SqlServer.

 1
Author: Michał Pilarek, 2017-09-05 21:03:27

Mas, para mais de (+4000) inserções eu recomendo usar o procedimento armazenado. anexou o tempo decorrido. Eu inseri 11.788 linhas em 20"enter image description here

É o código.
 public void InsertDataBase(MyEntity entity)
    {
        repository.Database.ExecuteSqlCommand("sp_mystored " +
                "@param1, @param2"
                 new SqlParameter("@param1", entity.property1),
                 new SqlParameter("@param2", entity.property2));
    }
 0
Author: Marinpietri, 2017-11-20 00:54:00

Utilizar o procedimento armazenado que recebe dados de entrada sob a forma de xml para inserir dados.

A partir do seu código C# Passe inserir os dados como xml.

E. G Em C#, a sintaxe seria assim:

object id_application = db.ExecuteScalar("procSaveApplication", xml)
 0
Author: arun tiwari, 2018-01-07 03:33:19