Back-End

16 out, 2015

Escrevendo cmdlets customizados com C#

Publicidade

Muitas vezes criamos aplicativos do tipo Console Application para nos auxiliar em tarefas diversas. O problema desta abordagem é que nunca existe um padrão para passagem de parâmetros, nem para exibir ajuda ao usuário. Por exemplo, se um aplicativo recebe três parâmetros, você precisa sempre executar o aplicativo passando os parâmetros na ordem correta. A consequência é que por mais que a ferramenta seja extremamente útil, acaba se tornando difícil de ser utilizada por terceiros e até mesmo por você depois de um tempo.

É possível resolver estes problemas criando um Console Application mais elaborado para permitir a passagem de parâmetros no estilo chave/valor de tal forma que a ordem dos parâmetros não faça diferença. Ainda assim, é preciso controlar os parâmetros obrigatórios e a exibição da ajuda para o usuário, entre outras coisas.

Uma alternativa elegante que tem se tornado uma tendência em grandes projetos (SQL Server, Microsoft IIS, Exchange), mas que ainda tem baixa adoção pela comunidade, é criar cmdlet (pronuncia-se “command-let”) customizados.

Além de resolverem os problemas descritos acima, os cmdlets executam no Windows PowerShell, que é um shell de linha de comando mais moderno que o ‘Prompt de Comando’, ou simplesmente ‘cmd.exe’. Uma das características do Windows PowerShell é ser orientado a objetos. Isto significa que a saída de um cmdlet não é texto simples, mas um .NET Object. Dessa forma, a saída de seu cmdlet pode ser combinada com qualquer outro cmdlet, o que é ainda mais útil para o usuário.

Exemplo demostrando que no PowerShell o output é um Object e não texto:

Um simples Get-Date que aparentemente retorna uma string…

get-date

Quando combinado com o cmdlet Get-Member, podemos ver que na verdade é um System.DateTime.

get-date-get-member

E combinando com o cmdlet Select-Object podemos obter o valor de uma propriedade específica.

get-date-select-object

Criando um cmdlet

No exemplo, vamos supor que estamos trabalhando em um sistema de catálogo de produtos e, para uma finalidade específica, precisamos de um utilitário que exporte os dados de um produto. Não adianta fazer uma query no banco de dados, pois queremos que sejam processadas todas as regras de negócio, portanto vamos obter o produto usando o método da camada de serviço na nossa aplicação.

Ao invés de criarmos um Console Application tradicional, iremos criar um cmdlet chamado ‘Get-Produto’. Um cmdlet nada mais é do que um simples projeto do tipo Class Library com referência a System.Management.Automation.dll (disponível em “C:\Program Files (x86)\Reference Assemblies\Microsoft\WindowsPowerShell\3.0″).

Veja o Exemplo abaixo:

namespace CS.PS.Tools
{
   using System.Management.Automation;
   using CS.PS.Tools.Services;
 
    [Cmdlet(VerbsCommon.Get, "Produto")]
    public class GetProduto : Cmdlet
    {
        private RepositorioDeProdutos repositorio;
 
        [Parameter(Mandatory = true, HelpMessage ="Ids do produtos.", Position = 0)]
        public int[] Ids { get; set; }
 
        protected override void BeginProcessing()
        {
            this.repositorio = RepositorioDeProdutos.ObterInstancia();
        }
 
        protected override void ProcessRecord()
        {
            foreach (var id in this.Ids)
            {
                WriteVerbose(string.Format("Obtendo produto com ID #{0}.", id));
                var produto = repositorio.ObterProduto(id);
                WriteObject(produto);
 
                if (produto == null)
                {
                    WriteVerbose(string.Format("Não existe produto com ID #{0}.", id));
                }
            }
        }
 
        protected override void EndProcessing()
        {
            base.EndProcessing();
        }
    }
}

No exemplo, criamos uma class “GetProduto” que herda de “System.Management.Automation.Cmdlet”.

A class foi decorada com o atributo “[Cmdlet(VerbsCommon.Get, “Produto”)]”. Dessa forma, independente do nome da class o cmdlet será executado no Windows PowerShell por meio da instrução “Get-Produto”.

Adicionamos a propriedade “Ids” tipada como um Array de int. Cada propriedade definida na class será um parâmetro do seu cmdlet. Neste exemplo, a propriedade Ids será usada para filtrar quais produtos serão retornados pelo cmdlet.

A propriedade “Ids” também foi decorada com o atributo “[Parameter(Mandatory = true, HelpMessage =”Ids do produtos.”, Position = 0)]”, que indica ao Windows PowerShell que o parâmetro é obrigatório, define sua posição e uma descrição.

Também sobrescrevemos o método “ProcessRecord”, da class base “Cmdlet”. Esse método é responsável por processar cada item individualmente e enviar ao pipeline do Windows PowerShell por meio do método “WriteObject();”.

Observação: no exemplo, enviamos ao pipeline uma instância da class “Produto.cs”, na qual cada instância é obtida do “RepositorioDeProdutos.cs”. Os detalhes de implementação de “Produto.cs” e “RepositorioDeProdutos.cs” podem ser vistos na solução completa (CS.PS.Tools). O ponto de interesse aqui é que é enviado ao pipeline uma instância de objeto, e não uma string. A exibição dos dados será formatada automaticamente pelo Windows PowerShell em função das propriedades do objeto.

Feito isto, basta compilar o projeto e o cmdlet está pronto para ser utilizado.

Usando o cmdlet

Antes de utilizar o cmdlet, é preciso importar a DLL no Windows PowerShell.

Import-Module .\CS.PS.Tools.dll

Observação: É possível salvar a instrução acima em seu “$profile” para que o módulo seja importado automaticamente sempre que for aberta uma nova instância do Windows PowerShell.

Depois de importar o módulo, é só executar o comando:

Get-Help Get-Produto

para obter uma ajuda de como utilizar o cmdlet.

Alguns exemplos de uso:

Get-Produto -Ids 1
Get-Produto -Ids 4,3

Get-Produto-Ids

Ou simplesmente:

Get-Produto

Get-Produto

Sem nenhum parâmetro. O Windows PowerShell detecta que um parâmetro obrigatório está ausente e solicita logo em seguida.

Conclusão

Com o Windows PowerShell, é extremamente simples criar ferramentas de linha de comando poderosas e simples de utilizar. Alem disso, é possível combinar o uso do seu cmdlet customizado com qualquer outro cmdlet do sistema, como “ConvertTo-Html”, “ConvertTo-Xml” e “ForEach-Object” para criar scripts que atendam as mais variadas necessidades.

Se você quiser saber mais, pode acessar este link com o básico para escrever um módulo em PowerShell em C# e/ou esse guia de introdução do Windows PowerShell.

Ficou alguma dúvida ou tem alguma observação? Aproveite os campos abaixo.

Até a próxima!