如何在解决方案文件夹(非物理文件夹)中的项目中创建文件

时间:2017-09-01 10:47:06

标签: c# visual-studio t4

我正在尝试在多输出文件T4模板中的解决方案文件夹中的项目创建文件tangible T4 Editor tutorial

我的解决方案中有以下结构:

/T4Templates
   - T4TemplateProject **<--- my T4 Project**
/Common
   -Project.NameSpace.Common **<--- Want to create file in here**
/Services
   -Project.NameSpace.Services **<--- And create file in here**

我的出发点是以下摘自ttincludefile:

/// <summary>
/// Marks the end of the last file if there was one, and starts a new
/// and marks this point in generation as a new file.
/// </summary>
/// <param name="name">Filename</param>
/// <param name="projectName">Name of the target project for the new file.</param>
/// <param name="folderName">Name of the target folder for the new file.</param>
/// <param name="fileProperties">File property settings in vs for the new File</param>
public void StartNewFile(string name, string projectName = "",
    string folderName = "", FileProperties fileProperties = null)
{
    if (String.IsNullOrWhiteSpace(name) == true)
    {
        throw new ArgumentException("name");
    }

    CurrentBlock = new Block 
    { 
        Name = name, 
        ProjectName = projectName, 
        FolderName = folderName,
        FileProperties = fileProperties ?? new FileProperties()
    };
}

/// <summary>
/// Produce the template output files.
/// </summary>
public virtual IEnumerable<OutputFile> Process(bool split = true)
{
    var list = new List<OutputFile>();

    if (split)
    {
        EndBlock();

        var headerText = _generationEnvironment.ToString(header.Start, header.Length);
        var footerText = _generationEnvironment.ToString(footer.Start, footer.Length);
        files.Reverse();

        foreach (var block in files)
        {
            var outputPath = VSHelper.GetOutputPath(dte, block,
                Path.GetDirectoryName(_textTransformation.Host.TemplateFile)); //<-- exception bubbled up here
            var fileName = Path.Combine(outputPath, block.Name);
            var content = this.ReplaceParameter(headerText, block) + 
                _generationEnvironment.ToString(block.Start, block.Length) + 
                footerText;

            var file = new OutputFile 
            { 
                FileName = fileName, 
                ProjectName = block.ProjectName, 
                FolderName = block.FolderName,
                FileProperties = block.FileProperties,
                Content = content
            };

            CreateFile(file);
            _generationEnvironment.Remove(block.Start, block.Length);

            list.Add(file);     
        }
    }

    projectSyncAction.EndInvoke(projectSyncAction.BeginInvoke(list, null, null));
    this.CleanUpTemplatePlaceholders();
    var items = VSHelper.GetOutputFilesAsProjectItems(this.dte, list);
    this.WriteVsProperties(items, list);

    if (this.IsAutoIndentEnabled == true && split == true)
    {
        this.FormatProjectItems(items);
    }

    this.WriteLog(list);

    return list;
}

private void FormatProjectItems(IEnumerable<EnvDTE.ProjectItem> items)
{
    foreach (var item in items)
    {
        this._textTransformation.WriteLine(
        VSHelper.ExecuteVsCommand(this.dte, item, "Edit.FormatDocument"));
        this._textTransformation.WriteLine("//-> " + item.Name);
    }
}

private void WriteVsProperties(IEnumerable<EnvDTE.ProjectItem> items, 
    IEnumerable<OutputFile> outputFiles)
{
    foreach (var file in outputFiles)
    {
        var item = items
            .Where(p => p.Name == Path.GetFileName(file.FileName))
            .FirstOrDefault();
        if (item == null) continue; 

        if (String.IsNullOrEmpty(file.FileProperties.CustomTool) == false)
        {
            VSHelper.SetPropertyValue(
                item, "CustomTool", file.FileProperties.CustomTool);
        }

        if (String.IsNullOrEmpty(file.FileProperties.BuildActionString) == false)
        {
            VSHelper.SetPropertyValue(
                item, "ItemType", file.FileProperties.BuildActionString);
        }
    }
}

private string ReplaceParameter(string text, Block block)
{
    if (String.IsNullOrEmpty(text) == false)
    {
        text = text.Replace("$filename$", block.Name);
    }

    foreach (var item in block.FileProperties.TemplateParameter.AsEnumerable())
    {
        text = text.Replace(item.Key, item.Value);
    }

    return text;
}

