Compartir a través de


Este artículo proviene de un motor de traducción automática.

Plantillas T4

Disminuir las barreras de la generación de códigos con T4

Peter Vogel

 

Microsoft.NET Framework hace un uso extensivo de generación de código en tiempo de diseño (cuando arrastra un control sobre una superficie de diseño genera código) y en tiempo de ejecución (cuando LINQ genera las instrucciones SQL que recuperar datos). Generación de código obviamente hace desarrolladores más productiva mediante la reducción de la cantidad de código que un desarrollador tiene que escribir, pero puede ser especialmente útil cuando se utiliza más o menos idéntico código en muchas soluciones. Como aplicar este código similar (pero no idéntico) en una nueva aplicación, es fácil introducir nuevos errores.

A pesar de que los desarrolladores pueden aprovechar todas las herramientas de generación de código de la.NET Framework utiliza en la creación de aplicaciones, muy pocos hacen uso extensivo de la generación de código en su práctica cotidiana de desarrollo. Hay una serie de razones para esto: les preocupa que la incorporación de la generación de código requiere un nuevo conjunto de herramientas que no tienen el tiempo para aprender, sino que carecen de experiencia en el reconocimiento de los problemas que la generación de código va a resolver y en el diseño de soluciones que integran el código generado con "escrito a mano "el código, y que entienden que algunas de las soluciones de generación de código puede requerir más tiempo para mantener (o incluso de usar) que serán salvados por la aplicación de la solución.

Microsoft texto plantilla de transformación Toolkit (T4) aborda muchas de estas cuestiones, proporcionando una manera sencilla de implementar soluciones de generación de código que aprovechan las herramientas y desarrolladores de técnicas ya están cómodos con. T4 Para ello, se reconoce que una solución típica involucra dos tipos de código: código repetitivo que no cambia de generación de códigos a otra, y el código dinámico que cambia. T4 simplifica la generación de código, permite a los desarrolladores escribir simplemente la parte de código repetitivo de una solución en un archivo. En T4, el código dinámico (normalmente una pequeña parte de una solución de generación de código) se genera mediante un conjunto de etiquetas que parecen muy similares a las etiquetas de un ASP.NET MVC desarrollador podría utilizar en la creación de una vista, o que una aplicación Web ASP.NET developer utilizaría para incrustar el código del lado del servidor en un archivo .aspx.

Utilizando una solución basada en T4 aprovecha las habilidades ya tienes le permite especificar los insumos para la generación de código en cualquier lenguaje de programación ya está utilizando. T4 no generar soluciones de código neutral (el código generado por una solución de T4 es siempre en algún lenguaje de programación específico), pero la mayoría de los desarrolladores no necesita soluciones de código neutral.

Definir soluciones de generación de código

Mientras que T4 hace más fácil crear soluciones de generación de código, no abordar los problemas que los desarrolladores tienen en reconocer cuándo sería útil una solución de generación de código y en realidad diseñar una solución. Problemas que resuelve de generación de código generalmente comparten tres características:

  1. El código generado entra en un archivo independiente de los devel­código de oper. Esto asegura que el proceso de generación de código no interferirá con el código del desarrollador. Normalmente, esto significa que el código generado entra en una nueva clase que utilizará el desarrollador desde su código "a mano". Podría tener sentido para generar una clase parcial que el desarrollador no sólo puede llamar sino ampliar — pero código del desarrollador y del código generado aún se conservan en archivos separados.
  2. El código generado es repetitivo: El código de la solución es una plantilla que puede ser repetido muchas veces, a menudo con pequeñas variaciones. Esto asegura que la generación de código es más simple y más fácil de mantener que el código equivalente a mano.
  3. En comparación con la solución manuscrita, la solución generada requiere mucho menos insumos (idealmente, no hay entradas en absoluto: la solución de generación de código determina lo que debe hacerse desde el entorno). Si el número de entradas es grande o difícil de determinar, los desarrolladores podrían considerar la solución manuscrita como más sencilla que la solución generada.

Dadas estas características, son tres tipos de escenarios vale la pena investigar para generación de código. Los desarrolladores tienden a centrarse en el primer escenario ("el último escenario"), en el que pocos insumos se utilizan para generar una gran cantidad de código que se utiliza con frecuencia (pensar de Entity Framework, por ejemplo). De hecho, el código más fructífero -­oportunidades de generación de caen en otros dos escenarios.

