terça-feira, 11 de fevereiro de 2014

Getting Project's Types in T4

Me propus a criar um T4 que criasse os testes automaticamente, a medida que os AppService's do projeto fossem criados. Mas, para minha surpresa, não dá pra fazer Assembly.Load("...") no Text Template!. Consegui fazer o t4 consultando as classes a partir do EnvDTE (que não tem relação alguma com o Reflection

Utilizando um Class Feature Block, escrevi o seguinte trecho de código.



<#+
 protected class DetalhesDaClasse {
  public string Name { get; set; }
  public string FullName { get; set; }
 }


 /// <summary>
 ///    Lista todas as classes codificadas na solução atual 
 /// e que terminam com o sufixo informado. 
 /// </summary>
 protected IEnumerable<detalhesdaclasse> ListarClassesDaSolucaoQueTerminamCom(string sufixo) {
  
  return from classe in ListarClassesReferenciadas()
         where classe.Name.EndsWith(sufixo) 
                             && classe.FullName.StartsWith("Vvs", StringComparison.InvariantCultureIgnoreCase)
                       select classe;
 
 }

 #region ' DTE Specific Methods '


 /// <summary>
 ///    Lista todas as classes referenciadas no projeto atual 
        ///   (Exceto as abstratas e as genéricas.)
 /// </summary>
 protected IEnumerable<detalhesdaclasse> ListarClassesReferenciadas() {
  
  IServiceProvider _ServiceProvider = (IServiceProvider)Host;
  if (_ServiceProvider == null) throw new Exception("Host property returned unexpected value (null)");

  EnvDTE.DTE dte = (EnvDTE.DTE)_ServiceProvider.GetService(typeof(EnvDTE.DTE));
  if (dte == null) throw new Exception("Unable to retrieve EnvDTE.DTE");

  // Retorna os projetos ativos.
  // Obs.: Considera-se no escopo do T4 somente o projeto de teste como projeto ativo!
  var activeSolutionProjects = Enumerable.Cast<envdte .project="">((Array) dte.ActiveSolutionProjects);
  if (!activeSolutionProjects.Any()) throw new Exception("DTE.ActiveSolutionProjects returned null");

  return from EnvDTE.Project proj in activeSolutionProjects
      from EnvDTE.CodeElement namespaceElement in proj.CodeModel.CodeElements
      from classe in listarSubClasses(namespaceElement)
      where !classe.FullName.Contains("<") && !((EnvDTE.CodeClass)classe).IsAbstract
      orderby classe.Name
      select new DetalhesDaClasse() { Name = classe.Name, FullName = classe.FullName };
      

 }

 ///<summary>
 ///    Retorna todas as classes contidas em um Code Element 
        ///    (e.g. No namespace root 'Vvs')
 ///</summary>
 protected IEnumerable<envdte .codeelement=""> listarSubClasses(EnvDTE.CodeElement codeElement) {
  
  // se o CodeElement atual for uma classe, retorna-o.
  if (codeElement.Kind == EnvDTE.vsCMElement.vsCMElementClass)
   yield return codeElement;

  // Verifica se o item atual tem filhos.
  EnvDTE.CodeElements filhos = null;
  if (codeElement.Kind == EnvDTE.vsCMElement.vsCMElementNamespace)
                    filhos = ((EnvDTE.CodeNamespace)codeElement).Members; 

  
  // Se houver filhos, retorna as subclasses dos filhos.
  if (filhos != null) foreach(EnvDTE.CodeElement filho in filhos) {
                    foreach (var neto in listarSubClasses(filho)) yield return neto;
  }

 }

 #endregion

#>


Na parte básica do T4, seguiu o seguinte conteúdo:
<#@ template debug="true" hostspecific="true" language="C#" #>
<#@ assembly name="EnvDTE" #>
<#@ assembly name="System.Core" #>
<#@ import namespace="System" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ import namespace="System.Reflection" #>
<#@ output extension=".cs" #>
<#

 var classesASeremTestadas = ListarClassesDaSolucaoQueTerminamCom("AppService");

#>
//------------------------------------------------------------------------------
// <auto-generated>
//     This code was generated by a tool.

//     Changes to this file may cause incorrect behavior and will be lost if
//     the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
using System;

namespace Vvs.CPlusAnywhere.Aplicacao.Tests.Testes
{
    [TestClass]
    public partial class CrudBasicoAppServiceTests
    {

<# foreach (var classe in classesASeremTestadas) { #>
  
        [TestMethod]
        public void CrudBasico_<#= classe.Name #>()
        {
            TestarAppService(typeof(<#= classe.FullName #>));
        }

<# } #>

        protected void TestarAppService(Type appServiceType)
        {
            throw new NotImplementedException();
        }
    }
}

Nenhum comentário: