Aggiornamento / eliminazione batch EF5

Qual è il modo migliore per gestire gli aggiornamenti batch utilizzando (Entity Framework) EF5? Ho 2 casi particolari a cui sono interessato:

  1. Aggiornamento di un campo (ad esempio UpdateDate) per un elenco (Elenco) compreso tra 100 e 100.000 Id, che è la chiave primaria. Chiamare separatamente ogni aggiornamento sembra essere troppo costoso e richiede molto tempo.

  2. Inserendo molti, anche tra 100 e 100.000, degli stessi oggetti (ad es. Utenti) in un unico passaggio.

Qualche buon consiglio?

  1. Esistono due progetti open source che consentono questo: EntityFramework.Extended ed Entity Framework Extensions . Puoi anche consultare la discussione sugli aggiornamenti collettivi sul sito di codeplex di EF.
  2. L’inserimento di record 100k tramite EF è in primo luogo un’architettura di applicazione errata. Dovresti scegliere una tecnologia leggera diversa per le importazioni di dati. Anche l’operazione interna di EF con un set di record così grande ti costerà un sacco di tempo di elaborazione. Attualmente non esiste una soluzione per gli inserimenti batch per EF, ma esiste un’ampia discussione su questa caratteristica sul sito di plex code di EF.

Vedo le seguenti opzioni:

1. Il modo più semplice: crea la tua richiesta SQL manualmente ed esegui ObjectContext.ExecuteStoreCommand

context.ExecuteStoreCommand("UPDATE TABLE SET FIELD1 = {0} WHERE FIELD2 = {1}", value1, value2); 

2. Utilizzare EntityFramework.Extended

 context.Tasks.Update( t => t.StatusId == 1, t => new Task {StatusId = 2}); 

3. Crea la tua estensione per EF. C’è un articolo Bulk Delete in cui questo objective è stato raggiunto ereditando la class ObjectContext . Vale la pena dare un’occhiata. L’inserimento / aggiornamento di massa può essere implementato allo stesso modo.

Potresti non volerlo sentire, ma la tua migliore opzione è non usare l’EF per operazioni di massa. Per aggiornare un campo attraverso una tabella di record, utilizzare un’istruzione Update nel database (eventualmente chiamata tramite un processo memorizzato mappato su una funzione EF). È inoltre ansible utilizzare il metodo Context.ExecuteStoreQuery per emettere un’istruzione Update nel database.

Per inserti massicci, la soluzione migliore è utilizzare Bulk Copy o SSIS. EF richiederà un hit separato nel database per ogni riga inserita.

Gli inserimenti collettivi dovrebbero essere eseguiti utilizzando la class SqlBulkCopy. Si prega di consultare StackOverflow Q & A preesistente sull’integrazione dei due: SqlBulkCopy e Entity Framework

SqlBulkCopy è molto più user-friendly di bcp (Bulk Copy utility da riga di comando) o anche OPEN ROWSET.

Sono d’accordo con la risposta accettata che ef è probabilmente la tecnologia sbagliata per gli inserimenti di massa. Tuttavia, penso che valga la pena dare un’occhiata a EntityFramework.BulkInsert .

Ecco cosa ho fatto con successo:

 private void BulkUpdate() { var oc = ((IObjectContextAdapter)_dbContext).ObjectContext; var updateQuery = myIQueryable.ToString(); // This MUST be above the call to get the parameters. var updateParams = GetSqlParametersForIQueryable(updateQuery).ToArray(); var updateSql = [email protected]"UPDATE dbo.myTable SET col1 = x.alias2 FROM dbo.myTable JOIN ({updateQuery}) x(alias1, alias2) ON x.alias1 = dbo.myTable.Id"; oc.ExecuteStoreCommand(updateSql, updateParams); } private void BulkInsert() { var oc = ((IObjectContextAdapter)_dbContext).ObjectContext; var insertQuery = myIQueryable.ToString(); // This MUST be above the call to get the parameters. var insertParams = GetSqlParametersForIQueryable(insertQuery).ToArray(); var insertSql = [email protected]"INSERT INTO dbo.myTable (col1, col2) SELECT x.alias1, x.alias2 FROM ({insertQuery}) x(alias1, alias2)"; oc.ExecuteStoreCommand(insertSql, insertParams.ToArray()); } private static IEnumerable GetSqlParametersForIQueryable(IQueryable queryable) { var objectQuery = GetObjectQueryFromIQueryable(queryable); return objectQuery.Parameters.Select(x => new SqlParameter(x.Name, x.Value)); } private static ObjectQuery GetObjectQueryFromIQueryable(IQueryable queryable) { var dbQuery = (DbQuery)queryable; var iqProp = dbQuery.GetType().GetProperty("InternalQuery", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public); var iq = iqProp.GetValue(dbQuery, null); var oqProp = iq.GetType().GetProperty("ObjectQuery", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public); return (ObjectQuery)oqProp.GetValue(iq, null); } 
  public static bool BulkDelete(string tableName, string columnName, List val) { bool ret = true; var max = 2000; var pages = Math.Ceiling((double)val.Count / max); for (int i = 0; i < pages; i++) { var count = max; if (i == pages - 1) { count = val.Count % max; } var args = val.GetRange(i * max, count); var cond = string.Join("", args.Select((t, index) => $",@p{index}")).Substring(1); var sql = $"DELETE FROM {tableName} WHERE {columnName} IN ({cond}) "; ret &= Db.ExecuteSqlCommand(sql, args.ToArray()) > 0; } return ret; }