Atualização em lote / excluir EF5

Qual é a melhor maneira de lidar com atualizações em lote usando o (Entity Framework) EF5? Eu tenho dois casos particulares em que estou interessado:

  1. Atualizando um campo (por exemplo, UpdateDate) para uma lista (List) entre 100 e 100.000 Id’s, que é a chave primária. Chamar cada atualização separadamente parece ter muita sobrecarga e leva muito tempo.

  2. Inserindo muitos, também entre os 100 e os 100.000, dos mesmos objects (eg Usuários) de uma só vez.

Qualquer bom conselho?

  1. Existem dois projetos de código aberto que permitem isso: EntityFramework.Extended e Entity Framework Extensions . Você também pode verificar a discussão sobre atualizações em massa no site codeplex da EF.
  2. A inserção de 100k registros através do EF é, em primeiro lugar, uma arquitetura de aplicativo incorreta. Você deve escolher diferentes tecnologias leves para importações de dados. Mesmo a operação interna da EF com um grande conjunto de registros custará muito tempo de processamento. Atualmente, não há solução para inserções em lote para EF, mas há uma ampla discussão sobre esse recurso no site de código plex da EF.

Eu vejo as seguintes opções:

1 A maneira mais simples – crie sua requisição SQL por mãos e execute através de ObjectContext.ExecuteStoreCommand

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

2 Use EntityFramework.Extended

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

3 Faça sua própria extensão para a EF. Há um artigo Bulk Delete onde essa meta foi obtida herdando a class ObjectContext . Vale a pena dar uma olhada. A inserção / atualização em massa pode ser implementada da mesma maneira.

Você pode não querer ouvi-lo, mas sua melhor opção é não usar o EF para operações em massa. Para atualizar um campo em uma tabela de registros, use uma instrução Update no database (possivelmente chamada por meio de um procedimento armazenado mapeado para uma function EF). Você também pode usar o método Context.ExecuteStoreQuery para emitir uma instrução Update para o database.

Para inserções massivas, sua melhor opção é usar o Bulk Copy ou o SSIS. A EF exigirá um hit separado no database para cada linha sendo inserida.

Inserções em massa devem ser feitas usando a class SqlBulkCopy. Por favor, veja a pré-existente StackOverflow Q & A sobre a integração dos dois: SqlBulkCopy e Entity Framework

O SqlBulkCopy é muito mais fácil de usar que o bcp (utilitário de linha de comando Bulk Copy) ou até mesmo o OPEN ROWSET.

Concordo com a resposta aceita de que ef é provavelmente a tecnologia errada para inserções em massa. No entanto, acho que vale a pena dar uma olhada no EntityFramework.BulkInsert .

Aqui está o que eu fiz com sucesso:

 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 = $@"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 = $@"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; }