/// <summary>
/// Write log to the default output file.
/// </summary>
/// <param name="list"></param>
private void WriteLog(IEnumerable<OutputFile> list)
{
    this._textTransformation.WriteLine("// Generated helper templates");
    foreach (var item in templatePlaceholderList)
    {
        this._textTransformation.WriteLine(
            "// " + this.GetDirectorySolutionRelative(item));        
    }

    this._textTransformation.WriteLine("// Generated items");
    foreach (var item in list)
    {
        this._textTransformation.WriteLine(
            "// " + this.GetDirectorySolutionRelative(item.FileName)); 
    }
}

/// <summary>
/// Removes old template placeholders from the solution.
/// </summary>
private void CleanUpTemplatePlaceholders()      
{
    string[] activeTemplateFullNames = this.templatePlaceholderList.ToArray();
    string[] allHelperTemplateFullNames = VSHelper.GetAllSolutionItems(this.dte)
        .Where(p =>
            p.Name == VSHelper.GetTemplatePlaceholderName(this.templateProjectItem))
        .Select(p => VSHelper.GetProjectItemFullPath(p))
        .ToArray();

    var delta = allHelperTemplateFullNames.Except(activeTemplateFullNames).ToArray();

    var dirtyHelperTemplates = VSHelper.GetAllSolutionItems(this.dte)
        .Where(p => delta.Contains(VSHelper.GetProjectItemFullPath(p)));

    foreach (ProjectItem item in dirtyHelperTemplates)
    {
        if (item.ProjectItems != null)
        {
            foreach (ProjectItem subItem in item.ProjectItems)
            {
                subItem.Remove(); 
            }
        }

        item.Remove();
    }
}

protected virtual void CreateFile(OutputFile file)
{
    if (this.CanOverrideExistingFile == false && File.Exists(file.FileName) == true)
    {
        return;
    }

    if (IsFileContentDifferent(file))
    {
        CheckoutFileIfRequired(file.FileName);
        File.WriteAllText(file.FileName, file.Content, this.Encoding);
    }
}

protected bool IsFileContentDifferent(OutputFile file)
{
    return !(File.Exists(file.FileName) && File.ReadAllText(file.FileName) == file.Content);
}

private void CheckoutFileIfRequired(string fileName)
{
    if (dte.SourceControl == null
        || !dte.SourceControl.IsItemUnderSCC(fileName)
            || dte.SourceControl.IsItemCheckedOut(fileName))
    {
        return;
    }

    // run on worker thread to prevent T4 calling back into VS
    checkOutAction.EndInvoke(checkOutAction.BeginInvoke(fileName, null, null));
}   

摘录的第2部分: VSHelper 类。我已经指出异常发生的地方并且冒泡了。您可以搜索字符串<-- exception

public class VSHelper
{
    /// <summary>
    /// Execute Visual Studio commands against the project item.
    /// </summary>
    /// <param name="item">The current project item.</param>
    /// <param name="command">The vs command as string.</param>
    /// <returns>An error message if the command fails.</returns>
    public static string ExecuteVsCommand(EnvDTE.DTE dte, EnvDTE.ProjectItem item, params string[] command)
    {
        if (item == null)
        {
            throw new ArgumentNullException("item");
        }

        string error = String.Empty;

        try
        {
            EnvDTE.Window window = item.Open();
            window.Activate();

            foreach (var cmd in command)
            {
                if (String.IsNullOrWhiteSpace(cmd) == true)
                {
                    continue;
                }

                EnvDTE80.DTE2 dte2 = dte as EnvDTE80.DTE2;
                dte2.ExecuteCommand(cmd, String.Empty);     
            }

            item.Save();
            window.Visible = false;
            // window.Close(); // Ends VS, but not the tab :(
        }
        catch (Exception ex)
        {
            error = String.Format("Error processing file {0} {1}", item.Name, ex.Message);
        }

        return error;
    }

    /// <summary>
    /// Sets a property value for the vs project item.
    /// </summary>
    public static void SetPropertyValue(EnvDTE.ProjectItem item, string propertyName, object value)
    {
        EnvDTE.Property property = item.Properties.Item(propertyName);
        if (property == null)
        {
            throw new ArgumentException(String.Format("The property {0} was not found.", propertyName));
        }
        else
        {
            property.Value = value;
        }
    }

