Подтвердить что ты не робот

Как Visual Studio 2017 принимает изменения в файле csproj

Я разработал генератор кода для внутреннего использования, где активы кода (POCOs) создаются на основе интерфейсов С#. Процесс генерации кода программно добавляет/удаляет элементы в файл csproj. Рабочий процесс выглядит следующим образом: разработчик добавляет новый интерфейс С# или удаляет существующий интерфейс С# в Visual Studio 2017. Если разработчик сначала сохраняет файл проекта, а затем запускает генератор кода, тогда все работает так, как ожидалось. Сгенерированные кодом активы либо добавляются в проект (или удаляются), и Visual Studio соответствующим образом отражает эти изменения. Однако, если разработчику не удалось сохранить файл csproj перед запуском генератора кода и удалил интерфейс С#, тогда генерируемые кодом активы не будут удалены из проекта, потому что 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, это затрудняет полезность генератора кода для удаления уже сгенерированных кодом активов (связанных с физическим удалением файла интерфейса С#).

ИЗМЕНИТЬ

Здесь показано видео, показывающее процесс запуска генератора T4 внутри Visual Studio, генерирующего ресурсы С# на основе интерфейса С#. Я удаляю исходный С# -интерфейс, повторно запускаю генератор кода, и файл проекта обновляется соответственно, заставляя проект перезагружаться.

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

Проблема не в том, что проект перезагружается. Проблема заключается в обновлении генератора кода и сохраняет файл csproj за пределами Visual Studio, что заставляет Visual Studio запутываться, потому что файл csproj изменился. Как заставить Visual Studio "молча" принимать изменения, сохраненные в файле csproj?

Спасибо за вашу помощь.

4b9b3361

Ответ 1

Модификация файла проекта самостоятельно при загрузке в Visual Studio - отличная идея. Даже если вы найдете способ заставить его перезагрузить проект, ему все равно придется перезагрузить его, что является серьезной неприятностью.

Намного лучше получить доступ к EnvDTE из шаблона T4 и изменить файл проекта. Этот объект дает вам доступ к модели проекта Visual Studio.

Обратите внимание, что пользователю по-прежнему необходимо сохранить измененный файл проекта по-умолчанию, поскольку он будет рассматриваться как загрязненный VS, но это поведение согласуется с любой другой модификацией файла проекта, которую вы можете выполнять с помощью VS. Вы можете заставить VS сохранить проект, если он вам действительно нужен.

Вот что вам нужно сделать для доступа к VS, как описанный здесь:

Установите для атрибута 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));
#>

И теперь у вас есть полный доступ к API-интерфейсам VS. Помните, что при таком подходе вы теряете возможность запускать шаблон вне 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