Scripts em C#
Tags: ptbr, csharp, dotnet, braziliandevs
Published: 2021-09-14 12:56:21

## Introdução

Scripts são uma ferramenta bem antiga nos ambientes de TI e ainda hoje são muito utilizados. 

Sua natureza interpretada da à eles um dinamismo que supera em muito as facilidades de qualquer linguagem compilada.

JavaScript, com Node Js, e Python são exemplos que demonstram que a utilização de scripts no desenvolvimento de aplicações ainda segue em alta e sua força.

Estudando sobre Azure Functions me deparei com um arquivo a diferente extensão ".csx". Trata-se de um arquivo de scripts em linguagem C#, uma novidade para mim depois de vários anos trabalhando com essa linguagem.

Compartilho aqui com vocês o que aprendi sobre essa ferramenta e minhas conclusões.

## Os scripts em C Sharp

Nos idos de 2011, Kirill Osenkov informou a comunidade .NET que a MS estava entregando o primeiro _release preview_ do Roslyn Project. Nesse artigo, ele também informa que foi "_introduzido o conceito de um arquivo de script C#_" (OSENKOV, 2011). "_A ideia principal por trás de escrever scripts em C# foi permitir que o código fosse avaliado dinamicamente pelo _runtime_." (WOJ, 2015).

Para escrever um script C# você só precisa criar um arquivo com a extensão ".csx" em qualquer editor de textos. Depois de criar o arquivo você vai precisar de um programa para interpretá-lo. Hoje três opções:

- *Mono/Roslyn CSI* (que já vem instalado com o Visual Studio);
- *cs-scripts*;
- *dotnet-scripts*.

O _cs-scripts_ e _dotnet-scripts_ são multiplataforma, e  _dotnet-scripts_ tem a vantagem ser instalado com um `dotnet tool` e rodar usando o .NET 5. 

_No restante desse artigo vamos utilizar o dotnet-scripts para nossos exemplos. Para instalá-lo digite o comando abaixo em seu console de preferência:_

```Shell
dotnet tool install -g dotnet-script
```

## Sintaxe

> "Um script C# tem requisitos de sintaxe mais flexíveis, então a experiência geral pode ser sem cerimônias.  Abaixo estão algumas coisas a serem lembradas:
> - o ponto de entrada para o seu script é a primeira linha do seu arquivo (nenhum método `Main` obrigatório)
> - qualquer código é permitido a partir da primeira linha (nenhuma classe de nível superior, `Program`, obrigatória)
> - funções globais são permitidas;
> - sem `namespaces`;
> - sem um arquivo de projeto ou solução;
> - seu script pode ser totalmente autocontido;
> - instruções `using` e referências são importados implicitamente pelo aplicativo hospedeiro (responsável por executar o script)"
> (WOJCIESZYN, 2015)

Eu acrescento à essa lista o desenvolvimento de extensões, que foi algo que identifiquei nos meus testes. 

Abaixo um exemplo de arquivo de script C# utilizado para inicializar o processo:

```csharp
#load "Configs.csx"
#load "src/Data/TodoListRepository.csx"
#load "src/Services/TodoListService.csx"
#load "src/Controller/TodoListController.csx"
using Microsoft.Extensions.Configuration;

await Run(Args.ToArray());

async Task Run(string[] args)
{
    using TodoListRepository repository = new(Configs.Configuration.GetConnectionString("db"));
    TodoListService service = new(repository);
    TodoListController controller = new(service);
    await controller.ExecuteCommand(args);
}
```

Como pode ser visto a sintaxe da linguagem é a mesma já conhecida, só que sem a estrutura `Program.Main`. A forma de referenciar outros arquivos ".csx" é utilizando a diretiva `#load "<caminho do arquivo>"`. 

Abaixo um exemplo de arquivo para leitura de configurações, que faz referência à pacotes nuget:

```csharp
#r "nuget: Microsoft.Extensions.Configuration, 5.0.0"
#r "nuget: Microsoft.Extensions.Configuration.Binder, 5.0.0"
#r "nuget: Microsoft.Extensions.Configuration.UserSecrets, 5.0.0"
#r "nuget: Microsoft.Extensions.Configuration.EnvironmentVariables, 5.0.0"

using Microsoft.Extensions.Configuration;

public static class Configs
{
    private static IConfiguration _config;

    public static IConfiguration Configuration 
    {
        get 
        {
            if (_config is null)
                SetConfig();
            
            return _config;
        }
    }

    private static void SetConfig()
    {
        _config = new ConfigurationBuilder()
                        .SetBasePath(Directory.GetCurrentDirectory())
                        .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
                        .AddUserSecrets("csx-script-app")
                        .AddEnvironmentVariables()
                        .Build();
    }

    public static T GetConfig<T>(string sectionName)
    {
        return Configuration.GetSection(sectionName).Get<T>();
    }
}
```

Referenciar um pacote é bem simples utilizando `#r "nuget: <nome do pacote>, <versão>"`. 

Se for necessário importar um _assembly_ próprio é utilize a direntiva `#r "<caminho da dll>"`. Segue um exemplo:

```csharp
#r: "./build/MeuAssembly.dll"
```

