Najszybszy sposób wstawiania w encji Framework

Szukam najszybszego sposobu wstawienia do encji frameworku.

Pytam o to ze względu na scenariusz, w którym masz aktywny TransactionScope i wstawianie jest ogromne(4000+). Potencjalnie może trwać dłużej niż 10 minut (domyślny limit czasu transakcji), co doprowadzi do niekompletnej transakcji.

Author: Jon Schneider, 2011-05-09

25 answers

Do twojej uwagi w komentarzach do twojego pytania:

"...SavingChanges (dla każdego zapis )..."

To najgorsza rzecz, jaką możesz zrobić! Wywołanie SaveChanges() dla każdego rekordu bardzo spowalnia wkładki masowe. Zrobiłbym kilka prostych testów, które prawdopodobnie poprawią wydajność:
  • zadzwoń SaveChanges() raz po wszystkich rekordach.
  • wywołanie SaveChanges() PO na przykład 100 rekordach.
  • wywołaj SaveChanges() po np. 100 rekordach i usuwaj kontekst i stworzyć nowy.
  • Wyłącz wykrywanie zmian

Do wkładek luzem pracuję i eksperymentuję z takim wzorem:

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

Mam program testowy, który wstawia 560.000 encji (9 Właściwości skalarnych, brak właściwości nawigacji) do DB. Z tym kodem działa w mniej niż 3 minuty.

Dla wykonania ważne jest wywołanie SaveChanges() po" wielu " rekordach ("wiele" około 100 lub 1000). Poprawia również wydajność usuwania kontekst po SaveChanges i utworzyć nowy. To usuwa kontekst ze wszystkich entites, SaveChanges tego nie robi, encje są nadal dołączone do kontekstu w stanie Unchanged. To rosnący rozmiar dołączonych podmiotów w kontekście spowalnia wstawianie krok po kroku. Tak więc pomocne jest wyczyszczenie go po pewnym czasie.

Oto kilka pomiarów dla moich 560.000 Bytów:

  • commitCount = 1, recreateContext = false: many hours (to Twój current procedure)
  • commitCount = 100, recreateContext = false: więcej niż 20 minut
  • 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
  • commit = 1, recreateContext = true: więcej niż 10 minut
  • commitCount = 10, recreateContext = true: 241 sec
  • commitCount = 100, recreateContext = true: 164 sec
  • commitCount = 1000, recreateContext = true: 191 sec

Zachowanie w pierwszym powyższym teście polega na tym, że wydajność jest bardzo nieliniowa i bardzo maleje w czasie. ("Wiele godzin" jest oszacowaniem, nigdy nie skończyłem tego testu, I zatrzymał się na 50.000 jednostek po 20 minutach.) To nieliniowe zachowanie nie jest tak istotne we wszystkich innych testach.

 857
Author: Slauma,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-05-09 20:39:36

Ta kombinacja wystarczająco zwiększa prędkość.

context.Configuration.AutoDetectChangesEnabled = false;
context.Configuration.ValidateOnSaveEnabled = false;
 164
Author: arkhivania,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-08-12 15:52:04

Najszybszym sposobem byłoby użycie rozszerzenia bulk insert , które opracowałem.

Używa SqlBulkCopy i custom datareader, aby uzyskać maksymalną wydajność. W rezultacie jest to ponad 20 razy szybsze niż użycie zwykłego insert lub AddRange / Align = "left" / BulkInsert vs EF AddRange

Użycie jest niezwykle proste

context.BulkInsert(hugeAmountOfEntities);
 97
Author: maxlego,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-11-09 07:17:54

Powinieneś spojrzeć na użycie System.Data.SqlClient.SqlBulkCopy do tego. Oto Dokumentacja , i oczywiście istnieje wiele samouczków online.

Przepraszam, wiem, że szukałeś prostej odpowiedzi, aby zmusić EF do zrobienia tego, co chcesz, ale operacje masowe nie są tak naprawdę tym, do czego są przeznaczone ORM.

 68
Author: Adam Rackis,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-05-09 17:17:37

