Skip to content

Commit ef0c2fc

Browse files
authored
Suppress generating source checksums when configured (#31089)
Hot reload / EnC does not like it when type level attributes are modified. Razor views and Pages include a RazorSourceChecksumAttribute that includes a checksum of all of it's inputs (current cshtml file, and all _ViewImports that contribute to it). It's used used by runtime compilation to tell if the compiled view is current compared to it's inputs. However, it gets in the way with enc since editing a file updates the checksum. We'll disable this feature by default in RazorSourceGenerator, and enable it using an MSBuild switch that's configured by runtime compilation
1 parent fce37c1 commit ef0c2fc

File tree

7 files changed

+88
-4
lines changed

7 files changed

+88
-4
lines changed

src/Mvc/Mvc.Razor.RuntimeCompilation/src/targets/Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation.targets

+3
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,8 @@
1010
<RazorUpToDateReloadFileTypes>$(RazorUpToDateReloadFileTypes.Replace('.cshtml', ''))</RazorUpToDateReloadFileTypes>
1111

1212
<AddCshtmlFilesToDotNetWatchList>false</AddCshtmlFilesToDotNetWatchList>
13+
14+
<!-- Generate checksum attributes used to determine if a compiled view is out-of-sync with any of it's inputs -->
15+
<GenerateRazorMetadataSourceChecksumAttributes>true</GenerateRazorMetadataSourceChecksumAttributes>
1316
</PropertyGroup>
1417
</Project>

src/Razor/Microsoft.AspNetCore.Razor.Language/src/AllowedChildTagDescriptor.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright (c) .NET Foundation. All rights reserved.
1+
// Copyright (c) .NET Foundation. All rights reserved.
22
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
33

44
using System;

src/Razor/Microsoft.AspNetCore.Razor.Language/src/DefaultRazorCodeGenerationOptionsBuilder.cs

+4-1
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,10 @@ public override RazorCodeGenerationOptions Build()
5252
SuppressMetadataAttributes,
5353
SuppressPrimaryMethodBody,
5454
SuppressNullabilityEnforcement,
55-
OmitMinimizedComponentAttributeValues);
55+
OmitMinimizedComponentAttributeValues)
56+
{
57+
SuppressMetadataSourceChecksumAttributes = SuppressMetadataSourceChecksumAttributes,
58+
};
5659
}
5760

5861
public override void SetDesignTime(bool designTime)

src/Razor/Microsoft.AspNetCore.Razor.Language/src/Extensions/MetadataAttributePass.cs

+7-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright (c) .NET Foundation. All rights reserved.
1+
// Copyright (c) .NET Foundation. All rights reserved.
22
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
33

44
using System;
@@ -92,6 +92,12 @@ protected override void ExecuteCore(RazorCodeDocument codeDocument, DocumentInte
9292
return;
9393
}
9494

95+
if (documentNode.Options.SuppressMetadataSourceChecksumAttributes)
96+
{
97+
// Checksum attributes are turned off (or options not populated), nothing to do.
98+
return;
99+
}
100+
95101
// Checksum of the main source
96102
var checksum = codeDocument.Source.GetChecksum();
97103
var checksumAlgorithm = codeDocument.Source.GetChecksumAlgorithm();

src/Razor/Microsoft.AspNetCore.Razor.Language/src/RazorCodeGenerationOptions.cs

