如何让Visual Studio 2017接受对csproj文件的修改

时间:2017-09-29 05:18:50

标签: c# msbuild visual-studio-2017 microsoft.build

我开发了一个内部使用的代码生成器,其中代码资产(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文件的更改?

感谢您的帮助。

1 个答案:

答案 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();
    }
#>

以下是解决方案资源管理器中的结果:

result