Marivaldo Júnior
Sep 5, 2018 · 4 min read

Compilando e carregando DLLs em tempo de execução no .NET com o Roslyn

Soa até um pouco estranho a ideia de compilar uma DLL enquanto seu código está executando, a primeira vez que li sobre isso pensei “pra que eu iria precisar disso?”.

A verdade é que dependendo de como o seu serviço é oferecido, chega até a ser inviável que ele não compile código enquanto executa. Um exemplo bem simples e usual: existem diversos sites onde é possível escrever e testar códigos fonte de forma rápida — primeiro que seria impossível prever a infinidade de códigos que podem ser compilados, segundo que seria completamente inviável parar e rodar a execução do seu serviço a cada compilação. Claro que essa não é a única utilidade desse recurso, uma infinidade de possibilidades está aberta.

O responsável por toda essa “mágica” no .NET é o Roslyn,o mesmo disponibiliza uma API de código aberto para tal.

Pré-requisitos

  • .NET Core instalado (para esse exemplo, porém, também é possível utilizar no .NET padrão)

Pacotes necessários

  • Microsoft.CodeAnalysis.CSharp (para compilar e analisar códigos)
  • System.Runtime.Loader (para carregar o assembly)

Exemplo passo a passo

Existem basicamente duas formas de montar o código que será compilado, a forma que eu acho mais “natural” realizando um parser de um código fonte ou utilizando os método da API para montar toda a árvore de sintaxe. Neste post demonstro como gerar a mesma DLL utilizando as duas formas.

O código fonte da nossa DLL de demonstração será:

Código fonte da DLL

Exemplificando as duas formas, podemos montar a árvore via parser, criando a string:

Código fonte em string

E em seguida transformar no objeto SyntaxTree:

Método que realiza o parse do código fonte em string para o objeto SyntaxTree

Ou montar totalmente via API:

Código fonte montado utilizando a API

Os códigos fontes mostrados acima, quando compilados pelo Roslyn, serão idênticos. Claro que nesse exemplo podemos notar que montar o código via API é bem mais complexo do que utilizar o parser, porém, seu novo código pode ser tão dinâmico ao ponto que não é possível prever a estrutura. Tentando simplificar, seria como utilizar o parser fosse viável quando temos um template do código a ser gerado, já via API o código será completamente dinâmico.

Muito bem, considerando que ambos os códigos acima nos entregaram o objeto SyntaxTree, podemos montar a compilação:

Compilação do código fonte

No método estamos criando a estrutura de compilação no Roslyn,onde:

  • assemblyName é o nome do nosso novo assembly
  • syntaxTrees é a lista de sintaxes que vão ser compiladas
  • references é a lista de referências de outras DLLs utilizadas no código que será compilado

Para nosso exemplo, precisamos da DLL System.Runtime, que pode ser obtida por:

Lista de referências

Finalmente podemos criar a nova DLL:

Criação da DLL

Ao chamar o método compilation.Emit(file) será realizada a compilação e podemos obter os diagnósticos que também podem ser utilizado para tratamento de erros.

É possível carregar essa nova DLL em sua aplicação com:

Carregamento da nova DLL

A partir daí o uso da DLL pode ser feito com ajuda do Reflection.

Com esse exemplo simples, observamos que o Roslyn nos abre um grande leque de possibilidades quando pensamos em alterar o comportamento ao longo da execução.

Espero que eu tenha conseguido ser claro e que você tenha gostado.

Muito obrigado pela leitura!

Um código fonte de exemplo está disponível em meu GitHub.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade