Scaffold Template Edition MVC. How to get project reference in Solution Folder?

3

I'm editing the ASP.NET MVC Scaffold T4 templates and I need to get some extra class information. For example, , of the DisplayName class attribute.

I found some examples:

var env = (DTE)((IServiceProvider)this.Host).GetService(typeof(EnvDTE.DTE));
var proj = env.Solution.Projects.OfType<Project>()
    .Where(p => p.Name == assemblyName)
    .FirstOrDefault();

var codeType = proj.CodeModel.CodeTypeFromFullName(classFullName);

var attr = codeType != null ? codeType.Attributes
    .OfType<EnvDTE.CodeAttribute>()
    .FirstOrDefault(x => x.Name == "DisplayName") : null;

var modelName = attr != null ? 
    attr.Value.Replace("\"", "") : 
    ViewDataTypeShortName;

So far so good. But I have in my Solution two projects that are from another Solution (and are in different Solution directories) but have been added normally via Add / Existing Project .

So these two other assemblies I can not get reference to using:

var proj = env.Solution.Projects.OfType<Project>()
    .Where(p => p.Name == assemblyName)
    .FirstOrDefault();

I even made a reflection on env.Solution.Projects.OfType<Project>() to see what it would print:

<# foreach (var proj in env.Solution.Projects.OfType<Project>()) { #>
// <#= proj.Name #>
<# } #>

And I got something I did not understand. Among the projects that were actually created, such as:

// Common
// Projeto.Domain
// Projeto.Service
// Projeto.WebMVC

Where this Common is a Solution Folder created for the other Solution projects. But the projects that are within it do not.

So another way I found to do a reflection in the assemblies was to add them directly:

<#@ assembly name="C:\Projects\SolutionA\Projeto.Domain\bin\Debug\Projeto.Domain.dll" #>
<#@ include namespace="Projeto.Domain.Entities" #>
<#@ assembly name="C:\Projects\SolutionA\Projeto.Service\bin\Debug\Projeto.Service.dll" #>
<#@ include namespace="Projeto.Service.ViewModels" #>

In this way reflection is already done in the traditional way.

The problem with this type of addition is that the fonts will not be in the same directories, which will result in the assemblies not being in the specified directories.

So I tried with, for example $(SolutionDir) , but it seems that in Scaffold templates it does not work.

My question is how to add references logically or how to read the other assemblies via env.Solution.Projects .

For those who have had experience, another way to solve the problem is also welcome!

    
asked by anonymous 04.09.2015 / 16:22

2 answers

2

You just have to read the remaining levels to find the user classes you want to find. The following code may be useful:

var proj = env.Solution.Projects.OfType<Project>()
    .Where(p => p.Name == assemblyName)
    .FirstOrDefault();

foreach (EnvDTE.CodeElement element in proj.CodeModel.CodeElements)
{
    if (element.Kind == EnvDTE.vsCMElement.vsCMElementClass)
    {
        var myClass = (EnvDTE.CodeClass)element;
        // MyClass será uma classe de usuário
    }
}

To iterate the properties of the class, this code might be interesting:

public IEnumerable<CodeProperty> GetProperties(CodeClass @class)
{
    if (@class == null) 
        return new List<CodeProperty>();

    var baseProperties = GetProperties(@class.Bases.Cast<CodeClass>().FirstOrDefault());

    return baseProperties.Concat(@class
        .Members
        .Cast<CodeElement>()
        .Where(ce => ce.Kind == vsCMElement.vsCMElementProperty)
        .Cast<CodeProperty>()
        .Where(p => p.Access == vsCMAccess.vsCMAccessPublic));
    }
}

I pulled it out .

    
04.09.2015 / 17:10
1

Solution found at: EnvDTE: Getting all projects (the SolutionFolder PITA)

Well, I created an additional file where I put my functions, called MoreFunctions.cs.include.t4 and added in the template folders that I want to use, for example: Project \ CodeTemplates \ MvcView

At the end of the template files I add the following line:

<#@ include file="MoreFunctions.cs.include.t4" #>

Archive content, everything tested in Visual Studio 2015 Community :

<#+
DTE2 GetServiceEnvironment() {      
    return (DTE2)((IServiceProvider)this.Host).GetService(typeof(EnvDTE.DTE));
}

Solution GetSolution() {
    return GetServiceEnvironment().Solution;
}

List<Project> GetProjects() {
    Projects projects = GetSolution().Projects;
    var projectList = new List<Project>();
    foreach (Project project in projects) {
        if (project.Kind == ProjectKinds.vsProjectKindSolutionFolder)
            projectList.AddRange(GetProjectsInSolutionFolder(project));
        else
            projectList.Add(project);
    }  
    return projectList;
}

