获取 cshtml 文件的语义模型?
Getting a SemanticModel of a cshtml file?
我想使用 Roslyn 在 Razor 视图内的一段 C# 代码的上下文中分析语义信息。
有没有办法(在 Visual Studio 2015 年内,甚至在单元测试中)获得表示此代码的 SemanticModel
?
使用 RazorTemplateEngine.GenerateCode
and CSharpCodeProvider.GenerateCodeFromCompileUnit
从 Razor 视图文件中提取代表视图的代码(如果您希望中间源为 VB.NET,则使用 VBCodeProvider
)。然后您可以使用 Roslyn 来解析代码。
有一个将 Roslyn 与 Razor 视图文件结合使用的示例 here。
请注意 GenerateCode
有一个警告:
This type/member supports the .NET Framework infrastructure and is not intended to be used directly from your code.
Roslyn 仅在打开时对 cshtml 文件进行建模,但在此期间它们与 Workspace
模型中的所有其他源文件相似。
是否有您尝试过但不起作用的具体方法?
Razor 文件包含一个 C# 投影缓冲区和生成的 C# 代码(包括您不自己编写的部分)。此缓冲区具有完整的 Roslyn 服务,正是您要找的。
您需要遍历 TextView 的 BufferGraph 并找到 CSharp
缓冲区;然后你可以获得它的 Document
和语义模型。
如果您从光标位置开始,您只需将该位置映射到 CSharp
缓冲区。
请注意,TextView 包含多个 CSharp
缓冲区是完全合法的。 (虽然 Razor 编辑器永远不会那样做)
如果您不在 TextView 中工作,则需要自己完成所有这些工作;您需要通过 Razor 编译器 运行 Razor 源代码以获取生成的 C# 源代码,然后使用 Roslyn 对其进行编译以获得语义模型。
以防万一其他人遇到困难,我有迷你示例应用程序可能会有所帮助。
我有一个这样的 CMS class:
public partial class CMS
{
public static string SomeKey
{
get { return (string) ResourceProvider.GetResource("some_key"); }
}
// ... and many more ...
}
...我想找出在我的报告解决方案中使用了哪些...输入 Roslyn!
以下应用程序将打印出已使用和未使用参考文献的计数:
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;
}
}
}
我想使用 Roslyn 在 Razor 视图内的一段 C# 代码的上下文中分析语义信息。
有没有办法(在 Visual Studio 2015 年内,甚至在单元测试中)获得表示此代码的 SemanticModel
?
使用 RazorTemplateEngine.GenerateCode
and CSharpCodeProvider.GenerateCodeFromCompileUnit
从 Razor 视图文件中提取代表视图的代码(如果您希望中间源为 VB.NET,则使用 VBCodeProvider
)。然后您可以使用 Roslyn 来解析代码。
有一个将 Roslyn 与 Razor 视图文件结合使用的示例 here。
请注意 GenerateCode
有一个警告:
This type/member supports the .NET Framework infrastructure and is not intended to be used directly from your code.
Roslyn 仅在打开时对 cshtml 文件进行建模,但在此期间它们与 Workspace
模型中的所有其他源文件相似。
是否有您尝试过但不起作用的具体方法?
Razor 文件包含一个 C# 投影缓冲区和生成的 C# 代码(包括您不自己编写的部分)。此缓冲区具有完整的 Roslyn 服务,正是您要找的。
您需要遍历 TextView 的 BufferGraph 并找到 CSharp
缓冲区;然后你可以获得它的 Document
和语义模型。
如果您从光标位置开始,您只需将该位置映射到 CSharp
缓冲区。
请注意,TextView 包含多个 CSharp
缓冲区是完全合法的。 (虽然 Razor 编辑器永远不会那样做)
如果您不在 TextView 中工作,则需要自己完成所有这些工作;您需要通过 Razor 编译器 运行 Razor 源代码以获取生成的 C# 源代码,然后使用 Roslyn 对其进行编译以获得语义模型。
以防万一其他人遇到困难,我有迷你示例应用程序可能会有所帮助。
我有一个这样的 CMS class:
public partial class CMS
{
public static string SomeKey
{
get { return (string) ResourceProvider.GetResource("some_key"); }
}
// ... and many more ...
}
...我想找出在我的报告解决方案中使用了哪些...输入 Roslyn!
以下应用程序将打印出已使用和未使用参考文献的计数:
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;
}
}
}