2017-09-29 69 views
17

。代码生成过程以编程方式将项目添加/删除到csproj文件。工作流程如下:开发人员添加一个新的C#接口,或者删除Visual Studio 2017中现有的C#接口。如果开发人员先保存项目文件,然后运行代码生成器,那么一切都按预期工作。代码生成的资产要么添加到项目中(要么被移除),Visual Studio会相应地反映这些更改。但是,如果开发人员在运行代码生成器之前未能保存csproj文件,并且删除了C#接口,则代码生成的资产不会从项目中删除,因为Visual Studio不接受csproj文件修改。如何的Visual Studio 2017年接受我已经开发了其中的代码资产(波苏斯)基于关闭的C#接口产生内部使用一个代码生成修改的csproj文件

里面的代码生成器,我物理地去除该被删除,我保存的csproj文件中的代码生成的文件的引用。我通过在记事本中打开csproj来验证引用的文件是否从csproj文件中删除。然而,当我把Visual Studio中成为关注的焦点,Visual Studio中识别出的csproj文件已更改,并问我是否要放弃,覆盖,另存为等,并提出从我的代码生成过程的csproj文件的更改将丢失。 Visual Studio将对已删除文件的引用添加回到csproj文件中。我试过丢弃,覆盖,另存为等,我没有让Visual Studio接受新修改的csproj文件(删除文件的引用被删除)。

这里是我的删除代码生成的资产代码:

using Microsoft.Build.Evaluation; 
using Microsoft.Build.Logging; 
using System; 
using System.Collections.Generic; 
using System.IO; 
using System.Linq; 
using System.Threading.Tasks; 

    public static void RemoveGeneratedFilesFromProject(String projectPath) 
    { 
     UnloadAnyProject(); 

     var project = ProjectCollection.GlobalProjectCollection.LoadedProjects.FirstOrDefault(pr => pr.FullPath == projectPath); 

     //ATTEMPT TO SAVE PROJECT IN CASE DEVELOPER DID NOT... 
     project.Save(); 


     //GET A LIST OF ITEMS CONTAINING PATH TO CODE-GENERATED ASSETS ("Generated\API") 
     IList<ProjectItem> generatedItemsList = project.GetItems("Compile").Where(item => item.EvaluatedInclude.Contains(@"Generated\Api")).ToList(); 

     foreach (var item in generatedItemsList) 
     { 
      project.RemoveItem(item); 
     } 

     //SAVE PROJECT TO REFLECT ALL OF THE CODE GENERATED ITEMS REMOVED FROM PROJECT FILE 
     project.Save(); 

     UnloadAnyProject(); 
    } 

    private static void UnloadAnyProject() 
    { 
     ProjectCollection projcoll = ProjectCollection.GlobalProjectCollection; 

     foreach (Project project in projcoll.LoadedProjects) 
     { 
      ProjectCollection mypcollection = project.ProjectCollection; 
      mypcollection.UnloadProject(project); 
     } 
    } 

是否有可能有Visual Studio的只是接受新的csproj文件?删除资产时是否需要对csproj文件进行一些设置?让Visual Studio陷入修改后的csproj文件阻碍了代码生成器去除不再需要的代码生成资产(源于物理删除C#接口文件)的实用性。

EDIT

下面是示出运行在Visual Studio中的T4发生器基于C#界面上生成C#资产的处理的视频。我删除源代码的C#接口,重新运行代码生成器,并相应更新项目文件,导致项目重新加载。

https://www.screencast.com/t/JWTE0LpkXZGX

的问题不是该项目被重新加载。问题是代码生成器更新并将csproj文件保存在Visual Studio之外,这会导致Visual Studio因csproj文件更改而感到困惑。如何让Visual Studio'默默'接受保存到csproj文件的更改?

感谢您的帮助。

+0

你运行代码生成器从一个msbuild目标在构建过程中还是在构建过程之外作为一个独立的exe /工具?大多数方法一代融入的MSBuild,使他们能够动态地,甚至在设计时在VS,而无需编辑项目文件 –

+0

所以基本上到project.Save(第一次调用)在VS没有效果添加代码生成? – stijn

+0

这是一个用T4文件编写的独立代码生成器。我不确定如何将T4与MS Build整合。第一个project.save()似乎不影响visual studio。第二次保存会在删除过时的代码资产后发生,VS会提示重新加载。但似乎VS都有它自己的内存中的项目的版本,这是不同的已加载/中的csproj文件修改/保存里面的代码生成的范围。 –

回答

2

修改自己的项目文件,而它加载到Visual Studio是不是一个好主意。即使你找到了强制它重新加载项目的方法,它仍然需要重新加载它,这是一个主要的麻烦。

这是更好的从T4模板访问EnvDTE,并通过修改项目文件。该对象使您可以访问Visual Studio的项目模型。

注意,用户仍然需要保存在默认情况下修改后的项目文件,因为它会被视为由VS脏,但这种行为是与其他所有项目文件的修改,你可以通过做VS.一致不过,如果你真的需要它,你可以强制VS保存这个项目。

这里就是你需要做的访问VS,为documented here什么:

设置hostspecific属性true

<#@ template debug="false" hostspecific="true" language="C#" #> 

进口EnvDTE

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

获取dte对象:

<# 
    var dte = (DTE)((IServiceProvider)Host).GetService(typeof(DTE)); 
#> 

现在你可以完全访问VS的项目的API。请注意,使用这种方法,您将失去在Visual Studio之外执行模板的能力。


下面是如何添加模板旁的文件一个完整的例子:

<#@ template debug="false" hostspecific="true" language="C#" #> 
<#@ assembly name="EnvDTE" #> 
<#@ import namespace="EnvDTE" #> 
<#@ output extension=".txt" #> 
<# 
    // Get the DTE 
    var dte = (DTE)((IServiceProvider)Host).GetService(typeof(DTE)); 

    // Find the currently running template file in the project structure 
    var template = dte.Solution.FindProjectItem(Host.TemplateFile); 

    // Write something to a dummy file next to the template 
    var filePath = System.IO.Path.ChangeExtension(Host.TemplateFile, "foo"); 
    System.IO.File.WriteAllText(filePath, "Hello, world!"); 

    // Add the file as a subitem of the template 
    var fileItem = dte.Solution.FindProjectItem(filePath); 
    if (fileItem == null) 
    { 
     template.ProjectItems.AddFromFile(filePath); 

     // If you really want to, you can force VS to save the project, 
     // though I wouldn't recommend this 
     template.ContainingProject.Save(); 
    } 
#> 

这里的结果在Solution Explorer:

result