2017-09-29 75 views
17

Opracowałem generator kodu do użytku wewnętrznego, w którym zasoby kodu (POCO) są generowane w oparciu o interfejsy C#. Proces generowania kodu programowo dodaje/usuwa elementy do pliku csproj. Przepływ pracy wygląda następująco: programista dodaje nowy interfejs C# lub usuwa istniejący interfejs C# w programie Visual Studio 2017. Jeśli programista najpierw zapisze plik projektu, a następnie uruchom generator kodu, wszystko działa zgodnie z oczekiwaniami. Zasoby generowane przez kod są dodawane do projektu (lub usuwane), a Visual Studio odpowiednio je uwzględnia. Jeśli jednak deweloper nie zapisał pliku csproj przed uruchomieniem generatora kodu i usunął interfejs C#, zasoby generowane przez kod nie są usuwane z projektu, ponieważ program Visual Studio nie akceptuje modyfikacji pliku csproj.W jaki sposób program Visual Studio 2017 akceptuje modyfikacje pliku csproj

Wewnątrz generatora kodu fizycznie usuwam odniesienia do plików wygenerowanych przez kod, które są usuwane i zapisuję plik csproj. Weryfikuję, że przywoływane pliki są usuwane z pliku csproj, otwierając csproj w notatniku. Jednak zaraz po włączeniu Visual Studio Visual Studio rozpoznaje, że plik csproj został zmieniony i pyta, czy chcę odrzucić, nadpisać, zapisać jako itp., A zmiany dokonane w pliku csproj z mojego procesu generowania kodu zostaną utracone. Visual Studio dodaje odwołania do usuniętych plików z powrotem do pliku csproj. Próbowałem odrzucić, nadpisać, zapisać jako itp. I nie dostaję Visual Studio, aby zaakceptować nowo zmodyfikowany plik csproj (który ma odniesienia do usuniętych usuniętych plików).

Oto mój kod do usuwania trwałych kodów generowanych:

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); 
     } 
    } 

Czy to możliwe, aby mieć Visual Studio prostu zaakceptować nowy plik csproj? Czy jest jakieś ustawienie, które muszę wprowadzić do pliku csproj podczas usuwania zasobów? Posiadanie programu Visual Studio do zbuforowania zmodyfikowanego pliku csproj utrudnia korzystanie z generatora kodu do usuwania potrzebnych zasobów generowanych przez kod (wynikających z fizycznego usunięcia pliku interfejsu C#).

EDIT

Oto film pokazujący proces uruchomiony generator T4 wewnątrz Visual Studio generowania # aktywa C w oparciu o interfejs C#. Usuwam źródłowy interfejs C#, ponownie uruchamiam generator kodu, a plik projektu jest odpowiednio aktualizowany, powodując ponowne załadowanie projektu.

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

Problemem nie jest to, że projekt zostanie przeładowana. Problem polega na tym, że aktualizacje generatora kodu i zapisują plik csproj poza programem Visual Studio, co powoduje błąd programu Visual Studio, ponieważ plik csproj został zmieniony. W jaki sposób program Visual Studio "cicho" akceptuje zmiany zapisane w pliku csproj?

Dzięki za pomoc.

+0

ty uruchomiony generator kodu z celu msbuild podczas budowania lub poza procesem kompilacji jako samodzielnego exe/tool? większość podejść do generacji integruje się w msbuild, dzięki czemu mogą dynamicznie dodawać kod nawet podczas projektowania w czasie kompilacji w VS bez potrzeby edytowania pliku projektu –

+0

Więc zasadniczo pierwsze wywołanie metody project.Save() nie ma wpływu na VS? – stijn

+0

Jest to samodzielny generator kodu napisany z plikami T4. Nie jestem pewien, jak zintegrować T4 z MS Build. Pierwszy project.save() nie wydaje się wpływać na visual studio. Drugie zapisanie następuje po usunięciu przestarzałych zasobów kodu, a VS zostanie wyświetlony monit o przeładowanie. Ale wygląda na to, że VS ma własną wersję projektu w pamięci, która jest innym plikiem csproj, który został załadowany/zmodyfikowany/zapisany w zasięgu generatora kodu. –

Odpowiedz

2

Samodzielne modyfikowanie pliku projektu podczas jego ładowania do programu Visual Studio nie jest dobrym pomysłem. Nawet jeśli znajdziesz sposób, aby zmusić go do przeładowania projektu, nadal będzie trzeba go ponownie załadować, co jest bardzo uciążliwe.

Znacznie lepiej jest uzyskać dostęp do EnvDTE z szablonu T4 i zmodyfikować plik projektu przez to. Ten obiekt daje dostęp do modelu projektu Visual Studio.

Należy pamiętać, że użytkownik będzie nadal musiał zapisać zmodyfikowany plik projektu domyślnie, ponieważ będzie on postrzegany jako zabrudzony przez VS, ale to zachowanie jest zgodne z każdą inną modyfikacją pliku projektu, którą można wykonać za pomocą VS. Możesz zmusić VS do zapisania projektu, jeśli naprawdę tego potrzebujesz.

Oto, co trzeba zrobić, aby uzyskać dostęp do VS, jak documented here:

Ustaw hostspecific atrybutem true:

<#@ template debug="false" hostspecific="true" language="C#" #> 

importu EnvDTE:

<#@ assembly name="EnvDTE" #> 
<#@ import namespace="EnvDTE" #> 

Uzyskaj obiekt dte:

<# 
    var dte = (DTE)((IServiceProvider)Host).GetService(typeof(DTE)); 
#> 

A teraz masz pełny dostęp do interfejsów API projektów VS. Uwaga: przy takim podejściu utracisz możliwość wykonywania szablonu poza programem Visual Studio.


Oto pełna przykładów, jak dodać plik obok szablonu:

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

oto wynik w Solution Explorer:

result