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

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

ИЗМЕНИТЬ

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

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

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

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


person Tom Schreck    schedule 29.09.2017    source источник
comment
вы запускаете генератор кода из цели msbuild во время сборки или вне процесса сборки в качестве автономного exe / инструмента? большинство подходов к генерации интегрируются в msbuild, поэтому они могут динамически добавлять код даже во время сборки во время разработки в VS без необходимости редактировать файл проекта   -  person Martin Ullrich    schedule 29.09.2017
comment
Итак, в основном первый вызов project.Save () не имеет никакого эффекта в VS?   -  person stijn    schedule 29.09.2017
comment
Это автономный генератор кода, написанный с помощью файлов T4. Я не уверен, как интегрировать T4 с MS Build. Первый project.save (), похоже, не влияет на визуальную студию. Второе сохранение происходит после удаления устаревших активов кода, и VS получает запрос на перезагрузку. Но похоже, что у VS есть собственная версия проекта в памяти, которая отличается от файла csproj, который был загружен / изменен / сохранен внутри области генератора кода.   -  person Tom Schreck    schedule 29.09.2017
comment
Вот видео генератора кода в действии внутри VS. Вы увидите, что когда я удаляю интерфейс C # без сохранения проекта, VS понимает, что файл csproj для проекта DataContracts был изменен. Пользователю предлагается отменить, сохранить как, перезаписать и т. Д. Выбор «сохранить как» не удаляет удаленный ресурс из VS. Ссылка все еще существует в файле csproj. screencast.com/t/JWTE0LpkXZGX   -  person Tom Schreck    schedule 29.09.2017
comment
@MartinUllrich - я генерирую содержимое файлов с помощью TextTransform и использую System.IO.File для физической записи файла по пути. Вот почему мне нужно вручную изменить файл csproj, чтобы он узнал о новом файле. Вы хотите сказать, что я могу ссылаться на сборку Microsoft.Build в моем T4 и использовать Microsoft.Build для добавления нового файла?   -  person Tom Schreck    schedule 29.09.2017
comment
Изучали ли вы создание расширения Visual Studio вместо автономного генератора кода?   -  person Helder Sepulveda    schedule 02.10.2017
comment
@HelderSepu спасибо за ответ. Я нет. Я знаю, как создавать активы кода через T4. В T4 у меня много работы. Может ли расширение Visual Studio работать с моими существующими сценариями генерации кода T4?   -  person Tom Schreck    schedule 03.10.2017
comment
В качестве альтернативного подхода, является ли Visual Studio строгим требованием перезагрузки проекта? Можете ли вы вместо этого изменить тип проекта на более гибкий, позволяющий добавлять / удалять файлы без перезагрузки всего проекта?   -  person zastrowm    schedule 07.10.2017
comment
@FriendlyGuy, спасибо за ответ. Я могу попробовать вашу идею. Сгенерированные активы кода написаны на C # и должны быть созданы. Не уверен, влияет ли изменение типа проекта на встраивание проекта в DLL.   -  person Tom Schreck    schedule 09.10.2017
comment
Вы можете попробовать выгрузить измененный проект из sln, а затем после процесса генерации добавить их обратно.   -  person Dmitrii Zyrianov    schedule 15.10.2017
comment
Сработало бы что-нибудь в этом роде для вас? helixoft.com/blog/. Получить Env DTE текущего проекта, а затем изменить решение?   -  person Tarun Lalwani    schedule 15.10.2017


Ответы (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();
    }
#>

Вот результат в обозревателе решений:

результат

person Lucas Trzesniewski    schedule 16.10.2017