2015-02-08 13 views
6

Mi piacerebbe utilizzare Roslyn per analizzare le informazioni semantiche nel contesto di un blocco di codice C# all'interno di una Razor View.Ottenere un SemanticModel di un file cshtml?

Esiste un modo (all'interno di Visual Studio 2015 o anche in un test di unità) per ottenere lo SemanticModel che rappresenta questo codice?

+0

quale sia lo scopo di analizzare il SemanticModel? –

+0

@ErikPhilips Sto creando [un'estensione di Visual Studio] (http: //www.oz-code.com) che deve essere in grado di porre domande semantiche (chiamate 'GetSymbolInfo',' GetTypeInfo' etc) dato il contesto della prossima istruzione (la 'linea gialla') nel debugger. Attualmente, per Razor Views, non sono stato in grado di farlo. –

+0

@OmerRaviv Forse la tua domanda è più vicina a * Come ottengo il 'SemanticModel' del documento corrente? * In alternativa, hai bisogno dell'estensione di Visual Studio? Non puoi aggiungere il tuo codice come analizzatore Roslyn? Per esempio. se ti interessano determinati tipi di violazioni di stile, puoi applicarlo efficacemente attraverso un analizzatore anziché un'estensione. E a quel punto, non è necessario avere un'estensione. –

risposta

5

I file rasoio contengono un buffer di proiezione C# con il codice C# generato (comprese le parti che non si scrive da soli). Questo buffer ha i servizi completi di Roslyn ed è esattamente quello che stai cercando.

È necessario scorrere il BufferGraph di TextView e trovare il buffer CSharp; è quindi possibile ottenere il suo modello Document e semantico.

Se si sta iniziando dalla posizione del cursore, è necessario semplicemente associare tale posizione a un buffer CSharp.

Si noti che è perfettamente legale per un TextView contenere piùCSharp buffer. (Anche se l'editor Razor non potrà mai farlo)


Se non si lavora in un TextView, è necessario fare tutto questo da soli; è necessario eseguire la sorgente Razor tramite il compilatore Razor per ottenere il sorgente C# generato, quindi compilare quello con Roslyn per ottenere un modello semantico.

6

Estrarre il codice che rappresenta la vista dal file di visualizzazione Razor utilizzando RazorTemplateEngine.GenerateCode e CSharpCodeProvider.GenerateCodeFromCompileUnit (oppure lo VBCodeProvider se si desidera che la fonte intermedia sia VB.NET). È quindi possibile utilizzare Roslyn per analizzare il codice.

C'è un esempio di utilizzo di Roslyn con file di visualizzazione Razor here.

Si precisa che per GenerateCode porta un avvertimento:

Questo tipo/membro supporta l'infrastruttura .NET Framework e non deve essere utilizzata direttamente dal codice.

+0

Vi sono ulteriori informazioni su Roslyn e ASP.NET [qui] (http://blogs.msdn.com/b/webdev/archive/2014/05/12/enabling-the-net-compiler-platform-roslyn- in-asp-net-applications.aspx) –

+0

Buone informazioni: ha funzionato bene per me! –

1

Roslyn modella solo i file cshtml mentre sono aperti, ma durante questo periodo sono simili a tutti gli altri file di origine nel modello Workspace.

C'è qualcosa di specifico che hai provato che non funziona?

2

Nel caso in cui qualcun altro resti bloccato su questo, ho mini app di esempio che può aiutare.

ho avuto una classe di CMS come questo:

public partial class CMS 
{ 
    public static string SomeKey 
    { 
     get { return (string) ResourceProvider.GetResource("some_key"); } 
    } 

    // ... and many more ... 
} 

... e volevo scoprire quali di questi sono stati utilizzati in tutta la mia soluzione per un rapporto ... Inserisci Roslyn!

La seguente applicazione stamperà il conteggio per i riferimenti utilizzati e non utilizzati:

using Microsoft.CodeAnalysis; 
using Microsoft.CodeAnalysis.FindSymbols; 
using Microsoft.CodeAnalysis.MSBuild; 
using Microsoft.CSharp; 
using System; 
using System.CodeDom.Compiler; 
using System.Collections.Generic; 
using System.IO; 
using System.Linq; 
using System.Text; 
using System.Threading.Tasks; 
using System.Web.Razor; 

namespace TranslationSniffer 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      new Program().Go().Wait(); 
     }   

     public async Task Go() 
     { 
      // Roslyn! 
      var ws = MSBuildWorkspace.Create(); 

      // Store the translation keys... 
      List<string> used = new List<string>(); 
      List<string> delete = new List<string>(); 

      string solutionRoot = @"C:\_Code\PathToProject\"; 
      string sln = solutionRoot + "MySolution.sln"; 

      // Load the solution, and find all the cshtml Razor views... 
      var solution = await ws.OpenSolutionAsync(sln); 
      var mainProj = solution.Projects.Where(x => x.Name == "ConsumerWeb").Single(); 
      FileInfo[] cshtmls = new DirectoryInfo(solutionRoot).GetFiles("*.cshtml", SearchOption.AllDirectories); 

      // Go through each Razor View - generate the equivalent CS and add to the project for compilation. 
      var host = new RazorEngineHost(RazorCodeLanguage.Languages["cshtml"]); 
      var razor = new RazorTemplateEngine(host); 
      var cs = new CSharpCodeProvider(); 
      var csOptions = new CodeGeneratorOptions(); 
      foreach (var cshtml in cshtmls) 
      { 
       using (StreamReader re = new StreamReader(cshtml.FullName)) 
       { 
        try 
        { 
         // Let Razor do it's thang... 
         var compileUnit = razor.GenerateCode(re).GeneratedCode; 

         // Pull the code into a stringbuilder, and append to the main project: 
         StringBuilder sb = new StringBuilder(); 
         using (StringWriter rw = new StringWriter(sb)) 
         { 
          cs.GenerateCodeFromCompileUnit(compileUnit, rw, csOptions); 
         } 

         // Get the new immutable project 
         var doc = mainProj.AddDocument(cshtml.Name + ".cs", sb.ToString()); 
         mainProj = doc.Project; 
        } 
        catch(Exception ex) 
        { 
         Console.WriteLine("Compile fail for: {0}", cshtml.Name); 
         // throw; 
        } 

        continue; 
       } 
      } 

      // We now have a new immutable solution, as we have changed the project instance... 
      solution = mainProj.Solution; 

      // Pull out our application translation list (its in a static class called 'CMS'): 
      var mainCompile = await mainProj.GetCompilationAsync(); 
      var mainModel = mainCompile.GetTypeByMetadataName("Resources.CMS"); 
      var translations = mainModel.GetMembers().Where(x => x.Kind == SymbolKind.Property).ToList(); 

      foreach (var translation in translations) 
      { 
       var references = await SymbolFinder.FindReferencesAsync(translation, solution)     ; 

       if (!references.First().Locations.Any()) 
       { 
        Console.WriteLine("{0} translation is not used!", translation.Name); 
        delete.Add(translation.Name); 
       } 
       else 
       { 
        Console.WriteLine("{0} :in: {1}", translation.Name, references.First().Locations.First().Document.Name); 
        used.Add(translation.Name); 
       } 
      } 

      Console.WriteLine(); 
      Console.WriteLine("Used references {0}. Unused references: {1}", used.Count, delete.Count); 

      return; 
     } 
    } 
} 
Problemi correlati