    public static IEnumerable<ProjectItem> GetOutputFilesAsProjectItems(EnvDTE.DTE dte, IEnumerable<OutputFile> outputFiles)
    {
        var fileNames = (from o in outputFiles
                        select Path.GetFileName(o.FileName)).ToArray();

        return VSHelper.GetAllSolutionItems(dte).Where(f => fileNames.Contains(f.Name));
    }

    public static string GetOutputPath(EnvDTE.DTE dte, Block block, string defaultPath)
    {
        if (String.IsNullOrEmpty(block.ProjectName) == true && String.IsNullOrEmpty(block.FolderName) == true)
        {
            return defaultPath;
        }

        EnvDTE.Project prj = null;
        EnvDTE.ProjectItem item = null;

        if (String.IsNullOrEmpty(block.ProjectName) == false)
        {
            prj = GetProject(dte, block.ProjectName); //<-- exception bubbled up here           
        }

        if (String.IsNullOrEmpty(block.FolderName) == true && prj != null)
        {
            return Path.GetDirectoryName(prj.FullName);
        }
        else if (prj != null && String.IsNullOrEmpty(block.FolderName) == false)
        {
            item = GetAllProjectItemsRecursive(prj.ProjectItems).Where(i=>i.Name == block.FolderName).First();
        }
        else if (String.IsNullOrEmpty(block.FolderName) == false)
        {
            item = GetAllProjectItemsRecursive(
                dte.ActiveDocument.ProjectItem.ContainingProject.ProjectItems).
                Where(i=>i.Name == block.FolderName).First();
        }

        if (item != null)
        {
            return GetProjectItemFullPath(item);
        }

        return defaultPath;
    }
    public static string GetTemplatePlaceholderName(EnvDTE.ProjectItem item)
    {
        return String.Format("{0}.txt4", Path.GetFileNameWithoutExtension(item.Name));
    }

    public static EnvDTE.ProjectItem GetTemplateProjectItem(EnvDTE.DTE dte, OutputFile file, EnvDTE.ProjectItem defaultItem)
    {
        if (String.IsNullOrEmpty(file.ProjectName) == true && String.IsNullOrEmpty(file.FolderName) == true)
        {
            return defaultItem;
        }

        string templatePlaceholder = GetTemplatePlaceholderName(defaultItem);
        string itemPath = Path.GetDirectoryName(file.FileName); 
        string fullName = Path.Combine(itemPath, templatePlaceholder);
        EnvDTE.Project prj = null;
        EnvDTE.ProjectItem item = null;

        if (String.IsNullOrEmpty(file.ProjectName) == false)
        {
            prj = GetProject(dte, file.ProjectName);            
        }

        if (String.IsNullOrEmpty(file.FolderName) == true && prj != null)
        {
            return FindProjectItem(prj.ProjectItems, fullName, true);
        }
        else if (prj != null && String.IsNullOrEmpty(file.FolderName) == false)
        {
            item = GetAllProjectItemsRecursive(prj.ProjectItems).Where(i=>i.Name == file.FolderName).First();
        }
        else if (String.IsNullOrEmpty(file.FolderName) == false)
        {
            item = GetAllProjectItemsRecursive(
                dte.ActiveDocument.ProjectItem.ContainingProject.ProjectItems).
                Where(i=>i.Name == file.FolderName).First();
        }

        if (item != null)
        {
            return FindProjectItem(item.ProjectItems, fullName, true);
        }

        return defaultItem;
    }

    private static EnvDTE.ProjectItem FindProjectItem(EnvDTE.ProjectItems items, string fullName, bool canCreateIfNotExists)
    {
        EnvDTE.ProjectItem item = (from i in items.Cast<EnvDTE.ProjectItem>()
                                  where i.Name == Path.GetFileName(fullName)
                                  select i).FirstOrDefault();
        if (item == null)
        {
            File.CreateText(fullName);
            item = items.AddFromFile(fullName);
        }

        return item;
    }

    public static EnvDTE.Project GetProject(EnvDTE.DTE dte, string projectName) 
    {
        return GetAllProjects(dte).Where(p=>p.Name == projectName).First(); // <-- exception ORIGINATES HERE
    }

