La manière la plus rapide d'insérer dans le cadre de L'entité

je suis à la recherche du moyen le plus rapide d'insérer dans le cadre de L'entité.

je pose cette question à cause du scénario où vous avez un Scope de transaction active et l'insertion est énorme (4000+). Il peut durer plus de 10 minutes (délai d'attente par défaut des transactions), ce qui entraînera une transaction incomplète.

561
demandé sur Jon Schneider 2011-05-09 21:14:00

25 réponses

À votre remarque dans les commentaires à votre question:

"...SavingChanges ( pour chaque dossier )..."

C'est la pire chose à faire! Appeler SaveChanges() pour chaque record ralentit considérablement les inserts en vrac. Je voudrais faire quelques tests simples qui amélioreront très probablement la performance:

  • Appel SaveChanges() une fois, après TOUS les records.
  • Appel SaveChanges() après par exemple 100 enregistrements.
  • appeler SaveChanges() après par exemple 100 enregistrements et disposer le contexte et en créer un nouveau.
  • Désactiver la détection de changement

pour les inserts en vrac je travaille et expérimente avec un modèle comme celui-ci:

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

j'ai un programme de test qui insère 560.000 entités (9 propriétés scalaires, no propriétés de navigation) dans le DB. Avec ce code, il fonctionne en moins de 3 minutes.

Pour la performance, il est important d'appeler SaveChanges() après "plusieurs" dossiers ("beaucoup" autour de 100 ou 1000). Il améliore également les performances de disposer du contexte après SaveChanges et en créer un nouveau. Cela efface le contexte de toutes les entités, SaveChanges ne fait pas cela, les entités sont toujours attachées au contexte dans l'état Unchanged . C'est la taille croissante de l'attaché entités dans le contexte ce qui ralentit l'insertion pas à pas. Donc, il est utile de le vider après un certain temps.

voici quelques mesures pour mes 560.000 entités:

  • commitCount = 1, recreateContext = false: nombre d'heures (C'est votre procédure actuelle)
  • commitCount = 100, recreateContext = false: plus de 20 minutes 1519200920"
  • commitCount = 1000, recreateContext = false: 242 sec
  • commitCount = 10000, recreateContext= false: 202 sec
  • commitCount = 100000, recreateContext= false: 199 sec
  • commitCount = 1000000, recreateContext= false: out of memory exception
  • commitCount = 1, recreateContext = true: plus de 10 minutes
  • commitCount = 10, recreateContext = true: 241 sec
  • commitCount = 100, recreateContext= true: 164 sec
  • commitCount = 1000, recreateContext = true: 191 sec

Le comportement dans le premier test ci-dessus est que la performance est très non linéaire et diminue considérablement avec le temps. ("Plusieurs heures" est une estimation, Je n'ai jamais terminé ce test, je me suis arrêté à 50.000 entités après 20 minutes.) Ce comportement non linéaire n'est pas aussi significatif dans tous les autres essais.

857
répondu Slauma 2011-05-09 20:39:36

cette combinaison augmente suffisamment la vitesse.

context.Configuration.AutoDetectChangesEnabled = false;
context.Configuration.ValidateOnSaveEnabled = false;
164
répondu arkhivania 2012-08-12 15:52:04

le moyen le plus rapide serait d'utiliser bulk insert extension , que j'ai développé.

il utilise SqlBulkCopy et custom datareader pour obtenir des performances max. En conséquence, il est plus de 20 fois plus rapide que l'utilisation régulière insert ou AddRange EntityFramework.BulkInsert vs EF AddRange

l'usage est extrêmement simple

context.BulkInsert(hugeAmountOfEntities);
97
répondu maxlego 2015-11-09 07:17:54

vous devriez regarder en utilisant le System.Data.SqlClient.SqlBulkCopy pour cela. Voici la documentation , et bien sûr il ya beaucoup de tutoriels en ligne.

Désolé, je sais que vous cherchiez une réponse simple pour que EF fasse ce que vous voulez, mais les opérations en vrac ne sont pas vraiment ce que les ORMs sont censés faire.

68
répondu Adam Rackis 2011-05-09 17:17:37

Je suis D'accord avec Adam Rackis. SqlBulkCopy est le moyen le plus rapide de transférer des enregistrements en vrac d'une source de données à une autre. J'ai utilisé ça pour copier des disques de 20K et ça a pris moins de 3 secondes. Regardez l'exemple ci-dessous.

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
répondu Irfons 2013-01-15 10:56:44

J'ai étudié la réponse de Slauma (ce qui est génial, merci pour l'idea man), et j'ai réduit la taille du lot jusqu'à ce que j'ai atteint la vitesse optimale. En regardant les résultats de la 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

