我想使用Roslyn來分析Razor視圖內的C#代碼塊內的語義信息。獲取cshtml文件的語義模型?
是否有任何方法(在Visual Studio 2015中,甚至在單元測試中)獲取表示此代碼的SemanticModel
?
我想使用Roslyn來分析Razor視圖內的C#代碼塊內的語義信息。獲取cshtml文件的語義模型?
是否有任何方法(在Visual Studio 2015中,甚至在單元測試中)獲取表示此代碼的SemanticModel
?
Razor文件包含一個帶有生成的C#代碼的C#投影緩衝區(包括您自己不寫的部分)。該緩衝區具有完整的Roslyn服務,正是您所需要的。
您需要遍歷TextView的BufferGraph並找到CSharp
緩衝區;然後您可以獲得它的Document
和語義模型。
如果您從光標位置開始,只需將該位置映射到CSharp
緩衝區。
請注意,對於包含多個緩衝區的多個CSharp
TextView是完全合法的。 (雖然剃刀編輯器將永遠不會做到這一點)
如果您不是在一個TextView工作,你需要做的這一切自己;您需要通過Razor編譯器運行Razor源代碼以獲取生成的C#源代碼,然後使用Roslyn編譯該代碼以獲取語義模型。
使用RazorTemplateEngine.GenerateCode
和CSharpCodeProvider.GenerateCodeFromCompileUnit
(或如果要將中間源作爲VB.NET)提取表示來自Razor視圖文件的視圖的代碼(或者如果要將中間源視爲VB.NET,請提取VBCodeProvider
)。然後你可以使用Roslyn來解析代碼。
有一個使用Roslyn和Razor查看文件here的示例。
注意到,GenerateCode
攜帶一個警告:
此類型/成員支持.NET Framework基礎結構,不適合直接在代碼中使用。
還有一些關於Roslyn和ASP.NET的信息[here](http://blogs.msdn.com/b/webdev/archive/2014/05/12/enabling-the-net-compiler-platform-roslyn- in-asp-net-applications.aspx) – 2015-02-08 09:00:26
良好的信息 - 爲我工作得很好! – 2015-06-08 14:24:24
Roslyn只在cshtml文件打開時建模,但在此期間它們與Workspace
模型中的其他源文件相似。
有沒有具體的事情你試過了,不行?
爲了防止其他人被困在此,我有迷你示例應用程序可能會有所幫助。
我有一個CMS類是這樣的:
public partial class CMS
{
public static string SomeKey
{
get { return (string) ResourceProvider.GetResource("some_key"); }
}
// ... and many more ...
}
...我想找出其中的這些都是在我的解決方案用於報告......進入羅斯林!
下面的程序將打印出計數的使用和未使用的引用:
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;
}
}
}
什麼分析SemanticModel的目的是什麼? – 2015-02-08 08:46:46
@ErikPhilips我正在創作[Visual Studio擴展](http://www.oz-code。com)需要能夠在調試器中給出語義問題(調用'GetSymbolInfo','GetTypeInfo'等)給出下一個語句('黃線')的上下文。目前,對於Razor Views而言,我一直無法做到這一點。 – 2015-02-08 12:02:32
@OmerRaviv或許你的問題更接近*我如何獲得當前文檔的'SemanticModel'?*或者,你是否需要Visual Studio擴展?你不能將你的代碼添加爲Roslyn分析器嗎?例如。如果您關心某些類型的風格違規,則可以通過分析器而不是擴展來有效執行。在那個時候,擴展是不必要的。 – 2015-02-08 12:32:48