As part of a Roslyn Analyzer (C#), I would like to access the intermediate model of Razor parser to check the content for project specific coding guidelines.
// Referenced NuGet:
// <PackageReference Include="Microsoft.AspNetCore.Mvc.Razor.Extensions" Version="6.0.36" />
public void Run()
{
// Create RazorEngine
string rootDirectoryPath = @"D:\Home-2025\RazorEnginePrototype";
var fileSystem = RazorProjectFileSystem.Create(rootDirectoryPath);
RazorConfiguration configuration = RazorConfiguration.Default;
var engine = RazorProjectEngine.Create(configuration, fileSystem, builder =>
{
// Microsoft.AspNetCore.Mvc.Razor.Extensions
RazorExtensions.Register(builder);
});
var razorEngine = engine.Engine;
// Parse razor file
string relativeFilePath = @"Views\StartPage.cshtml";
string filePath = Path.Combine(rootDirectoryPath, relativeFilePath);
string razorContent = File.ReadAllText(filePath);
var projectItem = fileSystem.GetItem(relativeFilePath);
RazorSourceDocumentProperties properties = new RazorSourceDocumentProperties(filePath, relativeFilePath);
var sourceDocument = RazorSourceDocument.Create(razorContent, properties);
var codeDocument = RazorCodeDocument.Create(sourceDocument);
razorEngine.Process(codeDocument);
// Try access intermediate node => mt is "dynamic"
DocumentIntermediateNode documentNode = codeDocument.GetDocumentIntermediateNode();
var mt = ModelDirective.GetModelType(documentNode);
// Returns generated C# code
var csDoc = codeDocument.GetCSharpDocument();
Console.WriteLine(csDoc.GeneratedCode);
}
The content of the StartPage.cshtml
:
@model YourNamespace.YourModelClass
<h1>@Model.Property</h1>
The code above generates this C# representation of StartPage.cshtml
in csDoc.GeneratedCode
(this means that the code above runs the whole Razor-to-C# compilation process, also YourNamespace.YourModelClass
is part of the generated code):
#pragma checksum "D:\Home-2025\RazorEnginePrototype\Views\StartPage.cshtml" "{ff1816ec-aa5e-4d10-87f7-6f4963833460}" "3ccef06a8a2d010b1de36900769176e1a7c1d2c4"
// <auto-generated/>
#pragma warning disable 1591
[assembly: global::Microsoft.AspNetCore.Razor.Hosting.RazorCompiledItemAttribute(typeof(AspNetCore.Views_StartPage), @"mvc.1.0.view", @"/Views/StartPage.cshtml")]
namespace AspNetCore
{
#line hidden
[global::Microsoft.AspNetCore.Razor.Hosting.RazorSourceChecksumAttribute(@"SHA1", @"3ccef06a8a2d010b1de36900769176e1a7c1d2c4", @"/Views/StartPage.cshtml")]
[global::Microsoft.AspNetCore.Razor.Hosting.RazorCompiledItemMetadataAttribute("Identifier", "/Views/StartPage.cshtml")]
[global::System.Runtime.CompilerServices.CreateNewOnMetadataUpdateAttribute]
public class Views_StartPage : global::Microsoft.AspNetCore.Mvc.Razor.RazorPage<YourNamespace.YourModelClass>
{
#pragma warning disable 1998
public async override global::System.Threading.Tasks.Task ExecuteAsync()
{
WriteLiteral("\r\n<h1>");
#nullable restore
#line 3 "D:\Home-2025\RazorEnginePrototype\Views\StartPage.cshtml"
Write(Model.Property);
#line default
#line hidden
#nullable disable
WriteLiteral("</h1>\r\n");
}
#pragma warning restore 1998
}
}
#pragma warning restore 1591
Microsoft.AspNetCore.Mvc.Razor.Extensions
seems to provide access functions to the intermediate model.
The problem: I would like to access a near-razor-source based intermediate model (containing "@model" nodes or such). But ModelDirective.GetModelType
returns "dynamic" (no specific model type).
What am I doing wrong?
I also traversed the tree of IntermediateNode
to see if there is some 'model intermediate Node' as part of the tree:
DocumentIntermediateNode
RazorCompiledItemAttributeIntermediateNode
NamespaceDeclarationIntermediateNode
RazorSourceChecksumAttributeIntermediateNode
RazorCompiledItemMetadataAttributeIntermediateNode
CreateNewOnMetadataUpdateAttributeIntermediateNode
ClassDeclarationIntermediateNode
MethodDeclarationIntermediateNode
HtmlContentIntermediateNode
LazyIntermediateToken
LazyIntermediateToken
LazyIntermediateToken
CSharpExpressionIntermediateNode
LazyIntermediateToken
HtmlContentIntermediateNode
LazyIntermediateToken
LazyIntermediateToken
Does 'intermediate node' mean that razor parser transforms the intermediate model (replacing nodes by other nodes) instead of enriching it (adding new nodes)?