Zgadzam się z Adamem Rackisem. SqlBulkCopy jest najszybszym sposobem przesyłania masowych rekordów z jednego źródła danych do drugiego. Użyłem tego do skopiowania 20K rekordów i zajęło to mniej niż 3 sekundy. Spójrz na poniższy przykład.

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,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-01-15 10:56:44

Zbadałem odpowiedź Slauma (co jest niesamowite, dzięki za pomysł man), i zmniejszyłem rozmiar partii, aż osiągnę optymalną prędkość. Patrząc na wyniki Slauma:

  • commitCount = 1, recreateContext = true: więcej niż 10 minut
  • commitCount = 10, recreateContext = true: 241 sec
  • commitCount = 100, recreateContext = true: 164 sec
  • commitCount = 1000, recreateContext = true: 191 sec

Widać, że istnieje prędkość zwiększ przy przenoszeniu z 1 do 10 i z 10 do 100, ale z 100 do 1000 prędkość wprowadzania spada ponownie.

Więc skupiłem się na tym, co się dzieje, gdy zmniejszasz rozmiar partii do wartości gdzieś pomiędzy 10 a 100, a oto moje wyniki (używam innej zawartości wiersza, więc moje czasy mają inną wartość):

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

Na podstawie moich wyników, rzeczywiste optimum jest około wartości 30 dla wielkości partii. Jest mniej niż 10 i 100. Problem w tym, że nie mam pojęcia dlaczego 30 jest optymalne, nie mogłem znaleźć na to żadnego logicznego wyjaśnienia.

 19
Author: Admir Tuzović,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-03-26 08:46:42

Polecam ten artykuł o tym, jak zrobić wkładki luzem za pomocą EF.

Entity Framework i slow bulk INSERTs

Bada te obszary i porównuje perfomance:

  1. Default EF (57 minut do zakończenia dodawania 30 000 rekordów)
  2. zastępowanie przez ADO.NET kod (25 sekund dla tych samych 30 000)
  3. context Bloat-utrzymuj aktywny Wykres kontekstowy na małym poziomie, używając nowego kontekstu dla każdej jednostki pracy (te same 30 000 wstawek zajmuje 33 sekund)
  4. Duże listy-Wyłącz AutoDetectChangesEnabled (zmniejsza czas do około 20 sekund)
  5. dozowanie (do 16 sekund)
  6. DbTable.AddRange () - (wydajność jest w zakresie 12)
 16
Author: ShaTin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-06-20 03:10:55

Jak mówią inni ludzie, SqlBulkCopy jest sposobem na to, jeśli chcesz naprawdę dobrą wydajność wstawiania.

Jest to trochę kłopotliwe w implementacji, ale istnieją biblioteki, które mogą ci w tym pomóc. Jest ich kilka, ale tym razem będę miał własną bibliotekę: https://github.com/MikaelEliasson/EntityFramework.Utilities#batch-insert-entities

Potrzebny jest tylko kod:

 using (var db = new YourDbContext())
 {
     EFBatchOperation.For(db, db.BlogPosts).InsertAll(list);
 }
O ile szybciej? Bardzo trudno powiedzieć, bo to zależy od tak wielu czynników, wydajności komputera, sieci, wielkości obiektu itp itp. Testy wydajności, które zrobiłem, sugerują, że 25k podmiotów można wstawić w okolicach 10s standardowym sposobem na localhost, jeśli zoptymalizujesz konfigurację EF, jak wspomniano w innych odpowiedziach. Z EFUtilities, który trwa około 300ms. jeszcze bardziej interesujące jest to, że zapisałem około 3 miliony jednostek w ciągu 15 sekund za pomocą tej metody, średnio około 200k jednostek na sekundę.

The one problem jest oczywiście, jeśli musisz wstawić releated data. Można to zrobić skutecznie w sql server za pomocą powyższej metody, ale wymaga to posiadania strategii generowania Id, która pozwala generować id w kodzie aplikacji dla rodzica, dzięki czemu można ustawić klucze obce. Można to zrobić za pomocą GUID lub czegoś w rodzaju Hilo id generation.

 14
Author: Mikael Eliasson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-12-19 10:44:11

Dispose() context tworzenie problemów, jeśli encje Add() opierają się na innych fabrycznie załadowanych encjach (np. właściwości nawigacji) w kontekście

Używam podobnej koncepcji, aby mój kontekst był mały, aby osiągnąć tę samą wydajność

Ale zamiast Dispose() kontekstu i odtworzenia, po prostu odłączam byty, które już 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;
} }

Owiń go try catch i TrasactionScope() jeśli potrzebujesz, nie pokazując ich tutaj za utrzymanie czystego kodu

 14
Author: Stephen Ho,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-07-24 13:06:42

Spróbuj użyć procedury składowanej, która otrzyma XML danych, które chcesz wstawić.

 7
Author: Maxim,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-05-09 17:18:23

Zrobiłem ogólne rozszerzenie przykładu @Slauma S powyżej;

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

Użycie:

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,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-11-25 13:35:24

Wiem, że to bardzo stare pytanie, ale jeden z gości powiedział, że opracował metodę rozszerzenia do używania bulk insert z EF, a kiedy sprawdziłem, odkryłem, że biblioteka kosztuje 599 $dzisiaj (dla jednego dewelopera). Może to ma sens dla całej biblioteki, jednak dla samej wkładki zbiorczej to za dużo.

Oto bardzo prosta metoda rozszerzenia, którą zrobiłem. Używam tego na parze z bazy danych pierwszy (nie testowane z kodem najpierw, ale myślę, że działa to samo). Zmień YourEntities z nazwa kontekstu:

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

Możesz użyć tego przeciwko dowolnej kolekcji, która dziedziczy z IEnumerable, w ten sposób:

await context.BulkInsertAllAsync(items);
 4
Author: Guilherme,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-01-22 04:48:05

Szukam najszybszego sposobu wstawiania do encji Framework

Dostępne są biblioteki innych firm obsługujące Bulk Insert:

  • Z.Extensions (Recommended)
  • EFUtilities
  • EntityFramework.BulkInsert

Zobacz: Entity Framework Bulk Insert library

należy zachować ostrożność przy wyborze biblioteki wstawiania zbiorczego. Only Entity Framework Extensions supports all kind of Asocjacje i spadki i tylko to jest nadal wspierane.


Zastrzeżenie: jestem właścicielem Entity Framework Extensions

Ta biblioteka Pozwala na wykonywanie wszystkich operacji zbiorczych, których potrzebujesz do swoich scenariuszy:

  • Bulk SaveChanges
  • Bulk Insert
  • Bulk Delete
  • Bulk Update
  • Bulk Merge

Przykład

// 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,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-08-18 16:25:46

Zgodnie z moją wiedzą jest no BulkInsert w EntityFramework Aby zwiększyć wydajność ogromnych wkładek.

W tym scenariuszu możesz użyć SqlBulkCopy W ADO.net, aby rozwiązać swój problem

 2
Author: anishMarokey,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-05-09 17:20:03

Oto porównanie wydajności między używaniem encji Framework i używaniem klasy SqlBulkCopy na realistycznym przykładzie: Jak masowo wstawiać złożone obiekty do bazy danych SQL Server

Jak już podkreślali inni, ORM nie są przeznaczone do użycia w operacjach masowych. Oferują elastyczność, oddzielenie obaw i inne korzyści, ale operacje masowe (z wyjątkiem odczytu masowego) nie należą do nich.

 2
Author: Zoran Horvat,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-07-14 14:13:50

Inną opcją jest użycie SqlBulkTools dostępnego z Nuget. Jest bardzo łatwy w użyciu i ma kilka zaawansowanych funkcji.

Przykład:

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

Zobacz dokumentacja aby uzyskać więcej przykładów i zaawansowanych zastosowań. Zastrzeżenie: jestem autorem tej biblioteki i wszelkie poglądy są mojego własnego zdania.

 2
Author: Greg R Taylor,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-10-29 23:16:30

Użycie 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,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-06-04 22:06:11

Jeden z najszybszych sposobów na zapisanie listy należy zastosować następujący kod

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

AutoDetectChangesEnabled = false

Add, AddRange& SaveChanges: nie wykrywa zmian.

ValidateOnSaveEnabled = false;

