Gerenciando Referências de Assembly em Scripts C# com Roslyn

Ao utilizar a classe CSharpScript do Roslyn para executar código C#, o ambiente de script inclui automaticamente um conjunto de assemblies padrão. No entanto, para acessar tipos e métodos de assemblies personalizados ou de terceiros que não fazem parte desse conjunto padrão, é essencial fornecer referências explícitas. Este artigo detalha como incluir assemblies adicionais ao executar scripts C# utilizando a API CSharpScript do Roslyn.

Cenário de Exemplo

Para ilustrar a necessidade de referências, vamos criar uma biblioteca de classes C# simples. Esta biblioteca conterá uma classe com um método estático público que apenas exibe uma mensagem no console.


namespace MinhaBiblioteca.Utilitarios
{
    public class Exemplo
    {
        public static void ImprimirMensagem(string origemChamada)
        {
            Console.WriteLine($"Mensagem da Biblioteca: Chamada de '{origemChamada}' para Exemplo.ImprimirMensagem().");
        }
    }
}

Em seguida, criaremos uma aplicação console que faz referência a esta biblioteca de classes. Tentaremos invocar o método ImprimirMensagem de duas maneiras: diretamente e através de um script C# executado pelo Roslyn.


using MinhaBiblioteca.Utilitarios;
using Microsoft.CodeAnalysis.CSharp.Scripting;
using Microsoft.CodeAnalysis.Scripting; // Necessário para CompilationErrorException
using System;
using System.Threading.Tasks;

namespace AplicativoConsole
{
    internal class Program
    {
        static async Task Main(string[] args)
        {
            Console.WriteLine("--- Chamada Direta para o Método da Biblioteca ---");
            Exemplo.ImprimirMensagem("Aplicativo Console (Direta)");

            Console.WriteLine("\n--- Tentativa de Chamada Via Script (Esperada Falha) ---");
            var codigoScriptComErro = @"
                // Tentativa de usar a classe sem referência explícita para o script
                MinhaBiblioteca.Utilitarios.Exemplo.ImprimirMensagem(""Aplicativo Console (Via Script - Erro)"");
            ";

            try
            {
                await CSharpScript.Create(codigoScriptComErro).RunAsync();
            }
            catch (CompilationErrorException ex)
            {
                Console.WriteLine($"\nErro de Compilação do Script Detectado:\n{string.Join(Environment.NewLine, ex.Diagnostics)}");
                Console.WriteLine("Causa: O script não consegue encontrar a definição para 'MinhaBiblioteca.Utilitarios.Exemplo'.");
            }
            catch (Exception ex)
            {
                Console.WriteLine($"\nErro Inesperado: {ex.Message}");
            }
        }
    }
}

Ao executar este código, você observará que a chamada direta funciona perfeitamente, mas a execução do script falha com um erro de compilação similar a este:


--- Chamada Direta para o Método da Biblioteca ---
Mensagem da Biblioteca: Chamada de 'Aplicativo Console (Direta)' para Exemplo.ImprimirMensagem().

--- Tentativa de Chamada Via Script (Esperada Falha) ---

Erro de Compilação do Script Detectado:
(3,17): error CS0234: O tipo ou nome do namespace 'Utilitarios' não existe no namespace 'MinhaBiblioteca' (está faltando uma referência de assembly?)
(3,17): error CS0103: O nome 'Exemplo' não existe no contexto atual
Causa: O script não consegue encontrar a definição para 'MinhaBiblioteca.Utilitarios.Exemplo'.

Este erro ocorre porque, embora o aplicativo console referencie MinhaBiblioteca.dll, o ambiente de execução do script C# criado por CSharpScript.Create não herda automaticamente essas referências. Mesmo que você adicione uma diretiva using MinhaBiblioteca.Utilitarios; dentro do script, o problema persistirá, pois o ambiente de script ainda não sabe onde encontrar o assembly.

