Compartilhar via


Geração de código na hora de design usando modelos de texto T4

Modelos de texto T4 em tempo de design permitem gerar código do programa e outros arquivos em seu projeto do Visual Studio. Normalmente, você escreve os modelos para que eles variem o código que geram de acordo com dados de um modelo. Um modelo é um arquivo ou banco de dados que contém informações importantes sobre os requisitos do seu aplicativo.

Por exemplo, você pode ter um modelo que define um fluxo de trabalho, como uma tabela ou um diagrama. A partir do modelo, você pode gerar o software que executa o fluxo de trabalho. Quando os requisitos dos seus usuários mudam, fica fácil discutir o novo fluxo de trabalho com os usuários. Gerar novamente o código do fluxo de trabalho é mais confiável do que atualizar o código manualmente.

Observação

Um modelo é uma fonte de dados que descreve um aspecto particular de um aplicativo. Ela pode estar em qualquer formulário, tipo de arquivo ou banco de dados. Ela não precisa estar em qualquer formulário particular, como um modelo UML ou modelo DSL. Os modelos comuns estão na forma de tabelas ou arquivos XML.

Você provavelmente já está familiarizado com a geração de código. Quando você define recursos em um arquivo .resx na solução Visual Studio, um conjunto de classes e métodos é gerado automaticamente. O arquivo de recursos torna mais fácil e confiável editar os recursos do que se você tivesse que editar as classes e métodos. Com modelos de texto, você pode gerar o código da mesma forma a partir de uma fonte de seu próprio projeto.

Um modelo de texto contém uma mistura do texto que você deseja gerar e o código de programa que gera partes variáveis ​​do texto. O código do programa permite repetir ou omitir condicionalmente partes do texto gerado. O texto gerado pode ser o próprio código de programa que fará parte do seu aplicativo.

Criar um modelo de texto T4 em tempo de design

  1. Crie um novo projeto do Visual Studio ou abra um existente.

  2. Adicione um arquivo de modelo de texto ao seu projeto e nomeie-o com a extensão .tt.

    Para fazer isso, no Gerenciador de Soluções, no menu de atalho do projeto, escolha Adicionar>Novo Item. Na caixa de diálogo Adicionar Novo Item, selecione Modelo de Texto no painel central.

    Observe que a propriedade Ferramenta Personalizada do arquivo é TextTemplatingFileGenerator.

  3. Abra o arquivo. Ele conterá as seguintes diretrizes:

    <#@ template hostspecific="false" language="C#" #>
    <#@ output extension=".txt" #>
    

    Se você adicionou o modelo a um projeto Visual Basic, o atributo language será "VB".

  4. Adicione algum texto no final do arquivo. Por exemplo:

    Hello, world!
    
  5. Salve o arquivo.

    Você pode ver uma caixa de mensagem Aviso de Segurança que pedirá para você confirmar que deseja executar o modelo. Clique em OK.

  6. No Gerenciador de Soluções, expanda o nó do arquivo de modelo e você encontrará um arquivo com a extensão .txt. O arquivo contém o texto gerado a partir do modelo.

    Observação

    Se o seu projeto é um projeto do Visual Basic, Clique em Mostrar todos os arquivos para ver o arquivo de saída.

Regenerar o código

Um modelo será executado, gerando o arquivo subsidiário, em qualquer um dos seguintes casos:

  • Edite o modelo e mude o foco para uma janela do Visual Studio diferente.

  • Salve o modelo.

  • Clique em Transformar Todos os Modelos no menu Compilar. Isso transformará todos os modelos na solução Visual Studio.

  • No Gerenciador de Soluções, no menu de atalho de qualquer arquivo, selecione Executar Ferramenta Personalizada. Use esse método para transformar um subconjunto de modelos selecionado.

Você também pode configurar um projeto Visual Studio para executar os modelos quando os arquivos de dados que eles leem mudarem. Para obter mais informações, consulte Regenerando o código automaticamente.

Gerar texto de variável

