Olá pessoal, tudo certo?

Recentemente, tive um problema ao tentar processar um arquivo em uma thread separada da main thread da aplicação.

Para vocês entenderem o que eu queria fazer, essa era a situação: O usuário faz o upload de um arquivo (.xlsx) e a aplicação faz diversos processamentos com as informações desse arquivo.

Bom, a manipulação desse arquivo não é nada rápida e eu não queria travar a thread principal da aplicação, deixando o usuário “preso” com um load de vários minutos na tela. Tive duas ideias para resolver isso, são elas:

Implementar um serviço fora da aplicação, que fosse chamado quando tivesse um novo arquivo para processar.

A segunda ideia foi, na mesma aplicação, “startar” uma nova thread para fazer o trabalho. Escolhi a segunda alernativa, vejam o problema que tive e como resolvi.

O código inicial

//Controller

[HttpPost]
public ActionResult Processo(HttpPostedFileBase arquivo)
{
 try
 {
  var stream = arquivo.InputStream;
  new Thread(() => ProcessarArquivo(stream)).Start();
  TempData["MensagemSucesso"] = "O arquivo está sendo processado.";
 }
 catch (Exception ex)
 {
  TempData["MensagemErro"] = ex.Message;
 }
return RedirectToAction("MinhaView");
}

//Metodo que processa o arquivo

private void ProcessarArquivo(Stream file)
{
 var arquivo = ExcelReaderFactory.CreateOpenXmlReader(file);

 //...
 //...
}

Ao executar o código acima, uma NullReferenceException estourava no método ProcessarArquivo. O interessante, é que a Exception hora acontencia em uma linha, hora em outra.

Pesquisando o que poderia estar acontecendo, acabei encontrando um problema parecido com o meu. Aparentemente, a classe HttpPostedFileBase dá um Dispose() quando a action onde ela foi criada é finalizada. Como um outro processo começava a ser executado em parelelo, ao terminar de executar a action Processo que recebe o HttpPostedFileBase, o .net dava o Dispose e a aplicação perdia a referência do Stream que estava sendo manipulado. Por esse motivo, a exception não acontecia sempre no mesmo local, pois, o valor só era perdido quando a action Processo era finalizada.

A resolução

Ao descobrir o que estava acontecendo, ficou fácil pensar em soluções. A mais rápida e simples que veio em mente foi criar uma nova instância de Stream e copiar o valor que estava recebendo por parâmetro e passar essa cópia como argumento para o método ProcessarArquivo. E foi assim que fiz, veja:

//Controller

[HttpPost]
public ActionResult Processo(HttpPostedFileBase arquivo)
{
 try
 {
  var stream = arquivo.InputStream;
  var memoryStream = new MemoryStream();
  stream.CopyTo(memoryStream);
  new Thread(() => ProcessarArquivo(memoryStream)).Start();
  TempData["MensagemSucesso"] = "O arquivo está sendo processado.";
 }
 catch (Exception ex)
 {
  TempData["MensagemErro"] = ex.Message;
 }
return RedirectToAction("MinhaView");
}

O método ProcessarArquivo não teve alteração. Com essa implementação, tudo funcionou lindo.

Alguém precisou implementar algo parecido? Tem uma solução diferente? Compartilhe conosco nos comentários 🙂

Até o próximo post!

 

Link’s úteis

Excel Data Reader – Read Excel files in .NET