获取cshtml文件的SemanticModel?

时间:2015-02-08 08:37:33

标签: c# roslyn vsx

我想使用Roslyn在Razor视图中的一组C#代码的上下文中分析语义信息。

有没有办法(在Visual Studio 2015中,甚至在单元测试中)获取代表此代码的SemanticModel

4 个答案:

答案 0 :(得分:7)

使用RazorTemplateEngine.GenerateCodeCSharpCodeProvider.GenerateCodeFromCompileUnit(或VBCodeProvider,如果您希望中间源为VB.NET),从Razor视图文件中提取代表视图的代码。然后,您可以使用Roslyn来解析代码。

有一个将Roslyn与Razor视图文件here一起使用的示例。

请注意GenerateCode有警告:

  

此类型/成员支持.NET Framework基础结构,不能直接在您的代码中使用。

答案 1 :(得分:6)

Razor文件包含一个带有生成的C#代码的C#投影缓冲区(包括您自己不写的部分)。此缓冲区具有完整的Roslyn服务,正是您正在寻找的。

您需要遍历TextView的BufferGraph并找到CSharp缓冲区;然后,您可以获得其Document和语义模型。

如果您从光标位置开始,则只需将该位置映射到CSharp缓冲区。

请注意,TextView包含多个 CSharp缓冲区是完全合法的。 (虽然Razor编辑器永远不会这样做)


如果您不在TextView中工作,则需要自己完成所有这些工作;你需要通过Razor编译器运行Razor源来获取生成的C#源代码,然后用Roslyn编译它以获得语义模型。

答案 2 :(得分:3)

万一其他人都被困在这个问题上,我有一个迷你示例应用程序可能会有所帮助。

我有一个这样的CMS类:

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

答案 3 :(得分:1)

Roslyn仅在cshtml文件打开时对其进行建模,但在此期间它们与Workspace模型中的所有其他源文件类似。

你有没有尝试过哪些特定的功能?