INSERT OTIMIZADO COM ENTITY FRAMEWORK

DS2T 04/12/2015 03:10:50
#454785
Boa noite!

Surgiu a necessidade de eu importar 500 mil registros para uma tabela num banco de dados SQL Server.
Estou usando Entity Framework.
Eis meu código:


 Using ctx As ModeloEFContainer = New ModeloEFContainer
ctx.Configuration.AutoDetectChangesEnabled = False
For i As Integer = 1 To 500000
Dim expressao As New ExpressaoEntity
expressao.Resultado = enumResultado.Indefinido
expressao.ExpressaoString = [Ô]3+2[Ô]
ctx.Set(Of ExpressaoEntity).Add(expressao)
If i Mod 10000 = 0 Then
ctx.SaveChanges()
End If
Next
End Using



Como puderam notar, eu já desativei AutoDetectChangesEnabled. Isso teve um aumento de performance incrível.
Outra coisa que percebi uma melhora significativa foi apenas realizar o commit com SaveChanges a cada 10000 registros. Tentei com 100000 anteriormente, mas aqui gerou um erro estranho de thread (não entendi também). E se eu desse um SaveChanges a cada iteração, ficaria bem mais lento do que a cada 10000.

Mas mesmo assim, estou aqui a quase 1 hora e não chegou na metade.
Como podem ver, a tupla do meu registro é pequena. (Apesar desse ser apenas um exemplo)

Tem algo a mais que eu possa fazer pra não demorar tanto? Na verdade, ele está demorando mesmo é no método SaveChanges. 10 mil registros tá levando cerca de 10 minutos pra persistir. Isso dá aproximadamente 33.3333 registros por segundo.

Quem souber alguma dica, fico muito agradecido!
JABA 04/12/2015 03:14:49
#454786
Qual a necessidade de salvar dentro do comando FOR? Não vejo um sentido sensato para isso.

Using ctx As ModeloEFContainer = New ModeloEFContainer
ctx.Configuration.AutoDetectChangesEnabled = False
For i As Integer = 1 To 500000
Dim expressao As New ExpressaoEntity
expressao.Resultado = enumResultado.Indefinido
expressao.ExpressaoString = [Ô]3+2[Ô]
ctx.Set(Of ExpressaoEntity).Add(expressao)
Next
[txt-color=#e80000]ctx.SaveChanges()[/txt-color]
End Using
DS2T 04/12/2015 03:25:33
#454787
Pelo menos na minha máquina, ocorre um erro no SaveChages quando tento salvar um valor muito maior que 10 mil registros por vez.
Esse é o erro:

The CLR has been unable to transition from COM context 0x3322d98 to COM context 0x3322f08 for 60 seconds. The thread that owns the destination context/apartment is most likely either doing a non pumping wait or processing a very long running operation without pumping Windows messages. This situation generally has a negative performance impact and may even lead to the application becoming non responsive or memory usage accumulating continually over time. To avoid this problem, all single threaded apartment (STA) threads should use pumping wait primitives (such as CoWaitForMultipleHandles) and routinely pump messages during long running operations.
GUIMORAES 04/12/2015 10:54:16
#454794
DS2T,

Você já experimentou executar o aplicativo compilado?
Geralmente este erro ocorre quando tentamos executar o aplicativo em Debug pelo VS.
LAMPIAO 04/12/2015 11:36:52
#454797
Resposta escolhida
Fala DS2T,

Veja só, usando for ou foreach sempre será mais lento, em vez de usar o add, use o addrange, o addrange espera uma lista, então a sua variável i deve ser uma lista com os 50 mil valores.

Para você ter uma idéa de como isso é rápido, eu importo para o meu PDV 25.000 produtos, em menos de um minuto, lembrando que o objeto produto possui varias propriedades, claro que não descobrir isso do dia pra noite, antigamente eu usava o foreach e realmente fica lento, demora muito mesmo.

Eu não lembro a partir de que versão esta disponível o addrange, use o EntityFramework a partir da versão 6.0, eu não tenho certeza se esta disponível em uma versão abaixo da 6.0.

Abraços
KERPLUNK 04/12/2015 13:08:06
#454806
Tive um caso assim de quantidades absurdas de dados terem de ser inseridas no EF e passei por esse mesmo problema de performance. Era mais ou menos essa quantidade de registros, mas de hora em hora, então desempenho era crítico. Uma coisa que ajudou na performance, mas obviamente aumento o risco de inconsistências, foi desativar a validação além da auto-detecção de mudanças, no seu caso seria:

ctx.Configuration.ValidateOnSaveEnabled = false;


Mas o que fez mesmo diferença foi usar o add-in BulkInsert.
Experimente primeiro desabilitar a validação, se a performance ainda não for satisfatória, então use o BulkInsert
DS2T 05/12/2015 17:57:07
#454826
Desculpem a demora pessoal.

GUIMORAES123
Tentei rodar sem Debug. Realmente não apareceu nenhum erro, mas o processo do programa finalizou sozinho. Achei meio estranho.
Mas obrigado cara!

LAMPIAO
Fala aí Lampião, beleza cara?
Eu estava usando a versão 5.0 e não tinha o AddRange. Mas baixei pra testar e ver como era. Realmente, ficou mais rápido. Não entendi ainda o motivo disso. Até o SaveChanges ficou mais rápido. Isso que não entendi. Era pra ser mais rápido adicionar os itens na lista, mas não a persistência. Ou era?
Eu ainda vou fazer mais uns testes. To achando que pode ser a própria versão 6 que trouxe alguma melhoria no SaveChanges... não sei ainda. Obrigado!

LOCUTOR.. ops, KERPLUNK
Desabilitar a validação no Save melhorou bastante mesmo.O Bulkinsert eu ainda não cheguei a ver.
Dei uma pesquisada no MSDN mas não entendi ainda bem que tipo de validação fica desabilitada. Seria validação de chave estrangeira?


Obrigado a todos!
Tópico encerrado , respostas não são mais permitidas