我开发了一个内部使用的代码生成器,其中代码资产(POCO)是基于C#接口生成的。代码生成过程以编程方式将项目添加/删除到csproj文件。工作流程如下:开发人员在Visual Studio 2017中添加新的C#接口或删除现有的C#接口。如果开发人员首先保存项目文件然后运行代码生成器,那么一切都按预期工作。代码生成的资产可以添加到项目中(或删除),Visual Studio会相应地反映这些更改。但是,如果开发人员在运行代码生成器之前未能保存csproj文件,并且已删除了C#接口,则代码生成的资产不会从项目中删除,因为Visual Studio不接受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#接口文件)。
修改
这是一个视频,显示了在Visual Studio中运行T4生成器的过程,该生成器基于C#接口生成C#资产。我删除源C#接口,重新运行代码生成器,并相应地更新项目文件,导致重新加载项目。
https://www.screencast.com/t/JWTE0LpkXZGX
问题不在于项目重新加载。问题是代码生成器更新并将csproj文件保存在Visual Studio之外,这会导致Visual Studio混乱,因为csproj文件已更改。如何使Visual Studio“静默”接受保存到csproj文件的更改?
感谢您的帮助。
答案 0 :(得分:3)
在将项目文件加载到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();
}
#>
以下是解决方案资源管理器中的结果: