Je recherche le moyen le plus rapide de s’insérer dans Entity Framework.
Je pose la question à cause du scénario dans lequel vous avez un TransactionScope actif et où l’insertion est énorme (plus de 4000). Elle peut potentiellement durer plus de 10 minutes (délai d'attente par défaut des transactions), ce qui entraînera une transaction incomplète.
À votre remarque dans les commentaires à votre question:
"... SavingChanges ( pour chaque Enregistrement ) ..."
C'est la pire chose que vous puissiez faire! L'appel de SaveChanges()
pour chaque enregistrement ralentit considérablement les insertions en bloc. Je voudrais faire quelques tests simples qui amélioreront très probablement les performances:
SaveChanges()
une fois après TOUS les enregistrements.SaveChanges()
après par exemple 100 enregistrements.SaveChanges()
après par exemple 100 enregistrements, supprimez le contexte et créez-en un.Pour les inserts en vrac, je travaille et expérimente un motif 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, pas de propriétés de navigation) dans la base de données. Avec ce code, cela fonctionne en moins de 3 minutes.
Pour l'exécution, il est important d'appeler SaveChanges()
après "plusieurs" enregistrements ("plusieurs" autour de 100 ou 1000). Cela améliore également les performances de disposer du contexte après SaveChanges et d'en créer un nouveau. Cela efface le contexte de toutes les entités, SaveChanges
ne le fait pas, les entités sont toujours attachées au contexte dans l'état Unchanged
. C'est la taille croissante des entités attachées dans le contexte qui ralentit l'insertion étape par étape. Donc, il est utile de le nettoyer après un certain temps.
Voici quelques mesures pour mes 560 000 entités:
Le comportement dans le premier test ci-dessus est que la performance est très non linéaire et diminue extrêmement avec le temps. ("Many hours" est une estimation, je n'ai jamais terminé ce test, je me suis arrêté après 50 minutes à 50 000 entités.) Ce comportement non linéaire n'est pas aussi significatif dans tous les autres tests.
Cette combinaison augmente assez bien la vitesse.
context.Configuration.AutoDetectChangesEnabled = false;
context.Configuration.ValidateOnSaveEnabled = false;
Le moyen le plus rapide serait d’utiliser bulk insert extension , que j’ai développé.
Il utilise SqlBulkCopy et un datareader personnalisé pour obtenir des performances maximales. En conséquence, il est 20 fois plus rapide que l’utilisation d’insert standard ou AddRange
l'utilisation est extrêmement simple
context.BulkInsert(hugeAmountOfEntities);
Vous devriez envisager d'utiliser le System.Data.SqlClient.SqlBulkCopy
pour cela. Voici la documentation , et bien sûr, il existe de nombreux tutoriels en ligne.
Désolé, je sais que vous cherchiez une solution simple pour que EF fasse ce que vous voulez, mais les opérations en bloc ne sont pas vraiment ce à quoi les ORM sont destinés.
Je suis d'accord avec Adam Rackis. SqlBulkCopy
est le moyen le plus rapide de transférer des enregistrements en masse d'une source de données à une autre. J'ai utilisé cela pour copier des disques de 20K et cela 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();
}
}
}
J'ai étudié la réponse de Slauma (ce qui est génial, merci pour son idée homme), et j'ai réduit la taille du lot jusqu'à atteindre la vitesse optimale. En regardant les résultats de Slauma:
Il est visible qu'il y a une augmentation de la vitesse lorsque vous vous déplacez de 1 à 10 et de 10 à 100, mais de 100 à 1 000, la vitesse d'insertion redescend.
Je me suis donc concentré sur ce qui se passe lorsque vous réduisez la taille d'un lot à une valeur comprise entre 10 et 100, et voici mes résultats (j'utilise un contenu de ligne différent, mes temps sont donc d'une 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
Sur la base de mes résultats, l’optimum réel est proche de 30 pour la taille du lot. C'est moins que 10 et 100. Le problème, c'est que je ne sais pas pourquoi 30 est optimal, je n'aurais pas pu trouver d'explication logique à cela.
Je recommanderais cet article sur la façon de réaliser des insertions en bloc en utilisant EF.
Entity Framework et INSERTs à encombrement réduit
Il explore ces domaines et compare les performances:
Comme d'autres personnes l'ont dit, SqlBulkCopy est le moyen de le faire si vous voulez de très bonnes performances d'insertion.
C'est un peu fastidieux à mettre en place, mais il y a des bibliothèques qui peuvent vous aider. Il y en a quelques-uns mais je vais débrancher sans vergogne ma propre bibliothèque cette fois-ci: https://github.com/MikaelEliasson/EntityFramework.Utilities#batch-insert-entities
Le seul code dont vous auriez besoin est:
using (var db = new YourDbContext())
{
EFBatchOperation.For(db, db.BlogPosts).InsertAll(list);
}
Alors, combien est-ce plus rapide? Très difficile à dire car cela dépend de nombreux facteurs, performances de l'ordinateur, réseau, taille de l'objet, etc. Les tests de performance que j'ai effectués suggèrent que 25 000 entités peuvent être insérées à environ 10 secondes de la manière standard sur localhost SI vous optimisez votre configuration EF comme mentionné dans les autres réponses. Avec EFUtilities, cela prend environ 300 ms. Encore plus intéressant, j’ai sauvé environ 3 millions d’entités en moins de 15 secondes avec cette méthode, avec une moyenne d’environ 200 000 entités par seconde.
Le seul problème est bien sûr si vous devez insérer des données répétées. Cela peut être fait efficacement dans SQL Server à l'aide de la méthode ci-dessus, mais vous devez disposer d'une stratégie de génération d'identifiant vous permettant de générer des identifiants dans le code d'application du parent afin de pouvoir définir les clés étrangères. Cela peut être fait en utilisant des GUID ou quelque chose comme la génération d'identifiant HiLo.
Dispose()
context crée des problèmes si les entités que vous Add()
s'appuient sur d'autres entités préchargées (propriétés de navigation, par exemple) dans le contexte
J'utilise un concept similaire pour garder mon contexte petit et atteindre les mêmes performances
Mais au lieu de Dispose()
le contexte et de 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 avez besoin, ne les affiche pas ici pour garder le code propre
Je sais que la question est très ancienne, mais un type ici a mis au point une méthode d’extension permettant d’utiliser des insertions en bloc avec EF. Lors de ma vérification, j’ai découvert que la bibliothèque coûtait 599 $ aujourd’hui (pour un développeur). Peut-être que cela a du sens pour toute la bibliothèque, mais c'est trop pour une simple insertion en bloc.
Voici une méthode d'extension très simple que j'ai faite. J'utilise cela d'abord sur paire avec base de données (ne pas tester avec le code d'abord, mais je pense que cela fonctionne de la même manière). Changez 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))
{
await conn.OpenAsync();
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 héritée de IEnumerable
, comme ceci:
await context.BulkInsertAllAsync(items);
Essayez d'utiliser une Procédure stockée qui obtiendra un XML des données que vous souhaitez insérer.
J'ai créé 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;
}
}
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();
}
}
Je cherche le moyen le plus rapide de s'inscrire dans Entity Framework
Certaines bibliothèques tierces prenant en charge l'insertion en bloc sont disponibles:
Voir: Bibliothèque d'insertion en bloc d'Entity Framework
Faites attention lorsque vous choisissez une bibliothèque d'insertion en bloc. Seules Entity Framework Extensions prennent en charge tous les types d'associations et d'héritages. C'est le seul qui soit toujours pris en charge.
Disclaimer: Je suis le propriétaire de Entity Framework Extensions
Cette bibliothèque vous permet d'effectuer toutes les opérations en bloc dont vous avez besoin pour vos scénarios:
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;
});
Utilisez 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;
}
L’un des moyens les plus rapides de sauvegarder une listevous devez appliquer le code suivant
context.Configuration.AutoDetectChangesEnabled = false;
context.Configuration.ValidateOnSaveEnabled = false;
AutoDetectChangesEnabled = false
Add, AddRange & SaveChanges: Ne détecte pas les modifications.
ValidateOnSaveEnabled = false;
Ne détecte pas le suivi des modifications
Vous devez ajouter une pépite
Install-Package Z.EntityFramework.Extensions
Maintenant, vous pouvez utiliser le code suivant
var context = new MyContext();
context.Configuration.AutoDetectChangesEnabled = false;
context.Configuration.ValidateOnSaveEnabled = false;
context.BulkInsert(list);
context.BulkSaveChanges();
Une autre option consiste à utiliser SqlBulkTools disponible auprès de Nuget. Il est très facile à utiliser et dispose de puissantes fonctionnalités.
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 une utilisation avancée. Disclaimer: Je suis l'auteur de cette bibliothèque et toutes les opinions sont de mon opinion.
Voici une comparaison de performances entre l'utilisation d'Entity Framework et celle de la classe SqlBulkCopy sur un exemple réaliste: Procédure d'insertion en masse d'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 en vrac. Ils offrent souplesse, séparation des préoccupations et autres avantages, mais les opérations en bloc (à l'exception de la lecture en bloc) n'en font pas partie.
Avez-vous déjà essayé d’insérer via un travailleur ou une tâche en arrière-plan?
Dans mon cas, insérant 7760 registres, répartis dans 182 tables différentes avec des relations de clé étrangère (par NavigationProperties).
Sans la tâche, cela prenait 2 minutes et demie . Dans une tâche (Task.Factory.StartNew(...)
), cela prenait 15 secondes.
Je ne fais que le SaveChanges()
après avoir ajouté toutes les entités au contexte. (pour assurer l'intégrité des données)
Toutes les solutions écrites ici ne sont d'aucune aide, car lorsque vous effectuez SaveChanges (), les instructions d'insertion sont envoyées une à une à la base de données, c'est ainsi que fonctionne Entity.
Et si votre voyage en base de données et retour est de 50 ms par exemple, le temps nécessaire à l’insertion est le nombre d’enregistrements x 50 ms.
Vous devez utiliser BulkInsert, voici le lien: https://efbulkinsert.codeplex.com/
J'ai utilisé le temps d'insertion réduit de 5-6 minutes à 10-12 secondes.
À ma connaissance, il existe no BulkInsert
dans EntityFramework
pour augmenter les performances des énormes plaquettes.
Dans ce scénario, vous pouvez utiliser SqlBulkCopy in ADO.net
pour résoudre votre problème.
[NOUVELLE SOLUTION POUR POSTGRESQL] .__ Hé, je sais que c’est un article assez ancien, mais j’ai récemment rencontré un problème similaire, mais nous utilisions Postgresql. Je voulais utiliser bulkinsert efficace, ce qui s’est avéré assez difficile. Je n'ai pas trouvé de bibliothèque libre appropriée pour le faire sur cette base de données. J'ai uniquement trouvé cette aide: https://bytefish.de/blog/postgresql_bulk_insert/ , Qui est également disponible sur Nuget. J'ai écrit un petit mappeur, qui mappe automatiquement les propriétés de la manière 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 l'entité nommée Engagement):
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 cela peut également être fait avec une connexion normale extraite du contexte. engagementsToAdd est énumérable des enregistrements d'entité normaux, que je veux regrouper dans DB.
Cette solution, à laquelle j’ai eu recours après quelques heures de recherches et d’essais, est comme on pouvait s’y attendre beaucoup plus rapidement et enfin, facile à utiliser et gratuite! Je vous conseille vivement d'utiliser cette solution, non seulement pour les raisons mentionnées ci-dessus, mais également parce que c'est la seule solution pour laquelle je n'ai eu aucun problème avec Postgresql, de nombreuses autres solutions fonctionnent parfaitement, par exemple avec SqlServer.
Vous pouvez utiliser Bulk package library. La version Bulk Insert 1.0.0 est utilisée dans les projets ayant Entity Framework> = 6.0.0.
Plus de description peut être trouvée ici- Code source Bulkoperation
SqlBulkCopy est super rapide
Ceci est ma mise en œuvre:
// at some point in my calling code, I will call:
var myDataTable = CreateMyDataTable();
myDataTable.Rows.Add(Guid.NewGuid,tableHeaderId,theName,theValue); // e.g. - need this call for each row to insert
var efConnectionString = ConfigurationManager.ConnectionStrings["MyWebConfigEfConnection"].ConnectionString;
var efConnectionStringBuilder = new EntityConnectionStringBuilder(efConnectionString);
var connectionString = efConnectionStringBuilder.ProviderConnectionString;
BulkInsert(connectionString, myDataTable);
private DataTable CreateMyDataTable()
{
var myDataTable = new DataTable { TableName = "MyTable"};
// this table has an identity column - don't need to specify that
myDataTable.Columns.Add("MyTableRecordGuid", typeof(Guid));
myDataTable.Columns.Add("MyTableHeaderId", typeof(int));
myDataTable.Columns.Add("ColumnName", typeof(string));
myDataTable.Columns.Add("ColumnValue", typeof(string));
return myDataTable;
}
private void BulkInsert(string connectionString, DataTable dataTable)
{
using (var connection = new SqlConnection(connectionString))
{
connection.Open();
SqlTransaction transaction = null;
try
{
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();
}
catch (Exception)
{
transaction?.Rollback();
throw;
}
}
}
comme il n’a jamais été mentionné ici, je souhaite recommander EFCore.BulkExtensions ici
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);
Mais, pour plus de (+4 000) insertions, il est recommandé d'utiliser une procédure stockée. attaché le temps écoulé . Je l'ai inséré 11,788 lignes dans 20 "
thats it code
public void InsertDataBase(MyEntity entity)
{
repository.Database.ExecuteSqlCommand("sp_mystored " +
"@param1, @param2"
new SqlParameter("@param1", entity.property1),
new SqlParameter("@param2", entity.property2));
}
Le secret consiste à insérer dans une table de transfert vierge identique. Les inserts s'éclaircissent rapidement. Ensuite, exécutez un single insert à partir de cela dans votre grande table principale. Tronquez ensuite la table de préparation pour le prochain lot.
c'est à dire.
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
Utilisez une procédure stockée qui prend des données d'entrée sous la forme de XML pour insérer des données.
Depuis votre code c #, insérez des données au format xml.
par exemple, en c #, la syntaxe serait la suivante:
object id_application = db.ExecuteScalar("procSaveApplication", xml)