il est visible qu'il y a une augmentation de vitesse quand on passe de 1 à 10, et de 10 à 100, mais de 100 à 1000 en insérant la vitesse est de nouveau en baisse.

donc j'ai mis l'accent sur ce qui se passe quand vous réduisez la taille du lot de valeur quelque part entre 10 et 100, et voici mes résultats (j'utilise des contenus de ligne différents, donc mes temps sont de valeur différente):

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

basé sur mes résultats, optimum réel est d'environ la valeur de 30 pour la taille du lot. C'est moins de 10 et 100. Le problème, c'est que je ne sais pas pourquoi 30 est optimal, et je n'aurais pas pu trouver d'explication logique.

19
répondu Admir Tuzović 2013-03-26 08:46:42

je recommande cet article sur la façon de faire des inserts en vrac en utilisant EF.

Entity Framework et lente des Insertions

il explore ces domaines et compare la performance:

  1. EF par défaut (57 minutes pour compléter l'ajout de 30 000 enregistrements)
  2. remplacé par ADO.NET Code (25 seconds for those same 30,000)
  3. Contexte Bloat-garder le graphe de contexte actif petit en utilisant un nouveau contexte pour chaque unité de travail (même 30,000 inserts prennent 33 secondes)
  4. Large Lists-Turn off AutoDetectChangesEnabled (ramène le temps à environ 20 secondes)
  5. par lot (jusqu'à 16 secondes)
  6. DbTable.AddRange() - (performance est dans la 12 de gamme)
16
répondu ShaTin 2014-06-20 03:10:55

comme d'autres personnes ont dit SqlBulkCopy est la façon de le faire si vous voulez vraiment bonne performance d'insertion.

c'est un peu encombrant à mettre en œuvre, mais il y a des bibliothèques qui peuvent vous aider avec cela. Il ya quelques-uns là-bas, mais je vais shamelesslyplug ma propre bibliothèque cette fois: https://github.com/MikaelEliasson/EntityFramework.Utilities#batch-insert-entities

le seul code dont vous avez besoin est:

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

c'est beaucoup plus rapide? Très difficile à dire car il dépend de tant de facteurs, la performance de l'ordinateur, le réseau, la taille de l'objet, etc. Les tests de performance que j'ai faits suggèrent que les entités 25k peuvent être insérées à environ 10s le standard way sur localhost si vous optimisez votre configuration EF comme mentionné dans les autres réponses. Avec EFUtilities qui prend environ 300ms. Encore plus intéressant est que j'ai sauvé environ 3 millions d'entités dans moins de 15 secondes en utilisant cette méthode, avec une moyenne d'environ 200K entités par seconde.

le seul problème est bien sûr si vous avez besoin d'insérer des données releated. Cela peut être fait efficacement dans sql server en utilisant la méthode ci-dessus, mais cela exige que vous ayez une stratégie de génération D'Id qui vous permette de générer des id dans l'app-code pour le parent afin que vous puissiez définir les clés étrangères. Cela peut être fait en utilisant des GUIDs ou quelque chose comme génération HiLo id.

14
répondu Mikael Eliasson 2014-12-19 10:44:11

"151910920 contextuel" créer des problèmes si les entités Add() s'appuyer sur d'autres préchargé entités (par exemple, les propriétés de navigation) dans le contexte

j'utilise un concept similaire pour garder mon contexte petit pour atteindre la même performance

mais au lieu de Dispose() le contexte et recréer, je détache simplement les entités qui dé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;
} }

enveloppez-le avec try catch et TrasactionScope() si vous besoin, ne pas les montrer ici pour garder le code propre

14
répondu Stephen Ho 2018-07-24 13:06:42

essayez d'utiliser un procédure stockée qui obtiendra un XML des données que vous voulez insérer.

7
répondu Maxim 2011-05-09 17:18:23

j'ai fait une extension générique de l'exemple de @Slauma ci-dessus;

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

Utilisation:

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
répondu Sgedda 2015-11-25 13:35:24

je sais que c'est une très vieille question, mais un gars ici a dit que développé une méthode d'extension pour utiliser insert en vrac avec EF, et quand j'ai vérifié, j'ai découvert que la bibliothèque coûte 599 $aujourd'hui (pour un développeur). Peut-être que cela a du sens pour l'ensemble de la bibliothèque, mais pour le seul encart en vrac c'est trop.

Voici une méthode d'extension très simple que j'ai faite. Je l'utilise sur la paire avec la base de données d'abord (ne pas testé avec le code d'abord, mais je pense que cela fonctionne la même chose). Changer YourEntities avec le nom de votre contexte:

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

vous pouvez l'utiliser contre n'importe quelle collection qui hérite de IEnumerable , comme cela:

await context.BulkInsertAllAsync(items);
4
répondu Guilherme 2018-01-22 04:48:05

je suis à la recherche du moyen le plus rapide d'insérer dans le cadre de L'entité

il y a des bibliothèques tierces supportant des encarts en vrac disponibles:

  • Z. Entityfram Framework.Extensions ( Recommandé )
  • EFUtilities
  • Entityfram Framework.BulkInsert

Voir: Entity Framework Bulk Insérer bibliothèque

soyez prudent lorsque vous choisissez une bibliothèque d'inserts en vrac. Seule Entity Framework Extensions supporte toutes sortes d'associations et d'héritages et c'est la seule qui est encore supportée.


Disclaimer :je suis le propriétaire de Entity Framework Extensions

cette bibliothèque vous permet d'effectuer tous les opérations dont vous avez besoin pour vos scénarios:

  • SaveChanges En Vrac
  • Vrac Insérer
  • En Vrac Supprimer
  • Mise À Jour En Bloc
  • Fusion En Vrac

exemple

// 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
répondu Jonathan Magnan 2018-08-18 16:25:46

à ma connaissance il y a no BulkInsert dans EntityFramework pour augmenter la performance des énormes inserts.

dans ce scénario, vous pouvez aller avec SqlBulkCopy dans ADO.net pour résoudre votre problème

2
répondu anishMarokey 2011-05-09 17:20:03

Voici une comparaison de performance entre l'utilisation D'Entity Framework et L'utilisation de SqlBulkCopy class sur un exemple réaliste: Comment insérer des objets complexes dans la base de données SQL Server

comme d'autres l'ont déjà souligné, les ORM ne sont pas destinés à être utilisés dans les opérations en vrac. Ils offrent de la flexibilité, la séparation des préoccupations et d'autres avantages, mais les opérations en vrac (sauf la lecture en vrac) ne sont pas l'un d'entre eux.

2
répondu Zoran Horvat 2015-07-14 14:13:50

une autre option est D'utiliser des SqlBulkTools disponibles chez Nuget. Il est très facile à utiliser et a quelques traits puissants.

exemple:

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

Voir la documentation pour plus d'exemples et d'utilisation avancée. Avertissement: je suis l'auteur de cette bibliothèque et de toutes les vues sont de ma propre opinion.

2
répondu Greg R Taylor 2016-10-29 23:16:30

utiliser 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
répondu Amir Saniyan 2017-06-04 22:06:11

l'Un des moyens les plus rapides pour enregistrer une liste vous devez appliquer le code suivant

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

AutoDetectChangesEnabled = false

Add, AddRange & SaveChanges: ne détecte pas les changements.

ValidateOnSaveEnabled = false;

Ne détecte pas de changement de tracker

vous devez ajouter nuget

Install-Package Z.EntityFramework.Extensions

Vous pouvez maintenant utiliser le code suivant

var context = new MyContext();

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

context.BulkInsert(list);
context.BulkSaveChanges();
2
répondu Reza Jenabi 2018-10-02 10:05:11

le secret est d'insérer dans une table d'étape vierge identique. Les Inserts éclairent vite. Ensuite, lancez un simple insérer à partir de cela dans votre grande table principale. Puis tronquez la table de staging prête pour la prochaine fournée.

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
répondu Simon Hughes 2012-07-18 10:43:30

avez-vous déjà essayé d'insérer par l'intermédiaire d'un travailleur d'arrière-plan ou d'une tâche?

dans mon cas, im insérant 7760 registres, distribués dans 182 tableaux différents avec des relations clés étrangères ( par NavigationProperties).

sans la tâche, il a fallu 2 minutes et demie. Dans le cadre d'une tâche ( Task.Factory.StartNew(...) ), il a fallu 15 secondes.

Im seulement à faire le SaveChanges() après l'ajout de toutes les entités du contexte. (pour s'assurer que les données l'intégrité)

1
répondu Rafael A. M. S. 2013-05-27 20:19:58

toutes les solutions écrites ici n'aident pas parce que quand vous faites SaveChanges(), insert statements sont envoyés à la base de données un par un, c'est comme ça que fonctionne Entity.

et si votre voyage à la base de données et retour est de 50 ms par exemple, alors le temps nécessaire pour insérer est le nombre d'enregistrements x 50 ms.

vous devez utiliser BulkInsert, voici le lien: https://efbulkinsert.codeplex.com /

j'ai eu l'heure d'insertion réduit de 5-6 minutes à 10-12 secondes en l'utilisant.

1
répondu Aleksa 2015-10-01 13:05:03

[NOUVELLE SOLUTION POUR POSTGRESQL] Hey, je sais que c'est un vieux post, mais j'ai récemment rencontré un problème similaire, mais nous utilisions Postgresql. Je voulais utilisation efficace bulkinsert, ce qui s'est avéré être assez difficile. Je n'ai pas trouvé de bibliothèque libre pour le faire sur ce DB. Je n'ai trouvé que cet assistant: https://bytefish.de/blog/postgresql_bulk_insert / qui est aussi sur Nuget. J'ai écrit un petit mapper, qui AUTO mapped propriétés de la façon 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;
        }

Je l'utilise de la manière suivante (j'avais entité nommée entreprise):

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