El segundo escenario es el más obvio: cuando una gran cantidad de código necesario generar. En ese caso, es claramente beneficioso evitar escribir varias líneas de código repetitivo. Mientras más soluciones de generación de código se utilizan en múltiples aplicaciones, soluciones en esta categoría pueden merecer la pena incluso si utiliza en sólo una sola aplicación. En lugar de código de escritura generalizada con si muchas declaraciones para manejar cada situación — cada si duplica la complejidad lógica del código de instrucción — una solución de generación de código puede generar el código específico necesario para cada conjunto de condiciones. En lugar de mano muchas clases que comparten una interfaz de codificación, generación de código puede utilizarse para crear las clases individuales (suponiendo que las clases comparten una estructura común).

Pero el tercer tipo de escenario es el más común: Cuando unas pocas entradas generará un poco de código a ser utilizados en muchas aplicaciones. En este escenario, la cantidad de código repetida en cualquier aplicación particular es pequeña pero la actividad es compatible con el código es tan común que la solución termina generando una gran cantidad de código, pero no en cualquier aplicación de uno.

Por ejemplo, aquí es algo típico del código que ADO.Los desarrolladores netos escribir todo el tiempo:

string conString =
  System.Configuration.ConfigurationManager.
ConnectionStrings["Northwind"].ConnectionString;

Aunque se trata de una cantidad de código trivial, es código que se repite, con sólo el nombre de la cadena de conexión cambiando — en aplicación después de la aplicación. Por otra parte, en la ausencia de compatibilidad con IntelliSense para el nombre de cadena de conexión, el código es propenso a errores: es abierto a la "cuenta de ortografía" los errores que se descubren, sino en tiempo de ejecución (probablemente cuando alguien que tiene la entrada a su evaluación se busca por encima del hombro). Otro ejemplo es implementar INotifyPropertyChanged, que deja abierto el desarrollador "ortografía recuentos" errores en cada propiedad.

Este código para recuperar una cadena de conexión denominada Northwind sería más útil si existía alguna solución de generación de código para crear una clase ConnectionManager para cada cadena de conexión, como este:

string conString = ConnectionManager.Northwind;

Una vez que reconocer una oportunidad para una solución de generación de código, el siguiente paso es escribir un ejemplo del código que podría generar la solución. En este caso, la clase ConnectionManager sería parecido a este:

public partial class ConnectManager
{
  public static string Northwind
    {
      get
        {
          return System.Configuration.ConfigurationManager.
ConnectionStrings["Northwind"].ConnectionString;
        }
    }
}

Este código reúne los criterios para una solución de generación de código: es repetitivo (se repite el código de propiedad de cada cadena de conexión) con sólo pequeños cambios (el nombre de la cadena de conexión y el nombre de la propiedad) y el número de entradas es pequeño (sólo el nombres de las cadenas de conexión).

La primera plantilla de generación de código

Ahora se necesita para crear la segunda parte de la solución: El archivo de entrada que el desarrollador va a utilizar para proveer los insumos para el proceso de generación de código. Ambos archivos son archivos de plantilla de T4 y se crean usando las mismas herramientas de programación que puede utilizar para escribir sus aplicaciones. Este diseño permite separar su plantilla de generación de código del archivo que el programador utiliza para proporcionar las entradas para el proceso.

Para empezar a crear la solución de generación de código, debe agregar el archivo de plantilla T4 que generará el código para una aplicación donde se puede probar, preferiblemente, una aplicación similar a las esperas su solución para ser utilizado. Para agregar el archivo de plantilla T4, en el cuadro de diálogo Agregar nuevo elemento de Visual Studio, se muestra en la figura 1, agregar una plantilla de texto especificando un nombre adecuado para la plantilla de generación de código (por ejemplo, ConnectionManagerGenerator). Si la versión de Visual Studio no tiene la opción de plantilla de texto, agregar un nuevo archivo de texto (también se encuentra en la sección General), dando el archivo la extensión ".tt" procesamiento de desencadenador T4. Si agrega un archivo de texto que se obtendrá un mensaje de advertencia que puede ignorar.

Adding a T4 Template
Figura 1 agregar una plantilla de T4

Si examina las propiedades para el nuevo archivo de plantilla, encontrará su herramienta personalizada propiedad se ha establecido en TextTemplatingFileGenerator. Esta herramienta personalizada se ejecuta automáticamente por Visual Studio y el host que administra el proceso de generación de código. En T4, el contenido del archivo de plantilla se pasa al host de generación de código, que pone el código generado resultante en la plantilla del niño anidados archivo.

Si ha agregado un archivo de texto a su proyecto, el archivo de plantilla estará vacío; Si se puede agregar un archivo de plantilla, contendrá dos directivas de T4, marcadas con < # @... # > delimitadores (si ha agregado un archivo de texto, deberás agregar estas directivas). Estas directivas especifican el idioma que la plantilla se escribirán en (no en el idioma para el código generado) y la extensión del archivo de niño. En este ejemplo, las dos directivas establecen el lenguaje de programación para la plantilla de Visual Basic y la extensión de archivo para el archivo de niño que contiene el código generado a. generated.cs:

<#@ template language="VB" #>
<#@ output extension=".generated.cs" #>

Para crear la aplicación "Hello, World" tradicional, sólo agregue el código en el archivo de plantilla (Observe que, si bien el idioma de en que la plantilla está siendo escrita Visual Basic, la plantilla está generando código de C#):

public class HelloWorld
{
  public static string HelloWorld(string value)
  {
    return "Hello, " + value;
  }
}

Este ejemplo utiliza sólo el código repetitivo. En T4, repetitivo es copiado directamente de la plantilla en el archivo de código. En Visual Studio 2010, debería ocurrir al cambiar el archivo de plantilla o guardarlo. También puede activar la generación de código haciendo clic en el archivo de plantilla en el explorador de soluciones y seleccionando Ejecutar herramienta personalizada en el menú contextual o haciendo clic en el botón Transformar todas las plantillas en la parte superior del explorador de soluciones.

Después de la activación de generación, si abre el archivo de código de la plantilla (que ahora tendrá la extensión especificada en la Directiva de salida de la plantilla), encontrará que contiene el código especificado en la plantilla. Visual Studio también habrá hecho una compilación de antecedentes del nuevo código, por lo que encontrará que usted puede utilizar el código generado desde el resto de la aplicación.

Generar código

Código repetitivo no es suficiente, sin embargo. La solución ConnectionManager debe generar dinámicamente una propiedad para cada cadena de conexión que requiere de la aplicación. Para generar el código, debe agregar código de control para administrar el proceso de generación de código y algunas variables que celebrarán las entradas desde el desarrollador mediante la solución de generación de código.

El ConnectionManager utiliza un objeto ArrayList (que he llamado conexiones) del espacio de nombres System.Collections para mantener la lista de cadenas de conexión que forman la entrada para el proceso de generación de código. Para importar ese espacio de nombres para su uso por el código dentro de su plantilla, utiliza la Directiva de T4 de importación:

<#@ Import Namespace="System.Collections" #>

Ahora puede agregar cualquier código estático que comienza su clase generada. Porque soy generación de código de C#, el código inicial para el ConnectionManager este aspecto:

public partial class ConnectionManager
{

Ahora debo añadir el código de control que generará dinámicamente el código de salida. Código que controla la generación (código que se ejecuta, en lugar de copiar el archivo de niño) debe escribirse el < # … # > delimitadores. En este ejemplo, para hacer más fácil distinguir entre el código de control y el código que se genera, he escrito el código de control en Visual Basic (no es un requisito del proceso de generación de código). El código de control para la solución de ConnectionManager recorre la colección de conexiones para cada cadena de conexión:

<#
  For Each conName As String in Connections
#>

Además de cualquier código de control de la plantilla, también deberás incluir las expresiones cuyos valores deben incorporarse en el código generado dinámicamente. En la solución de ConnectionManager, el nombre de la cadena de conexión tiene que ser incorporados en la declaración de la propiedad y en el parámetro que se pasa a la colección de ConnectionStrings. Para evaluar una expresión y que su valor insertado en el código repetitivo, la expresión debe escribirse en la < # = … # > delimitadores. Este ejemplo inserta dinámicamente el valor de la variable conName en dos lugares en el código estático dentro del para cada bucle:

public static string <#= conName #>
{
  get
  {
    return System.Configuration.ConfigurationManager.
ConnectionStrings["<#= conName #>"].ConnectionString;
  }
}
<#
  Next
#>
}

Lo único que queda es definir el ArrayList que contiene la lista de conexión nombres de cadena. Para ello, usaré una característica de clase T4. Funciones de la clase de T4 se suelen utilizar para definir funciones auxiliares pero también pueden utilizarse para definir campos o cualquier otro elemento de nivel de clase que se utilizará durante el proceso de generación de código. Funciones de clase deben aparecer al final de una plantilla, como éste:

<#+
  Dim Connections As New ArrayList()
#>

Esta plantilla T4 constituye la primera parte de la solución ConnectionManager — la plantilla de generación de código. You now need to create the second part of the solution: The input file that the developer will use to provide the inputs to the code-generation process.

Usando el paquete de generación de código

Para proporcionar un lugar para el desarrollador entrar en los insumos para el proceso de generación de código, agregue una segunda plantilla T4 a la aplicación en la que está probando su solución de generación de código. Esta plantilla debe tener una directiva Include que copia la plantilla de generación de código en esta plantilla. Ya he nombrado a mi archivo de plantilla de generación de código ConnectionManagerGenerator, el archivo de plantilla de entrada para la solución de ConnectionManager este aspecto:

<#@ template language="VB" #>
<#@ output extension=".generated.cs" #>
<#@ Import Namespace="System.Collections" #>
<#
#>
<#@ Include file="ConnectionManagerGenerator.tt" #>

Cuando se realiza la generación de código, el proceso de host realmente ensambla a un intermediario.Programa NET de las directivas, código de control y código estático especificado en sus plantillas y, a continuación, ejecuta el programa resultante. Es el resultado de ese programa intermediario que se vierte en el archivo de la plantilla niño. El resultado de utilizar la directiva Include es fusionar su plantilla de generación de código (con su declaración de ArrayList conexiones) con el contenido de este archivo para crear ese programa intermediario. Todo el desarrollador utilizando la solución tiene que hacer es agregar el código a esta plantilla que establecerá las variables utilizadas por la plantilla de generación de código. Este proceso permite a los desarrolladores especificar los insumos para la generación de código mediante el lenguaje de programación que están acostumbrados a.

Para la solución de ConnectionManager, el desarrollador debe agregar el nombre de las cadenas de conexión en app.config de la aplicación o archivo web.config para conexiones ArrayList. Porque estos valores son parte del código de control que necesita para ser ejecutado, ese código debe ser encerrado la < # … # > delimitadores. Código del desarrollador también debe preceder la directiva Include para que las variables se establecen antes de que su código se ejecuta.

Para generar un ConnectionManager dos cadenas de conexión llamada Northwind y PHVIS, el desarrollador agregar este código a la plantilla de entrada antes de la directiva Include:

<#
  Me.connections.Add("Northwind")
  Me.connections.Add("PHVIS")
#>
<#@ Include file="ConnectionManagerGenerator.tt" #>

Ahora tiene un paquete de generación de código que consiste en el archivo de plantilla de generación de código y el archivo de plantilla de entrada. Los desarrolladores que utilizan la solución deben copiar ambos archivos en su aplicación, definir las variables en el archivo de entrada y cerrar o guardar el archivo de entrada para generar el código de la solución. Un desarrollador de generación de código emprendedora podría configurar el paquete de generación de código como una plantilla de elemento de Visual Studio que incluye tanto archivos de plantilla. Mientras no proceda a la solución ConnectionManager, si un desarrollador debe generar otro conjunto de código basado en diferentes entradas, sólo necesitaba hacer una segunda copia del archivo de entrada para celebrar el segundo conjunto de entradas.

Hay una arruga en la estructura de esta solución: Cualquier aplicación que utiliza esta solución tendrá tanto la plantilla de entrada y la plantilla de generación de código. En la solución de ConnectionManager, si ambas plantillas generan código que Visual Studio compila, los dos archivos de código resultante serán tanto definen una clase llamada Connection Manager y no compila la aplicación. Hay varias maneras de evitar esto, pero la forma más sencilla es modificar la plantilla de generación de código para que su archivo de código generado tiene una extensión que Visual Studio no reconocen. Cambiar la Directiva de salida en el archivo de plantilla de generación de código hace el truco:

<#@ output extension=".ttinclude" #>

Recursos y herramientas de generación de código

Además de las páginas de MSDN Library, su mejor fuente de información sobre el uso de T4 es blog de Oleg Sych en olegsych.com— ciertamente he llegado a depender de sus ideas (y herramientas) en el desarrollo de mis propias soluciones de generación de código. Su caja de herramientas de T4 incluye varias plantillas para desarrollar soluciones de T4 (incluyendo una plantilla para generar archivos de salida múltiples desde una única plantilla de T4 y otras herramientas para administrar el proceso de generación de código). Kit de herramientas de Sych también incluye paquetes para varios escenarios de generación de código.

Visual Studio trata esencialmente T4 plantillas como archivos de texto, lo que significa no ser compatibilidad con IntelliSense o resaltado o, realmente, nada que los desarrolladores esperan desde un editor. En Visual Studio Extension Manager, encontrará varias herramientas que mejorarán su desarrollo T4 experimentan. Ambos T4 Visual de Clarius Consulting (bit.ly/maZFLm) y Editor de T4 de Devart (bit.ly/wEVEVa) le dará muchas de las características que toma por sentado en un editor. Como alternativa, puede obtener el Editor T4 (ya sea el libre o PRO EDITION) de ingeniería tangibles (véase figura 2) en bit.ly/16jvGY, que incluye un diseñador visual puede utilizar para crear paquetes de generación de código de lenguaje de modelado unificado (UML)-como diagramas.

The Default Editor for T4 Template Files (Left) Isn’t Much Better than NotePad—Adding the Tangible Editor (Right) Gives You the Kind of Features You Expect in Visual Studio
Figura 2 el Editor predeterminado para los archivos de plantilla de T4 (izquierda) no es mucho mejor que el Bloc de notas, añadiendo el Editor Tangible (derecha) le ofrece el tipo de características que se esperan en Visual Studio

Como con cualquier otra solución basada en el código, es poco probable que tu solución funcionará la primera vez. Compilar errores en su control de plantilla código figuran en la lista de errores después de seleccionar Ejecutar herramienta personalizada del menú contextual de un archivo plantilla. Sin embargo, incluso si se compila el código de control, podría encontrar que el archivo la plantilla infantil está vacío salvo por la palabra ErrorGeneratingOutput. Esto indica que el código de control en el paquete de generación de código genera un error cuando se ejecuta. A menos que su error es evidente, te vas a necesita depurar ese código de control.

Para depurar su paquete de generación, primero debe establecer el atributo debug en la Directiva de plantilla en True, como este:

<#@ template language="VB" debug="True"#>

Ahora puede establecer un punto de interrupción en el código de control y tiene Visual Studio respetarlo. Entonces, la manera más confiable para depurar su aplicación es iniciar una segunda versión de Visual Studio y, en el menú Depurar, seleccione asociar al proceso. En el cuadro de diálogo resultante, seleccione otra copia la ejecución de devenv.exe y haga clic en el botón Adjuntar. Ahora puede volver a la copia original de Visual Studio y utilizar Ejecutar herramienta personalizada menú contextual del archivo de entrada para comenzar a ejecutar el código.

Si ese proceso no funciona, puede activar depuración insertando esta línea en el código de control de la plantilla:

System.Diagnostics.Debugger.Break()

Con Visual Studio 2010 en Windows 7, debe agregar esta línea antes de la llamada al método de interrupción:

System.Diagnostics.Debugger.Launch()

Cuando se ejecuta el código de la plantilla, esta línea aparece un cuadro de diálogo que le permite reiniciar Visual Studio o depurarlo. Si selecciona la opción de depuración se iniciará una segunda copia de Visual Studio ya conectada al proceso que se ejecuta el código de plantilla. Mientras se está depurando el código, se deshabilitará la instancia inicial de Visual Studio. Lamentablemente, cuando la sesión de depuración, Visual Studio permanecerá en ese modo deshabilitado. Para evitar esto, tendrá que modificar una de las opciones de Visual Studio en el registro de Windows (ver post de Sych sobre la depuración de T4 en bit.ly/aXJwPx para obtener detalles). También deberás recordar eliminar esta declaración una vez que haya fijado su problema.

Por supuesto, esta solución cuenta todavía con el desarrollador escribir correctamente los nombres de las cadenas de conexión en el archivo de entrada. Una mejor solución tendría ConnectionManager incluir código que lee las cadenas de conexión del archivo de configuración de la aplicación, eliminando la necesidad para el desarrollador entrar en las entradas. Lamentablemente, porque se genera código en tiempo de diseño en lugar de en tiempo de ejecución, usted no puede utilizar el ConfigurationManager para leer el archivo de config y será necesario utilizar las clases de System.XML para procesar el archivo config. También deberás agregar una directiva de la Asamblea para recoger esas clases, como lo hice anteriormente para obtener ArrayList de System.Collections. También puede agregar referencias a sus propias bibliotecas personalizadas por establecer atributo de nombre de la Directiva de la Asamblea en la ruta completa al archivo DLL:

<#@ assembly name="C:\PHVIS\GenerationUtilities.dll" #>

Estos son los elementos esenciales para agregar la generación de código a su kit de herramientas y aumentar la productividad, junto con la calidad y la fiabilidad de su código. T4 facilita el comenzar por lo que le permite crear soluciones de generación de código mediante un conjunto de herramientas familiar. El mayor problema a enfrentar en el uso de T4 es aprender a reconocer oportunidades para aplicar estas herramientas.

Peter Vogel  es un principal en PH & v servicios de información. Su último libro fue "generación de código práctico en."NET" (Addison-Wesley Professional, 2010). PH & V Information Services se especializa en facilitar el diseño de arquitecturas basadas en servicios y en la integración.Tecnologías de redes en las arquitecturas. Además de su práctica de consultoría, Vogel escribió curso de diseño SOA Learning Tree International, que se imparte en todo el mundo.

Gracias al siguiente experto técnico por su ayuda en la revisión de este artículo: Gareth Jones