+9
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,15 @@ public static RazorCodeGenerationOptions CreateDesignTime(Action<RazorCodeGenera
107107
/// </remarks>
108108
public virtual bool SuppressMetadataAttributes { get; protected set; }
109109

110+
/// <summary>
111+
/// Gets a value that indicates whether to suppress the <c>RazorSourceChecksumAttribute</c>.
112+
/// <para>
113+
/// Used by default in .NET 6 apps since including a type-level attribute that changes on every
114+
/// edit are treated as rude edits by hot reload.
115+
/// </para>
116+
/// </summary>
117+
internal bool SuppressMetadataSourceChecksumAttributes { get; set; }
118+
110119
/// <summary>
111120
/// Gets or sets a value that determines if an empty body is generated for the primary method.
112121
/// </summary>

src/Razor/Microsoft.AspNetCore.Razor.Language/src/RazorCodeGenerationOptionsBuilder.cs

+9
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,15 @@ public abstract class RazorCodeGenerationOptionsBuilder
4949
/// </remarks>
5050
public virtual bool SuppressMetadataAttributes { get; set; }
5151

52+
/// <summary>
53+
/// Gets a value that indicates whether to suppress the <c>RazorSourceChecksumAttribute</c>.
54+
/// <para>
55+
/// Used by default in .NET 6 apps since including a type-level attribute that changes on every
56+
/// edit are treated as rude edits by hot reload.
57+
/// </para>
58+
/// </summary>
59+
internal bool SuppressMetadataSourceChecksumAttributes { get; set; }
60+
5261
/// <summary>
5362
/// Gets or sets a value that determines if an empty body is generated for the primary method.
5463
/// </summary>

src/Razor/Microsoft.AspNetCore.Razor.Language/test/Extensions/MetadataAttributePassTest.cs

+55-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright (c) .NET Foundation. All rights reserved.
1+
// Copyright (c) .NET Foundation. All rights reserved.
22
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
33

44
using Microsoft.AspNetCore.Razor.Language.Components;
@@ -381,6 +381,60 @@ public void Execute_HasRequiredInfo_AndImport_AddsItemAndSourceChecksum()
381381
Assert.Equal("/Foo/Import.cshtml", checksum.Identifier);
382382
}
383383

384+
[Fact]
385+
public void Execute_SuppressMetadataSourceChecksumAttributes_DoesNotGenerateSourceChecksumAttributes()
386+
{
387+
// Arrange
388+
var engine = CreateEngine();
389+
var pass = new MetadataAttributePass()
390+
{
391+
Engine = engine,
392+
};
393+
394+
var sourceDocument = TestRazorSourceDocument.Create("", new RazorSourceDocumentProperties(null, "Foo\\Bar.cshtml"));
395+
var import = TestRazorSourceDocument.Create("@using System", new RazorSourceDocumentProperties(null, "Foo\\Import.cshtml"));
396+
var codeDocument = RazorCodeDocument.Create(sourceDocument, new[] { import, });
397+
398+
var irDocument = new DocumentIntermediateNode()
399+
{
400+
DocumentKind = "test",
401+
Options = RazorCodeGenerationOptions.Create(o => o.SuppressMetadataSourceChecksumAttributes = true),
402+
};
403+
var builder = IntermediateNodeBuilder.Create(irDocument);
404+
var @namespace = new NamespaceDeclarationIntermediateNode
405+
{
406+
Annotations =
407+
{
408+
[CommonAnnotations.PrimaryNamespace] = CommonAnnotations.PrimaryNamespace,
409+
},
410+
Content = "Some.Namespace"
411+
};
412+
builder.Push(@namespace);
413+
var @class = new ClassDeclarationIntermediateNode
414+
{
415+
Annotations =
416+
{
417+
[CommonAnnotations.PrimaryClass] = CommonAnnotations.PrimaryClass,
418+
},
419+
ClassName = "Test",
420+
};
421+
builder.Add(@class);
422+
423+
// Act
424+
pass.Execute(codeDocument, irDocument);
425+
426+
// Assert
427+
Assert.Equal(2, irDocument.Children.Count);
428+
429+
var item = Assert.IsType<RazorCompiledItemAttributeIntermediateNode>(irDocument.Children[0]);
430+
Assert.Equal("/Foo/Bar.cshtml", item.Identifier);
431+
Assert.Equal("test", item.Kind);
432+
Assert.Equal("Some.Namespace.Test", item.TypeName);
433+
434+
var child = Assert.Single(@namespace.Children);
435+
Assert.IsType<ClassDeclarationIntermediateNode>(child);
436+
}
437+
384438
private static RazorEngine CreateEngine()
385439
{
386440
return RazorProjectEngine.Create(b =>

0 commit comments

Comments
 (0)