j'ai montré un exemple avec transaction, mais il peut aussi être fait avec connexion normale récupérée à partir du contexte. undertakingsToAdd est une liste de dossiers d'entités normales, que je veux grouper en DB.

Cette solution, à laquelle j'ai eu après quelques heures de recherche et d'essai, est comme vous pouvez vous attendre beaucoup plus rapide et enfin facile à utiliser et gratuit! Je vous conseille vraiment d'utiliser cette solution, non seulement pour les raisons mentionnées ci-dessus, mais aussi parce que c'est la seule avec laquelle je n'ai eu aucun problème avec Postgresql lui-même, beaucoup d'autres solutions fonctionnent parfaitement par exemple avec SqlServer.

1
répondu Michał Pilarek 2017-09-05 21:03:27

Mais, pour plus de (+4000) insère je recommande d'utiliser une procédure stockée. joint de temps s'est écoulé. Je l'ai inséré 11.788 lignes dans 20" enter image description here

c'est le code

 public void InsertDataBase(MyEntity entity)
    {
        repository.Database.ExecuteSqlCommand("sp_mystored " +
                "@param1, @param2"
                 new SqlParameter("@param1", entity.property1),
                 new SqlParameter("@param2", entity.property2));
    }
0
répondu Marinpietri 2017-11-20 00:54:00

utiliser la procédure stockée qui prend des données d'entrée sous forme de xml pour insérer des données.

de votre code C# insérez des données en xml.

E. g dans c#, la syntaxe serait comme ceci:

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