    public static IEnumerable<EnvDTE.Project> GetAllProjects(EnvDTE.DTE dte)
    {
        List<EnvDTE.Project> projectList = new List<EnvDTE.Project>();

        var folders = dte.Solution.Projects.Cast<EnvDTE.Project>().Where(p=>p.Kind == EnvDTE80.ProjectKinds.vsProjectKindSolutionFolder);

        foreach (EnvDTE.Project folder in folders)
        {
            if (folder.ProjectItems == null) continue;

            foreach (EnvDTE.ProjectItem item in folder.ProjectItems)
            {
                if (item.Object is EnvDTE.Project)
                    projectList.Add(item.Object as EnvDTE.Project);
            }
        }

        var projects = dte.Solution.Projects.Cast<EnvDTE.Project>().Where(p=>p.Kind != EnvDTE80.ProjectKinds.vsProjectKindSolutionFolder);

        if (projects.Count() > 0)
            projectList.AddRange(projects);

        return projectList;
    }

    public static EnvDTE.ProjectItem GetProjectItemWithName(EnvDTE.ProjectItems items, string itemName)
    {
        return GetAllProjectItemsRecursive(items).Cast<ProjectItem>().Where(i=>i.Name == itemName).First();
    }

    public static string GetProjectItemFullPath(EnvDTE.ProjectItem item)
    {
        return item.Properties.Item("FullPath").Value.ToString();
    }

    public static IEnumerable<EnvDTE.ProjectItem> GetAllSolutionItems(EnvDTE.DTE dte)
    {
        List<EnvDTE.ProjectItem> itemList = new List<EnvDTE.ProjectItem>();

        foreach (Project item in GetAllProjects(dte))
        {
            if (item == null || item.ProjectItems == null) continue;

            itemList.AddRange(GetAllProjectItemsRecursive(item.ProjectItems));   
        }

        return itemList;
    }

    public static IEnumerable<EnvDTE.ProjectItem> GetAllProjectItemsRecursive(EnvDTE.ProjectItems projectItems) 
    {
        foreach (EnvDTE.ProjectItem projectItem in projectItems) 
        {
            if (projectItem.ProjectItems == null) continue;

            foreach (EnvDTE.ProjectItem subItem in GetAllProjectItemsRecursive(projectItem.ProjectItems))
            {
                yield return subItem;
            }


            yield return projectItem;
        }
    }
}

我的模板:

<#@ template  debug="true" hostSpecific="true" #>
<#@ output extension=".cs" #>
<#@ Assembly Name="System.Core" #>
<#@ Assembly Name="System.Windows.Forms" #>
<#@ import namespace="System" #>
<#@ import namespace="System.IO" #>
<#@ import namespace="System.Diagnostics" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Collections" #>
<#@ import namespace="System.Collections.Generic" #> 

<#@ include file="TemplateFileManagerV2.1.ttinclude" #>
<#
var manager = TemplateFileManager.Create(this);
#>

<#
//This correctly generates a file in the local T4TemplateProject
manager.StartNewFile("MyObject.cs");
#>

<#
//this fails
manager.StartNewFile("MyObjectDto.cs","Project.NameSpace.Common","Common");
#>

<#    
manager.Process();
#>

我收到以下错误:

  

严重级代码描述项目文件行抑制状态   运行转换时出错:System.InvalidOperationException:   序列不包含元素

我已阅读以下内容:

  1. http://t4-editor.tangible-engineering.com/blog/how-to-generate-multiple-output-files-from-a-single-t4-template.html
  2. How to create multiple output files from a single T4 template using Tangible Editor?
  3. T4示例基于以下解决方案结构: enter image description here

    我想在解决方案文件夹的地方做同样的事情: enter image description here

1 个答案:

答案 0 :(得分:1)

我已将代码的相关部分缩减至:

var projectList = dte.Solution.Projects
    .Cast<EnvDTE.Project>()
    .Where(p => p.Kind == EnvDTE80.ProjectKinds.vsProjectKindSolutionFolder)
    .Where(folder => folder.ProjectItems != null)
    .SelectMany(folder => folder.ProjectItems)
    .Where(item => item.Object is EnvDTE.Project)
    .Select(item => item.Object as EnvDTE.Project)

var projects = dte.Solution.Projects.Cast<EnvDTE.Project>()
    .Where(p => p.Kind != EnvDTE80.ProjectKinds.vsProjectKindSolutionFolder);

projectList.Concat(projects)
    .Where(p=>p.Name == projectName)
    .First(); // <-- exception ORIGINATES HERE

所以它看起来像p.Kind或p.Name不是你所期望的。

要对此进行调试,请循环遍历dte.Solution.Projects及其所有子项ProjectItem。输出Kind和Name属性。