List<Project> GetProjectsInSolutionFolder(Project solutionFolder) {
    var projectList = new List<Project>();
    for (var i = 1; i <= solutionFolder.ProjectItems.Count; i++) {
        var subProject = solutionFolder.ProjectItems.Item(i).SubProject;
        if (subProject == null)
            continue;
        if (subProject.Kind == ProjectKinds.vsProjectKindSolutionFolder)
            projectList.AddRange(GetProjectsInSolutionFolder(subProject));
        else
            projectList.Add(subProject);
    }
    return projectList;
}

Project GetProject(string projectName) {
    return GetProjects().OfType<Project>().Where(p => p.Name == projectName).FirstOrDefault();
}

Project GetThisProject() {
    return GetSolution().FindProjectItem(this.Host.TemplateFile).ContainingProject as EnvDTE.Project;
}

CodeType GetCodeType(string classFullName) {
    var projects = GetProjects();
    foreach (var project in projects) {
        if (classFullName.StartsWith(project.Name)) {
            CodeType codeType = project.CodeModel.CodeTypeFromFullName(classFullName);
            return codeType;
        }
    }
    return null;
}

CodeClass GetCodeClass(CodeElements elements, string className) {
    if (elements == null)
        return null;
    foreach (EnvDTE.CodeElement element in elements) {
        if (element.Kind == vsCMElement.vsCMElementClass) {
            var codeClass = (EnvDTE.CodeClass)element;
            if (codeClass != null && codeClass.FullName == className)
                return codeClass;
        }

        if (element.Kind == vsCMElement.vsCMElementNamespace) {
            var result = GetCodeClass(((CodeNamespace)element).Members, className);
            if (result != null)
                return result;
        }

        if (element.IsCodeType) {
            var result = GetCodeClass(((CodeType)element).Members, className);
            if (result != null)
                return result;
        }
    }
    return null;
}
#>

I needed to add the following references in Imports.include.t4 :

<#@ assembly name="EnvDTE" #>
<#@ assembly name="EnvDTE80" #>
<#@ import namespace="EnvDTE" #>
<#@ import namespace="EnvDTE80" #>

Finally, an example use to get the value of the DisplayName attribute:

var codeType = GetCodeType(ViewDataTypeName);

var attr = codeType != null ? 
    codeType.Attributes.OfType<EnvDTE.CodeAttribute>()
        .FirstOrDefault(x => x.Name == "DisplayName") : null;

var modelDisplayName = attr != null ? 
    attr.Value.Replace("\"", "") : 
    ViewDataTypeShortName;

PREVIOUS TRYING - IT DOES NOT WORK !!!

Implemented as follows:

CodeClass GetCodeClass(CodeElements elements, string className) {
    if (elements == null)
        return null;
    foreach (EnvDTE.CodeElement element in elements) {
        if (element.Kind == vsCMElement.vsCMElementClass)
        {
            var codeClass = (EnvDTE.CodeClass)element;
            if (codeClass != null && codeClass.FullName == className)
                return codeClass;
        }

        if (element.Kind == vsCMElement.vsCMElementNamespace)
        {
            var result = GetCodeClass(((CodeNamespace)element).Members, className);
            if (result != null)
                return result;
        }

        if (element.IsCodeType)
        {
            var result = GetCodeClass(((CodeType)element).Members, className);
            if (result != null)
                return result;
        }
    }
    return null;
}

I used it for:

var project = GetProject();
var codeType = GetCodeClass(project.CodeModel.CodeElements, ViewDataTypeName);

var attr = codeType != null ?    
    codeType.Attributes.OfType<EnvDTE.CodeAttribute>()
        .FirstOrDefault(x => x.Name == "DisplayNameAttribute") : null;
modelName = attr != null ? 
    attr.Value.Replace("\"", "") : 
    ViewDataTypeShortName;

But unfortunately it does not return my attributes.

I made a reflection, but did not print anything.

<#
    foreach (var prop in codeType.GetType().GetProperties()) {
#>
//      <#= prop.Name #>
<#
    }
#>

Nothing, and nothing too:

foreach (CodeAttribute attribute in codeType.Attributes) {
    #>
//  <#= attribute.Name #>
    <#
}

But the class name printed ...

#>
// <#= codeType.Name #>

<#

¬¬ You will understand ..

Bora for the next journey !!

    
04.09.2015 / 22:00