2017-09-29 68 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