Nie wykrywa śledzenia zmian

Musisz dodać nuget

Install-Package Z.EntityFramework.Extensions

Teraz możesz użyć następującego kodu

var context = new MyContext();

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

context.BulkInsert(list);
context.BulkSaveChanges();
 2
Author: Reza Jenabi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-10-02 10:05:11

Sekretem jest wstawienie do identycznej pustej tabeli. Wkładki szybko się rozjaśniają. Następnie uruchom pojedynczy Wstaw z tego do głównej dużej tabeli. Następnie obcinaj stół przygotowawczy do następnej partii.

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,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-07-18 10:43:30

Czy kiedykolwiek próbowałeś wstawić przez pracownika lub zadanie w tle?

W moim przypadku wstawiam 7760 rejestrów, rozmieszczonych w 182 różnych tablicach z relacjami z kluczami obcymi (przez NavigationProperties).

Bez zadania zajęło to 2,5 minuty. Zadanie (Task.Factory.StartNew(...)) zajęło 15 sekund.

Wykonuję SaveChanges() tylko po dodaniu wszystkich encji do kontekstu. (w celu zapewnienia integralności danych)

 1
Author: Rafael A. M. S.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-05-27 20:19:58

Wszystkie napisane tutaj rozwiązania nie pomagają, ponieważ gdy wykonujesz SaveChanges (), polecenia insert są wysyłane do bazy danych jeden po drugim, tak działa Entity.

A jeśli podróż do bazy danych i z powrotem wynosi np. 50 ms, to czas potrzebny na wstawienie to ilość rekordów x 50 ms.

Musisz użyć BulkInsert, oto link: https://efbulkinsert.codeplex.com/

Dzięki temu zmniejszyłem czas wstawiania z 5-6 minut do 10-12 sekund.

 1
Author: Aleksa,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-10-01 13:05:03

Możesz użyć biblioteki Bulk package . Wersja Bulk Insert 1.0.0 jest używana w projektach mających Entity framework > = 6.0.0 .

Więcej opisów można znaleźć tutaj- Bulkoperation source code

 1
Author: Mohd Nadeem,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-04-17 13:54:31

[NOWE ROZWIĄZANIE DLA POSTGRESQL] Hej, wiem, że to dość stary post, ale ostatnio napotkałem podobny problem, ale używaliśmy Postgresql. Chciałem użyć skutecznego bulkinsert, co okazało się dość trudne. Nie znalazłem żadnej odpowiedniej wolnej biblioteki, aby to zrobić w tym DB. Znalazłem tylko tego pomocnika.: https://bytefish.de/blog/postgresql_bulk_insert / który jest również na Nuget. Napisałem mały mapper, który automatycznie mapuje właściwości w sposób 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;
        }

Używam go w następujący sposób (miałem podmiot o nazwie Przedsiębiorstwo):

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

Pokazałem przykład z transakcją, ale można to zrobić również przy normalnym połączeniu pobranym z kontekstu. undertoadd jest wyliczany z normalnych rekordów entity, które chcę dodać do DB.

To rozwiązanie, do którego mam po kilku godzinach badań i prób, jest jak można się spodziewać znacznie szybsze i w końcu łatwe w użyciu i darmowe! Naprawdę radzę ci używać tego rozwiązanie, nie tylko z wyżej wymienionych powodów, ale także dlatego, że tylko z nim nie miałem problemów z samym Postgresqlem, wiele innych rozwiązań działa bez zarzutu np. z SqlServer.

 1
Author: Michał Pilarek,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-09-05 21:03:27

Ale dla więcej niż (+4000) wstawek zalecam użycie procedury składowanej. dołączony czas, jaki upłynął. Wstawiłem 11.788 wierszy w 20"Tutaj wpisz opis obrazka

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));
    }
 0
Author: Marinpietri,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-11-20 00:54:00

Użyj procedury składowanej, która pobiera dane wejściowe w postaci xml do wstawiania danych.

Z kodu c# podaj dane jako xml.

Np. w c# składnia będzie taka:

object id_application = db.ExecuteScalar("procSaveApplication", xml)
 0
Author: arun tiwari,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-01-07 03:33:19