Passo a passo: criar um host de modelo de texto personalizado
Um host de modelo de texto fornece um ambiente que permite a execução do mecanismo de transformação de modelo de texto. O host é responsável por gerenciar a interação do mecanismo com o sistema de arquivos. O mecanismo ou processador de diretriz que precisar de um arquivo ou assembly pode solicitar um recurso do host. O host pode pesquisar em diretórios e no cache de assembly global para localizar o recurso solicitado. Para obter mais informações, confira O processo de transformação do modelo de texto.
Você pode escrever um host personalizado se quiser usar a funcionalidade de transformação de modelo de texto fora do Visual Studio ou se quiser integrar essa funcionalidade a ferramentas personalizadas. Para criar um host personalizado, é preciso criar uma classe herdada de ITextTemplatingEngineHost. Para obter a documentação dos métodos individuais, confira ITextTemplatingEngineHost.
Aviso
Se você estiver escrevendo uma extensão ou um pacote do Visual Studio, cogite usar o serviço de modelagem de texto em vez de criar seu próprio host. Para obter mais informações, confira Invocar a transformação de texto em uma extensão do VS.
Este passo a passo ilustra as seguintes tarefas:
Criar um host de modelo de texto personalizado.
Testar o host personalizado.
Pré-requisitos
Para concluir esta explicação passo a passo, você precisará do seguinte:
Visual Studio
SDK do Visual Studio
Criar um host de modelo de texto personalizado
Neste passo a passo, você cria um host personalizado em um aplicativo executável que pode ser chamado da linha de comando. O aplicativo aceita um arquivo de modelo de texto como argumento, lê o modelo, chama o mecanismo para transformar o modelo e exibe todos os erros que ocorrem na janela do prompt de comando.
No Visual Studio, crie um novo aplicativo de console Visual Basic ou C# chamado CustomHost.
Adicione referências aos assemblies a seguir:
Microsoft.VisualStudio.TextTemplating.*.0
Microsoft.VisualStudio.TextTemplating.Interfaces.10.0 e versões posteriores
Substitua o código no arquivo Program.cs ou Module1.vb pelo seguinte código:
using System; using System.IO; using System.CodeDom.Compiler; using System.Collections.Generic; using System.Text; using Microsoft.VisualStudio.TextTemplating; namespace CustomHost { //The text template transformation engine is responsible for running //the transformation process. //The host is responsible for all input and output, locating files, //and anything else related to the external environment. //------------------------------------------------------------------------- class CustomCmdLineHost : ITextTemplatingEngineHost { //the path and file name of the text template that is being processed //--------------------------------------------------------------------- internal string TemplateFileValue; public string TemplateFile { get { return TemplateFileValue; } } //This will be the extension of the generated text output file. //The host can provide a default by setting the value of the field here. //The engine can change this value based on the optional output directive //if the user specifies it in the text template. //--------------------------------------------------------------------- private string fileExtensionValue = ".txt"; public string FileExtension { get { return fileExtensionValue; } } //This will be the encoding of the generated text output file. //The host can provide a default by setting the value of the field here. //The engine can change this value based on the optional output directive //if the user specifies it in the text template. //--------------------------------------------------------------------- private Encoding fileEncodingValue = Encoding.UTF8; public Encoding FileEncoding { get { return fileEncodingValue; } } //These are the errors that occur when the engine processes a template. //The engine passes the errors to the host when it is done processing, //and the host can decide how to display them. For example, the host //can display the errors in the UI or write them to a file. //--------------------------------------------------------------------- private CompilerErrorCollection errorsValue; public CompilerErrorCollection Errors { get { return errorsValue; } } //The host can provide standard assembly references. //The engine will use these references when compiling and //executing the generated transformation class. //-------------------------------------------------------------- public IList<string> StandardAssemblyReferences { get { return new string[] { //If this host searches standard paths and the GAC, //we can specify the assembly name like this. //--------------------------------------------------------- //"System" //Because this host only resolves assemblies from the //fully qualified path and name of the assembly, //this is a quick way to get the code to give us the //fully qualified path and name of the System assembly. //--------------------------------------------------------- typeof(System.Uri).Assembly.Location }; } } //The host can provide standard imports or using statements. //The engine will add these statements to the generated //transformation class. //-------------------------------------------------------------- public IList<string> StandardImports { get { return new string[] { "System" }; } } //The engine calls this method based on the optional include directive //if the user has specified it in the text template. //This method can be called 0, 1, or more times. //--------------------------------------------------------------------- //The included text is returned in the context parameter. //If the host searches the registry for the location of include files, //or if the host searches multiple locations by default, the host can //return the final path of the include file in the location parameter. //--------------------------------------------------------------------- public bool LoadIncludeText(string requestFileName, out string content, out string location) { content = System.String.Empty; location = System.String.Empty; //If the argument is the fully qualified path of an existing file, //then we are done. //---------------------------------------------------------------- if (File.Exists(requestFileName)) { content = File.ReadAllText(requestFileName); return true; } //This can be customized to search specific paths for the file. //This can be customized to accept paths to search as command line //arguments. //---------------------------------------------------------------- else { return false; } } //Called by the Engine to enquire about //the processing options you require. //If you recognize that option, return an //appropriate value. //Otherwise, pass back NULL. //-------------------------------------------------------------------- public object GetHostOption(string optionName) { object returnObject; switch (optionName) { case "CacheAssemblies": returnObject = true; break; default: returnObject = null; break; } return returnObject; } //The engine calls this method to resolve assembly references used in //the generated transformation class project and for the optional //assembly directive if the user has specified it in the text template. //This method can be called 0, 1, or more times. //--------------------------------------------------------------------- public string ResolveAssemblyReference(string assemblyReference) { //If the argument is the fully qualified path of an existing file, //then we are done. (This does not do any work.) //---------------------------------------------------------------- if (File.Exists(assemblyReference)) { return assemblyReference; } //Maybe the assembly is in the same folder as the text template that //called the directive. //---------------------------------------------------------------- string candidate = Path.Combine(Path.GetDirectoryName(this.TemplateFile), assemblyReference); if (File.Exists(candidate)) { return candidate; } //This can be customized to search specific paths for the file //or to search the GAC. //---------------------------------------------------------------- //This can be customized to accept paths to search as command line //arguments. //---------------------------------------------------------------- //If we cannot do better, return the original file name. return ""; } //The engine calls this method based on the directives the user has //specified in the text template. //This method can be called 0, 1, or more times. //--------------------------------------------------------------------- public Type ResolveDirectiveProcessor(string processorName) { //This host will not resolve any specific processors. //Check the processor name, and if it is the name of a processor the //host wants to support, return the type of the processor. //--------------------------------------------------------------------- if (string.Compare(processorName, "XYZ", StringComparison.OrdinalIgnoreCase) == 0) { //return typeof(); } //This can be customized to search specific paths for the file //or to search the GAC //If the directive processor cannot be found, throw an error. throw new Exception("Directive Processor not found"); } //A directive processor can call this method if a file name does not //have a path. //The host can attempt to provide path information by searching //specific paths for the file and returning the file and path if found. //This method can be called 0, 1, or more times. //--------------------------------------------------------------------- public string ResolvePath(string fileName) { if (fileName == null) { throw new ArgumentNullException("the file name cannot be null"); } //If the argument is the fully qualified path of an existing file, //then we are done //---------------------------------------------------------------- if (File.Exists(fileName)) { return fileName; } //Maybe the file is in the same folder as the text template that //called the directive. //---------------------------------------------------------------- string candidate = Path.Combine(Path.GetDirectoryName(this.TemplateFile), fileName); if (File.Exists(candidate)) { return candidate; } //Look more places. //---------------------------------------------------------------- //More code can go here... //If we cannot do better, return the original file name. return fileName; } //If a call to a directive in a text template does not provide a value //for a required parameter, the directive processor can try to get it //from the host by calling this method. //This method can be called 0, 1, or more times. //--------------------------------------------------------------------- public string ResolveParameterValue(string directiveId, string processorName, string parameterName) { if (directiveId == null) { throw new ArgumentNullException("the directiveId cannot be null"); } if (processorName == null) { throw new ArgumentNullException("the processorName cannot be null"); } if (parameterName == null) { throw new ArgumentNullException("the parameterName cannot be null"); } //Code to provide "hard-coded" parameter values goes here. //This code depends on the directive processors this host will interact with. //If we cannot do better, return the empty string. return String.Empty; } //The engine calls this method to change the extension of the //generated text output file based on the optional output directive //if the user specifies it in the text template. //--------------------------------------------------------------------- public void SetFileExtension(string extension) { //The parameter extension has a '.' in front of it already. //-------------------------------------------------------- fileExtensionValue = extension; } //The engine calls this method to change the encoding of the //generated text output file based on the optional output directive //if the user specifies it in the text template. //---------------------------------------------------------------------- public void SetOutputEncoding(System.Text.Encoding encoding, bool fromOutputDirective) { fileEncodingValue = encoding; } //The engine calls this method when it is done processing a text //template to pass any errors that occurred to the host. //The host can decide how to display them. //--------------------------------------------------------------------- public void LogErrors(CompilerErrorCollection errors) { errorsValue = errors; } //This is the application domain that is used to compile and run //the generated transformation class to create the generated text output. //---------------------------------------------------------------------- public AppDomain ProvideTemplatingAppDomain(string content) { //This host will provide a new application domain each time the //engine processes a text template. //------------------------------------------------------------- return AppDomain.CreateDomain("Generation App Domain"); //This could be changed to return the current appdomain, but new //assemblies are loaded into this AppDomain on a regular basis. //If the AppDomain lasts too long, it will grow indefinitely, //which might be regarded as a leak. //This could be customized to cache the application domain for //a certain number of text template generations (for example, 10). //This could be customized based on the contents of the text //template, which are provided as a parameter for that purpose. } } //This will accept the path of a text template as an argument. //It will create an instance of the custom host and an instance of the //text templating transformation engine, and will transform the //template to create the generated text output file. //------------------------------------------------------------------------- class Program { static void Main(string[] args) { try { ProcessTemplate(args); } catch (Exception ex) { Console.WriteLine(ex.Message); } } static void ProcessTemplate(string[] args) { string templateFileName = null; if (args.Length == 0) { throw new System.Exception("you must provide a text template file path"); } templateFileName = args[0]; if (templateFileName == null) { throw new ArgumentNullException("the file name cannot be null"); } if (!File.Exists(templateFileName)) { throw new FileNotFoundException("the file cannot be found"); } CustomCmdLineHost host = new CustomCmdLineHost(); Engine engine = new Engine(); host.TemplateFileValue = templateFileName; //Read the text template. string input = File.ReadAllText(templateFileName); //Transform the text template. string output = engine.ProcessTemplate(input, host); string outputFileName = Path.GetFileNameWithoutExtension(templateFileName); outputFileName = Path.Combine(Path.GetDirectoryName(templateFileName), outputFileName); outputFileName = outputFileName + "1" + host.FileExtension; File.WriteAllText(outputFileName, output, host.FileEncoding); foreach (CompilerError error in host.Errors) { Console.WriteLine(error.ToString()); } } } }
Somente no Visual Basic, abra o menu Projeto e clique em Propriedades do CustomHost. Na lista Objeto de inicialização, clique em CustomHost.Program.
No menu Arquivo , clique em Salvar Tudo.
No menu Compilar, clique em Compilar Solução.
Para testar o host personalizado
Para testar o host personalizado, você escreve um modelo de texto, executa o host personalizado, passa o nome do modelo de texto e verifica se o modelo é transformado.
Para criar um modelo de texto para testar o host personalizado
Crie um arquivo de texto e nomeie-o como
TestTemplate.tt
.Você pode usar qualquer editor de texto (por exemplo, o Bloco de Notas) para criar o arquivo.
Adicione o seguinte ao arquivo:
Observação
A linguagem de programação do modelo de texto não precisa corresponder à linguagem do host personalizado.
Text Template Host Test <#@ template debug="true" #> <# //Uncomment this line to test that the host allows the engine to set the extension. #> <# //@ output extension=".htm" #> <# //Uncomment this line if you want to debug the generated transformation class. #> <# //System.Diagnostics.Debugger.Break(); #> <# for (int i=0; i<3; i++) { WriteLine("This is a test"); } #>
Salve e feche o arquivo.
Para testar o host personalizado
Abra a janela do prompt de comando.
Digite o caminho do arquivo executável para o host personalizado, mas não pressione ENTER ainda.
Por exemplo, digite:
<YOUR PATH>CustomHost\bin\Debug\CustomHost.exe
Observação
Em vez de digitar o endereço, procure o arquivo CustomHost.exe no Windows Explorer, depois arraste o arquivo para a janela do Prompt de Comando.
Digite um espaço.
Digite o caminho do arquivo de modelo de texto e pressione ENTER.
Por exemplo, digite:
C:\<YOUR PATH>TestTemplate.tt
Observação
Em vez de digitar o endereço, procure o arquivo TestTemplate.tt no Windows Explorer, depois arraste o arquivo para a janela Prompt de Comando.
O aplicativo host personalizado é executado e conclui o processo de transformação do modelo de texto.
No Windows Explorer, navegue até a pasta que contém o arquivo TestTemplate.tt.
Essa pasta também contém o arquivo TestTemplate1.txt.
Abra esse arquivo para ver os resultados da transformação do modelo de texto.
A saída de texto gerada aparecerá e será semelhante a esta:
Text Template Host Test This is a test This is a test This is a test
Próximas etapas
Neste passo a passo, você criou um host de transformação de modelo de texto que oferece suporte à funcionalidade de transformação básica. Você pode expandir o host para oferecer suporte a modelos de texto que chamam processadores de diretriz personalizados ou gerados. Para obter mais informações, confira Passo a passo: conectar um host a um processador de diretiva gerada.