2017-09-29 3 views
17

J'ai développé un générateur de code à usage interne où les ressources de code (POCO) sont générées à partir d'interfaces C#. Le processus de génération de code ajoute/supprime par programme des éléments dans le fichier csproj. Le flux de travail est le suivant: un développeur ajoute une nouvelle interface C# ou supprime une interface C# existante dans Visual Studio 2017. Si le développeur enregistre d'abord le fichier projet puis exécute le générateur de code, tout fonctionne comme prévu. Les éléments générés par le code sont ajoutés au projet (ou supprimés) et Visual Studio reflète ces modifications en conséquence. Toutefois, si le développeur ne sauvegarde pas le fichier csproj avant d'exécuter le générateur de code et a supprimé une interface C#, les ressources générées par le code ne sont pas supprimées du projet car Visual Studio n'accepte pas les modifications du fichier csproj.Comment faire pour que Visual Studio 2017 accepte les modifications apportées au fichier csproj

À l'intérieur du générateur de code, je supprime physiquement les références aux fichiers générés par code qui sont supprimés et j'enregistre le fichier csproj. Je vérifie que les fichiers référencés sont supprimés du fichier csproj en ouvrant le csproj dans le bloc-notes. Cependant, dès que je mets Visual Studio en évidence, Visual Studio reconnaît que le fichier csproj a changé et demande si je veux supprimer, écraser, enregistrer sous, etc et les modifications apportées au fichier csproj de mon processus de génération de code sont perdues. Visual Studio ajoute les références aux fichiers supprimés dans le fichier csproj. J'ai essayé de supprimer, écraser, enregistrer sous, etc et je ne reçois pas Visual Studio pour accepter le fichier csproj nouvellement modifié (qui a supprimé les références aux fichiers supprimés).

Voici mon code pour la suppression des actifs générés Code-:

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

Est-il possible d'avoir Visual Studio juste accepter le nouveau fichier csproj? Y a-t-il un réglage que je dois faire dans le fichier csproj lors de la suppression des ressources? Le fait que Visual Studio soit altéré par le fichier csproj modifié entrave l'utilité du générateur de code pour supprimer les ressources générées par le code qui ne sont plus nécessaires (résultant de la suppression physique d'un fichier d'interface C#).

EDIT

Voici une vidéo montrant le processus de fonctionnement du générateur de T4 dans Visual Studio actifs générant C# basé sur une interface C#. Je supprime l'interface C# source, ré-exécute le générateur de code et le fichier de projet est mis à jour en conséquence, ce qui provoque le rechargement du projet.

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

Le problème est que le projet ne se rechargées. Le problème concerne les mises à jour du générateur de code et enregistre le fichier csproj en dehors de Visual Studio, ce qui provoque la confusion de Visual Studio car le fichier csproj a été modifié. Comment obtenez-vous Visual Studio pour «silencieusement» accepter les modifications enregistrées dans le fichier csproj?

Merci pour votre aide.

+0

vous utilisez le générateur de code à partir d'une cible msbuild pendant la construction ou en dehors du processus de construction en tant qu'exe/outil autonome? la plupart des approches de génération s'intègrent dans msbuild afin qu'elles puissent ajouter dynamiquement du code même pendant les compilations au moment du design dans VS sans avoir besoin d'éditer le fichier projet –

+0

Donc, fondamentalement, le premier appel à project.Save() n'a aucun effet dans VS? – stijn

+0

C'est un générateur de code autonome écrit avec des fichiers T4. Je ne sais pas comment intégrer T4 avec MS Build. Le premier project.save() ne semble pas affecter le studio visuel. La deuxième sauvegarde se produit après la suppression des ressources de code obsolètes et VS est invité à recharger. Mais il semble que VS a sa propre version du projet en mémoire, ce qui est différent du fichier csproj qui a été chargé/modifié/sauvegardé dans la portée du générateur de code. –

Répondre

2

Modifier le fichier de projet tout seul pendant qu'il est chargé dans Visual Studio n'est pas une bonne idée. Même si vous trouvez un moyen de le forcer à recharger le projet, il devra encore le recharger, ce qui est une nuisance majeure.

Il est préférable d'accéder au EnvDTE à partir d'un modèle T4 et de modifier le fichier du projet. Cet objet vous donne accès au modèle de projet de Visual Studio. Notez que l'utilisateur devra toujours enregistrer le fichier de projet modifié par défaut, car il sera considéré comme sale par VS, mais ce comportement est cohérent avec toutes les autres modifications de fichier de projet que vous pouvez effectuer via VS. Vous pouvez forcer VS à enregistrer le projet si vous en avez vraiment besoin.

Voici ce que vous devez faire pour accéder à VS, comme documented here:

Définissez l'attribut hostspecific à true:

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

importation EnvDTE:

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

Obtenez l'objet dte:

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

Et maintenant vous avez un accès complet aux API de projet de VS. Attention, avec cette approche, vous perdez la possibilité d'exécuter le modèle en dehors de Visual Studio.


Voici un exemple complet de la façon d'ajouter un fichier à côté de votre modèle:

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

Voici le résultat dans l'explorateur de solution:

result