Desenvolvi um aplicativo _Todo_ de exemplo e publiquei no meu GitHub onde é possível ver esses scripts em ação. Esse é o [link](https://github.com/andrebarsotti/csx-script-sample) do projeto.

## Debug

Se você utiliza o Visual Studio Code como seu editor de texto/IDE debugar um script é algo que seguirá os padrões que você já esta acostumado. Basta configurar seu _launch.json_ conforme abaixo, colocar o _breakpoint_ e clicar em F5.

```JSON
{
  "version": "0.2.0",
  "configurations": [
    {
      "name": ".NET Script Debug - Linux",
      "type": "coreclr",
      "request": "launch",
      "program": "${env:HOME}/.dotnet/tools/dotnet-script",
      "args": [
        "${file}"
      ],
      "cwd": "${workspaceFolder}",
      "stopAtEntry": false,
    },
    {
      "name": ".NET Script Debug - Windows",
      "type": "coreclr",
      "request": "launch",
      "program": "dotnet",
      "args": [
          "${env:USERPROFILE}/.dotnet/tools/.store/dotnet-script/1.2.1/dotnet-script/1.2.1/tools/net5.0/any/dotnet-script.dll",
          "${file}"
      ],
      "cwd": "${workspaceRoot}",
      "stopAtEntry": false
    }
  ]
}
```

## Compilação com um aplicativo e execução no Linux.

Um ponto interessante que achei utilizando o `dotnet-script` no Windows foi a possibilidade de criar um aplicativo autocontido. Para fazer isso é muito simples basta publicar o script como no exemplo abaixo:

```Sheel
dotnet script publish Main.csx
```

Um diretório _publish_ será criado com um executável. Como é um aplicativo autocontido, basta copiá-lo para qualquer computador e, mesmo sem o .NET instalado, executá-lo.

Em um ambiente Linux incluindo como primeira linha a diretiva `#!/usr/bin/env dotnet-script` é possível executar o script como qualquer outro, lembrando que é preciso dar permissão de execução no arquivo.

## Conclusão e aplicações

Observando a tecnologia vemos que essa ferramenta é bem madura e que vem se desenvolvendo junto com a plataforma e que irá continuar nesse caminho no futuro.

Hoje é possível escrever um programa console em C# de forma minimalista sem uma classe Program, um método Main e com funções globais, algo que até pouco tempo atrás era impensável. Com scripts C# você consegue tudo isso e ainda a vantagem de desenvolver sem um ".csproj" e sem o tempo de compilação, apenas escrever o código e já validar seu programa prontamente.

Testes de integração que serão executados sob demanda; programas de carga que serão utilizados apenas uma vez; pequenos testes e estudos de algum pacote Nuget novo; Scripts de configuração de ambiente. Esses são apenas alguns exemplos de utilização de scripts em C#.

No caso de scripts para configuração de ambiente ou inicialização de uma aplicativo, ainda existe a vantagem latente de seguir com a mesma linguagem que o projeto foi desenvolvido. Para a equipe isso é uma vantagem, visto que é preciso um esforço adicional para aprender e escrever o mesmo script com um bash script ou bat tradicional.

Em programas de carga existe ainda a vantagem de conseguir reaproveitar o código e as regras de negócio do próprio projeto, sem duplicidades.

As aplicações são muitas e certamente vale a pena incluir no "arsenal".

## Referências

BAHRAMINEZHAD, Ali. Hitchhiker’s Guide to the C# scripting. ITNEXT, 2019. Disponível em: <[https://itnext.io/hitchhikers-guide-to-the-c-scripting-13e45f753af9](https://itnext.io/hitchhikers-guide-to-the-c-scripting-13e45f753af9)>. Acesso em: 11-09-2021

MICHAELIS, Mark. C# Scripting. Microsotf Docs, 2016. Disponível em: <[https://docs.microsoft.com/en-us/archive/msdn-magazine/2016/january/essential-net-csharp-scripting](https://docs.microsoft.com/en-us/archive/msdn-magazine/2016/january/essential-net-csharp-scripting)>. Acesso em: 11-09-2021

OSENKOV, Kirill. Introducing the Microsoft “Roslyn” CTP. Microsoft DevBlogs, 2011. Disponível em <[https://devblogs.microsoft.com/visualstudio/introducing-the-microsoft-roslyn-ctp/](https://devblogs.microsoft.com/visualstudio/introducing-the-microsoft-roslyn-ctp/)>. Acesso em: 12-09-2021

SALVADEO, André C B. Todo List em C# script (CSX). Github, 2021. Disponível em <[https://github.com/andrebarsotti/csx-script-sample](https://github.com/andrebarsotti/csx-script-sample)>. Acesso em: 12-09-2021

VOGEL, Peter. Making Your Life Easier with C# Scripting. Visual Studio Magazine, 2021. Disponível em: <[https://visualstudiomagazine.com/articles/2021/06/14/csharp-scripting.aspx](https://visualstudiomagazine.com/articles/2021/06/14/csharp-scripting.aspx)>. Acesso em: 11-09-2021

WOJCIESZYN, Filip. Dotnet script. Github, 2021. Disponível em <[https://github.com/filipw/dotnet-script](https://github.com/filipw/dotnet-script)>. Acesso em: 11-09-2021

WOJCIESZYN, Filip. Adding C# scripting to your development arsenal - Part 1. Microsoft Docs - Blog Archive - Canadian Developer Connection, 2015. Disponível em <[https://docs.microsoft.com/pt-br/archive/blogs/cdndevs/adding-c-scripting-to-your-development-arsenal-part-1](https://docs.microsoft.com/pt-br/archive/blogs/cdndevs/adding-c-scripting-to-your-development-arsenal-part-1)>. Acesso em: 12-09-2021