Solucionando o Problema: Adicionando Referências de Assembly

Para permitir que CSharpScript localize e utilize tipos de assemblies externos, precisamos fornecer as referências de assebmly explicitamente através do objeto ScriptOptions. Isso é feito utilizando o método WithReferences.

Modificaremos o código do aplicativo console para incluir a referência ao nosso assembly:


using System.Reflection; // Para Assembly.Load
using MinhaBiblioteca.Utilitarios; // Apenas para a chamada direta
using Microsoft.CodeAnalysis.CSharp.Scripting;
using Microsoft.CodeAnalysis.Scripting; // Para ScriptOptions e CompilationErrorException
using System;
using System.Threading.Tasks;

namespace AplicativoConsole
{
    internal class Program
    {
        static async Task Main(string[] args)
        {
            Console.WriteLine("--- Chamada Direta para o Método da Biblioteca ---");
            Exemplo.ImprimirMensagem("Aplicativo Console (Direta)");

            Console.WriteLine("\n--- Chamada Via Script com Referência (Sucesso Esperado) ---");
            var codigoScriptComSucesso = @"
                using MinhaBiblioteca.Utilitarios; // Importa o namespace dentro do script
                Exemplo.ImprimirMensagem(""Aplicativo Console (Via Script - Sucesso)"");
            ";

            // 1. Carregar o assembly:
            // Se o assembly já estiver carregado no AppDomain (ex: porque o console app o referencia),
            // Assembly.Load(nomeDoAssembly) é suficiente.
            // Caso contrário, Assembly.LoadFrom("caminho/completo/para/MinhaBiblioteca.dll") pode ser necessário.
            var assemblyDaBiblioteca = Assembly.Load("MinhaBiblioteca");

            // 2. Criar as opções de script e adicionar a referência:
            var opcoesDeScript = ScriptOptions.Default.WithReferences(assemblyDaBiblioteca);

            // 3. Executar o script com as opções configuradas:
            await CSharpScript.Create(codigoScriptComSucesso, opcoesDeScript).RunAsync();

            Console.WriteLine("\n--- Alternativas para Referenciar Assemblies ---");

            // Opção 1: Referenciar usando o caminho do arquivo do assembly (requer caminho absoluto)
            // string caminhoAbsolutoAssembly = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "MinhaBiblioteca.dll");
            // var opcoesPorCaminho = ScriptOptions.Default.WithReferences(caminhoAbsolutoAssembly);
            // await CSharpScript.Create(codigoScriptComSucesso, opcoesPorCaminho).RunAsync();

            // Opção 2: Referenciar usando um objeto MetadataReference (requer caminho absoluto)
            // var metaReferencia = MetadataReference.CreateFromFile(caminhoAbsolutoAssembly);
            // var opcoesPorMetaRef = ScriptOptions.Default.WithReferences(metaReferencia);
            // await CSharpScript.Create(codigoScriptComSucesso, opcoesPorMetaRef).RunAsync();
        }
    }
}

Ao executar este código atualizado, a saída será:


--- Chamada Direta para o Método da Biblioteca ---
Mensagem da Biblioteca: Chamada de 'Aplicativo Console (Direta)' para Exemplo.ImprimirMensagem().

--- Chamada Via Script com Referência (Sucesso Esperado) ---
Mensagem da Biblioteca: Chamada de 'Aplicativo Console (Via Script - Sucesso)' para Exemplo.ImprimirMensagem().

--- Alternativas para Referenciar Assemblies ---

Como demonstrado, o método WithReferences de ScriptOptions permite adicionar referências de assembly de diversas formas: aceitando diretamente um objeto Assembly, uma string contendo o caminho completo para o assembly, ou um objeto MetadataReference. A escolha do método depende de como o assembly é carregado ou de onde ele está localizado no sistema de arquivos.

Tags: Roslyn CSharpScript AssemblyReference ScriptOptions dotnet

Publicado em 7-6 19:18