Os modelos de texto permitem que você use o código de programa para alterar o conteúdo do arquivo gerado.

  1. Altere o conteúdo do arquivo .tt:

    <#@ template hostspecific="false" language="C#" #>
    <#@ output extension=".txt" #>
    <#int top = 10;
    
    for (int i = 0; i<=top; i++)
    { #>
       The square of <#= i #> is <#= i*i #>
    <# } #>
    
  2. Salve o arquivo .tt e inspecione novamente o arquivo .txt gerado. Ele lista os quadrados dos números de 0 a 10.

    Observe que as instruções são incluídas em <#...#> e expressões individuais em <#=...#>. Para obter mais informações, consulte Gravando um Modelo de Texto T4.

    Se você escrever o código de geração no Visual Basic, a diretiva template deve conter language="VB". "C#" é o padrão.

Depurando um modelo de texto T4 em tempo de design

Para depurar um modelo de texto:

  • Insira debug="true" na diretiva template. Por exemplo:

    <#@ template debug="true" hostspecific="false" language="C#" #>

  • Defina pontos de interrupção no modelo, da mesma maneira que você faria para um código comum.

  • Escolha Depurar Modelo T4 no menu de atalho do arquivo de modelo de texto no Gerenciador de Soluções.

    O modelo é executado e interrompido nos pontos de interrupção. Você pode examinar variáveis ​​e percorrer o código como de costume.

Dica

debug="true" torna o mapa de código gerado mais preciso para o modelo de texto, com a inserção de mais diretivas de numeração de linhas no código gerado. Se você deixa-o de fora, os pontos de interrupção podem parar a execução no estado errado.

Mas, você pode deixar a cláusula na diretiva do modelo mesmo quando você não estiver depurando. Isso causa apensa uma pequena queda de desempenho.

Gerando código ou recursos para sua solução

Você pode gerar arquivos de programas que variam de acordo com o modelo. Um modelo é uma entrada, como um banco de dados, arquivo de configuração, o modelo UML, o modelo DSL ou outra fonte. Você normalmente gera vários arquivos de programas do mesmo modelo. Para isso, você cria um arquivo de modelo para cada arquivo de programa gerado e faz com que todos os modelos leiam o mesmo modelo.

Para gerar o código de programa ou recursos

  1. Altere a diretiva de saída para gerar um arquivo do tipo apropriado, como .cs, .vb, .resx ou .xml.

  2. Insira o código que gerará o código da solução que você precisa. Por exemplo, se você deseja gerar três instruções de campo inteiro em uma classe:

    
    <#@ template debug="false" hostspecific="false" language="C#" #>
    <#@ output extension=".cs" #>
    <# var properties = new string [] {"P1", "P2", "P3"}; #>
    // This is generated code:
    class MyGeneratedClass {
    <# // This code runs in the text template:
      foreach (string propertyName in properties)  { #>
      // Generated code:
      private int <#= propertyName #> = 0;
    <# } #>
    }
    
  3. Salve o arquivo e inspecione o arquivo gerado, que agora contém o seguinte código:

    class MyGeneratedClass {
      private int P1 = 0;
      private int P2 = 0;
      private int P3 = 0;
    }
    

Gerando código e texto gerado

Quando você gera o código de programa, é muito importante evitar confundir o código de geração que executa em seu modelo e o código gerado resultante que se torna parte de sua solução. As duas linguagens não precisam ser a mesma.

O exemplo anterior tem duas versões. Em uma versão, o código de geração está em C#. Em outra versão, o código de geração está em Visual Basic. Mas o texto gerado por ambos é o mesmo e é uma classe C#.

Da mesma forma, você pode usar um modelo do Visual C# para gerar código em qualquer linguagem. O texto gerado não precisa estar em qualquer linguagem em particular e não tem que ser um código de programa.

Estruturando modelos de texto

Por uma questão de práticas recomendadas, nós tendemos a separar o código de modelo em duas partes:

  • A parte de configuração ou de coleta de dados, que define os valores das variáveis, mas não contém os blocos de texto. No exemplo anterior, essa parte é a inicialização das properties.

    Costumamos chamar isso de seção "modelo", porque ela constrói um modelo no repositório, normalmente, lê um arquivo de modelo.

  • A parte de geração de texto (foreach(...){...} no exemplo), que usa os valores das variáveis.

    Essa não é a separação necessária, mas é um estilo que torna mais fácil a leitura do modelo, reduzindo a complexidade da parte que inclui o texto.

Lendo arquivos ou outras fontes

Para acessar um arquivo de modelo ou banco de dados, o código de modelo pode usar conjuntos como o System.XML. Para ter acesso a esses assemblies, você deve inserir diretivas como estas:

<#@ assembly name="System.Xml.dll" #>
<#@ import namespace="System.Xml" #>
<#@ import namespace="System.IO" #>

A diretiva assembly torna o assembly especificado disponível para o código de modelo, da mesma forma como a seção Referências de um projeto do Visual Studio. Você não precisa incluir uma referência em System.dll, ele é referenciado automaticamente. A diretiva import permite que você use os tipos sem usar seus nomes totalmente qualificados, da mesma forma como a diretiva using em um arquivo de programa normal.

Por exemplo, depois de importar o System.IO, você poderia escrever:


<# var properties = File.ReadLines("C:\\propertyList.txt");#>
...
<# foreach (string propertyName in properties) { #>
...

Abrindo um arquivo com um nome de caminho relativo

Para carregar um arquivo de um local referente ao modelo de texto, você pode usar o this.Host.ResolvePath(). Para usar this.Host, você deve definir hostspecific="true" no template:

<#@ template debug="false" hostspecific="true" language="C#" #>

Depois, você pode escrever, por exemplo:

<# string filename = this.Host.ResolvePath("filename.txt");
  string [] properties = File.ReadLines(filename);
#>
...
<#  foreach (string propertyName in properties { #>
...

Você também pode usar o this.Host.TemplateFile, que identifica o nome do arquivo de modelo atual.

O tipo de this.Host (no VB, Me.Host) é Microsoft.VisualStudio.TextTemplating.ITextTemplatingEngineHost.

Obter dados do Visual Studio

Para usar os serviços fornecidos no Visual Studio, defina o atributo hostspecific e carregue o assembly EnvDTE. Importe Microsoft.VisualStudio.TextTemplating, que contém o método de extensão GetCOMService(). Você pode usar ServiceProvider.GetCOMService() para acessar o DTE e outros serviços. Por exemplo:

<#@ template hostspecific="true" language="C#" #>
<#@ output extension=".txt" #>
<#@ assembly name="EnvDTE" #>
<#@ import namespace="Microsoft.VisualStudio.TextTemplating" #>
<#
  IServiceProvider serviceProvider = (IServiceProvider)this.Host;
  EnvDTE.DTE dte = (EnvDTE.DTE) serviceProvider.GetCOMService(typeof(EnvDTE.DTE));
#>

Number of projects in this VS solution:  <#= dte.Solution.Projects.Count #>

Dica

Um modelo de texto é executado em seu próprio domínio de aplicativo e os serviços são acessados ​​por empacotamento. Nesse caso, GetCOMService() é mais confiável que GetService().

Gerando o código automaticamente

Normalmente, vários arquivos em uma solução do Visual Studio são gerados com um modelo de entrada. Cada arquivo é gerado a partir do seu próprio modelo, mas todos os modelos se referem ao mesmo modelo.

Se o modelo de fonte mudar, você deve executar novamente todos os modelos na solução. Para fazer isso manualmente, clique em Transformar Todos os Modelos no menu Build.

Se você tiver o SDK de visualização e modelagem do Visual Studio instalado, você pode transformar todos os modelos automaticamente sempre que você executar uma compilação. Para fazer isso, edite o arquivo do projeto (.csproj ou .vbproj) em um editor de texto e adicione as seguintes linhas perto do final do arquivo, depois de qualquer outra instrução <import>. Em um projeto ao estilo de SDK, ele pode ir a qualquer lugar no arquivo de projeto.

Observação

O SDK de Transformação de Modelo de Texto e o SDK de Modelagem do Visual Studio são instalados automaticamente, quando você instala os recursos específicos do Visual Studio.

<Import Project="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v17.0\TextTemplating\Microsoft.TextTemplating.targets" />
<PropertyGroup>
   <TransformOnBuild>true</TransformOnBuild>
   <!-- Other properties can be inserted here -->
</PropertyGroup>

Para saber mais, confira Geração de código em um processo de build.

Relatório de erros

Para colocar mensagens de erro e de aviso na janela de erro do Visual Studio, você pode usar estes métodos:

Error("An error message");
Warning("A warning message");

Convertendo um arquivo existente para um modelo

Um recurso útil dos modelos é que eles parecem muito com os arquivos que eles geram, além de um código de programa inserido. Isso sugere um método útil para a criação de um modelo. Primeiro crie um arquivo comum, como protótipo, por exemplo, um arquivo do Visual C# e introduza gradualmente o código de geração que varia o arquivo resultante.

Para converter um arquivo existente para um modelo de tempo de design

  1. Para o seu projeto do Visual Studio, adicione um arquivo do tipo que você deseja gerar, como um arquivo .cs, .vb ou .resx.

  2. Teste o novo arquivo para conferir se ele funciona.

  3. No Gerenciador de Soluções, altere a extensão do nome de arquivo para .tt.

  4. Verifique as seguintes propriedades do arquivo .tt:

    Propriedade Configuração
    Ferramenta personalizada = TextTemplatingFileGenerator
    Build Action = Nenhum
  5. Insira as seguintes linhas no início do arquivo:

    <#@ template debug="false" hostspecific="false" language="C#" #>
    <#@ output extension=".cs" #>
    

    Se você quiser escrever o código de geração do seu modelo no Visual Basic, defina o atributo language como "VB" em vez de "C#".

    Defina o atributo extension para a extensão de nome de arquivo para o tipo de arquivo que você deseja gerar, por exemplo .cs, .resx ou .xml.

  6. Salve o arquivo.

    Um arquivo subsidiário é criado com a extensão especificada. As propriedades dele estão corretas para o tipo de arquivo. Por exemplo, a propriedade Ação de Build de um arquivo .cs seria Compilar.

    Verifique se o arquivo gerado contém o mesmo conteúdo que o arquivo original.

  7. Identifique uma parte do arquivo que você deseja variar. Por exemplo, uma parte que aparece apenas sob certas condições, uma parte que se repete ou em que os valores específicos variam. Insira o código de geração. Salve o arquivo e verifique se o arquivo subsidiário é gerado corretamente. Repita esta etapa.

Diretrizes para a geração de código

Consulte Diretrizes para escrever modelos de texto T4.