iterate over a class for properties
Asked Answered
H

3

6

I'm trying to write a T4 template to iterate over a project folder (specified) and generate a js file based on those properties.

I'm able to return my first class file as a ProjectItem (returns as a System.__ComObject)

i see name is returning correctly ("MyReadModel.cs")

Public Class MyReadModel{
  Public string MyName { get; set; }
  public int MyAge { get; set;}
}

now I'm struggling to return the properties out of it. the file has a FileCodeModel as System.__ComObject. i can't find any properties.

i tried doing the following:

projectItem.GetType().GetProperties()

but returns System.Reflection.PropertyInfo[0]

any tips on where I'm going wrong? it appears its being cast as a com object... maybe this is wrong?

EDIT:

references:

http://www.olegsych.com/2008/07/t4-template-for-generating-sql-view-from-csharp-enumeration/

How to get T4 in VS2010 to iterate over class' properties

Code:

<# Prepare(this); #>
<# foreach(ProjectItem pi in FindProjectItemsIn(CurrentProject.ProjectItems.Item("Commands"))) { #>
    <# WriteLine("found " + pi); #>
<# } #>

<#+    
static DTE Dte;
static Dictionary<string, ResultTypeInfo> ResultTypes;
static TextTransformation TT;
static Microsoft.CSharp.CSharpCodeProvider codeProvider = new Microsoft.CSharp.CSharpCodeProvider();

static Project CurrentProject;

IList<ProjectItem> FindProjectItemsIn(ProjectItem start) {
var list = new List<ProjectItem>();
FindProjectItemsIn(start, list);
return list;
}

static bool IsFolder1(ProjectItem item) {
    return (item.Kind == Constants.vsProjectItemKindPhysicalFolder);
}

void FindProjectItemsIn(ProjectItem start, IList<ProjectItem> list) {
foreach(ProjectItem item in start.ProjectItems) {
if(IsFolder1(item)) {
FindProjectItemsIn(item, list);
continue;
}
list.Add(item);
}
}


void Prepare(TextTransformation tt) {
TT = tt;
    // Get the DTE service from the host
    var serviceProvider = Host as IServiceProvider;
    if (serviceProvider != null) {
        Dte = serviceProvider.GetService(typeof(SDTE)) as DTE;
    }

var projectItem = Dte.Solution.FindProjectItem(Host.TemplateFile);
CurrentProject = projectItem.ContainingProject;
}
Hustler answered 15/3, 2012 at 10:46 Comment(1)
Please don't prefix your titles with "t4" and such. That's what the tags are for.Evert
T
4

In order to get the list of properties in the classes/structs contained in a given source file you can do the following (you could easily get the class name etc. by using CodeClass):

private IList<CodeProperty> GetProperties(string csFile)
{
  ProjectItem projectItem = TransformationContext.FindProjectItem(csFile);
  FileCodeModel codeModel = projectItem.FileCodeModel;
  var propertyList = new List<CodeProperty>();
  FindProperties(codeModel.CodeElements, propertyList);
  return propertyList;
}

private void FindProperties(CodeElements elements, IList<CodeProperty> properties)
{
  foreach (CodeElement element in elements)
  {
    CodeProperty property = element as CodeProperty;
    if (property != null)
    {
      properties.Add(property);
    } 
    FindProperties(element.Children, properties);
  }
}
Transitive answered 15/3, 2012 at 12:25 Comment(0)
A
0

You have to import your own Assembly to be able to access the type properties via reflection. T4 templates are executed in the context of Visual Studio, this is the reason why you can't access the types of your project.

So import the assembly with the desired type with the assembly-directive. You can use Macros to locate your assembly (such as $(SolutionDir)). Then you can import the namespace and use the type from your own assembly.

It could be something like this:

<#@ template debug="false" hostspecific="true" language="C#" #>
<#@ assembly name="$(SolutionDir)YourProject\bin\debug\YourAssembly.dll" #>
<#@ import namespace="YourNamespace" #>

<#
   Type typeInfo = typeof(yourType);
   foreach (PropertyInfo propertyInfo in yourType.GetProperties())
   {#>
      <#=propertyInfo.Name#>
<#}#>

It is highly recommended to use this with Visual Studio 2010 SP 1 (or newer). Previous Visual Studio versions used a single application domain to load assembly with the assembly-directive. So you had to restart Visual Studio each time the assembly was updated. With SP1 VS uses a new appdomain for each T4 transformation.

Am‚lie answered 15/3, 2012 at 11:0 Comment(4)
am i missing something.. i want to iterate over a project folder, return the projectItem, get the type and return the properties.. If i'm specifying the type, i might as well hardcode it all.Hustler
It would be far more easier to use the .NET reflection. The ComObject returned by your method can not be used by .NET reflection, as it is no .NET type. But if you already have an project item, do you can use it with the ProjectItem interface (msdn.microsoft.com/en-us/library/envdte.projectitem.aspx)?Am‚lie
im obviously being dim, but if i'm specifying the assembly and the type i want to reflect.. i don't see the benefit. i'd need to iterate over the assembly for Models inside?Hustler
i'm sniffing around <CodeClass>Hustler
D
0
private static IEnumerable<CodeElement> Flatten(CodeElements elements) {
    foreach (CodeElement2 child in elements) {
        yield return child;

        foreach (var i in Flatten( child.Children )) {
            yield return i;
        }
    }
}

...

var imports = Flatten( fileCodeModel.CodeElements )
                .Where( i => i.Kind == vsCMElement.vsCMElementImportStmt )
                .Cast<CodeImport>();
Deft answered 24/6, 2017 at 19:54 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.