Skip to content

Commit 623e0c9

Browse files
authored
[Razor] Generate embedded ValidatableTypeAttribute inline during build for .razor files (#50083)
Part of dotnet/aspnetcore#63141 The SDK generates and includes a `Microsoft.Extensions.Validation.ValidatableTypeAttribute` when .razor files are part of the compilation.
1 parent 517ba16 commit 623e0c9

File tree

4 files changed

+125
-0
lines changed

4 files changed

+125
-0
lines changed

src/RazorSdk/Razor.slnf

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
"test\\Microsoft.NET.Sdk.BlazorWebAssembly.Tests\\Microsoft.NET.Sdk.BlazorWebAssembly.Tests.csproj",
1717
"test\\Microsoft.NET.Sdk.Razor.Tests\\Microsoft.NET.Sdk.Razor.Tests.csproj",
1818
"test\\Microsoft.NET.Sdk.Razor.Tool.Tests\\Microsoft.NET.Sdk.Razor.Tool.Tests.csproj",
19+
"test\\Microsoft.NET.Sdk.StaticWebAssets.Tests\\Microsoft.NET.Sdk.StaticWebAssets.Tests.csproj",
1920
"test\\Microsoft.NET.TestFramework\\Microsoft.NET.TestFramework.csproj"
2021
]
2122
}

src/RazorSdk/Targets/Sdk.Razor.CurrentVersion.targets

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -587,6 +587,84 @@ Copyright (c) .NET Foundation. All rights reserved.
587587
</ItemGroup>
588588
</Target>
589589

590+
<!--
591+
Target to detect presence of .razor files and set related properties for embedded ValidatableTypeAttribute support
592+
This target runs after component items are resolved to ensure accurate detection
593+
-->
594+
<Target Name="_AddEmbeddedValidationAttribute" BeforeTargets="GenerateGlobalUsings" DependsOnTargets="ResolveRazorComponentInputs;ResolveRazorGenerateInputs">
595+
<PropertyGroup Condition="'$(_TargetingNET100OrLater)' == 'true'">
596+
<_HasRazorFiles Condition="'@(RazorComponent)' != '' OR '@(RazorGenerate->WithMetadataValue('Extension', '.razor'))' != '' OR '@(Content->WithMetadataValue('Extension', '.razor'))' != ''">true</_HasRazorFiles>
597+
598+
<!-- Set embedded attribute generation property based on .razor file detection -->
599+
<GenerateEmbeddedValidatableTypeAttribute Condition="'$(GenerateEmbeddedValidatableTypeAttribute)' == '' AND '$(_HasRazorFiles)' == 'true'">true</GenerateEmbeddedValidatableTypeAttribute>
600+
<GenerateEmbeddedValidatableTypeAttribute Condition="'$(GenerateEmbeddedValidatableTypeAttribute)' == ''">false</GenerateEmbeddedValidatableTypeAttribute>
601+
602+
<!-- Set global using property based on attribute generation -->
603+
<IncludeEmbeddedValidationGlobalUsing Condition="'$(IncludeEmbeddedValidationGlobalUsing)' == '' AND '$(GenerateEmbeddedValidatableTypeAttribute)' == 'true'">true</IncludeEmbeddedValidationGlobalUsing>
604+
<IncludeEmbeddedValidationGlobalUsing Condition="'$(IncludeEmbeddedValidationGlobalUsing)' == ''">false</IncludeEmbeddedValidationGlobalUsing>
605+
</PropertyGroup>
606+
607+
<PropertyGroup Condition="'$(GenerateEmbeddedValidatableTypeAttribute)' == 'true' AND '$(_TargetingNET100OrLater)' == 'true'">
608+
<_EmbeddedAttributeFile>$(IntermediateOutputPath)EmbeddedAttribute.cs</_EmbeddedAttributeFile>
609+
<_ValidatableTypeAttributeFile>$(IntermediateOutputPath)ValidatableTypeAttribute.cs</_ValidatableTypeAttributeFile>
610+
</PropertyGroup>
611+
612+
<!-- Generate EmbeddedAttribute source file -->
613+
<ItemGroup Condition="'$(GenerateEmbeddedValidatableTypeAttribute)' == 'true' AND '$(_TargetingNET100OrLater)' == 'true'">
614+
<_EmbeddedAttributeLines Include="// &lt;auto-generated/&gt;" />
615+
<_EmbeddedAttributeLines Include="namespace Microsoft.CodeAnalysis" />
616+
<_EmbeddedAttributeLines Include="{" />
617+
<_EmbeddedAttributeLines Include=" internal sealed partial class EmbeddedAttribute : global::System.Attribute" />
618+
<_EmbeddedAttributeLines Include=" {" />
619+
<_EmbeddedAttributeLines Include=" }" />
620+
<_EmbeddedAttributeLines Include="}" />
621+
</ItemGroup>
622+
623+
<WriteLinesToFile
624+
Condition="'$(GenerateEmbeddedValidatableTypeAttribute)' == 'true' AND '$(_TargetingNET100OrLater)' == 'true'"
625+
File="$(_EmbeddedAttributeFile)"
626+
Lines="@(_EmbeddedAttributeLines)"
627+
Overwrite="true"
628+
WriteOnlyWhenDifferent="true" />
629+
630+
<!-- Generate ValidatableTypeAttribute source file -->
631+
<ItemGroup Condition="'$(GenerateEmbeddedValidatableTypeAttribute)' == 'true' AND '$(_TargetingNET100OrLater)' == 'true'">
632+
<_ValidatableTypeAttributeLines Include="// &lt;auto-generated/&gt;" />
633+
<_ValidatableTypeAttributeLines Include="namespace Microsoft.Extensions.Validation.Embedded" />
634+
<_ValidatableTypeAttributeLines Include="{" />
635+
<_ValidatableTypeAttributeLines Include=" [global::Microsoft.CodeAnalysis.EmbeddedAttribute]" />
636+
<_ValidatableTypeAttributeLines Include=" [global::System.AttributeUsage(global::System.AttributeTargets.Class)]" />
637+
<_ValidatableTypeAttributeLines Include=" internal sealed class ValidatableTypeAttribute : global::System.Attribute" />
638+
<_ValidatableTypeAttributeLines Include=" {" />
639+
<_ValidatableTypeAttributeLines Include=" }" />
640+
<_ValidatableTypeAttributeLines Include="}" />
641+
</ItemGroup>
642+
643+
<WriteLinesToFile
644+
Condition="'$(GenerateEmbeddedValidatableTypeAttribute)' == 'true' AND '$(_TargetingNET100OrLater)' == 'true'"
645+
File="$(_ValidatableTypeAttributeFile)"
646+
Lines="@(_ValidatableTypeAttributeLines)"
647+
Overwrite="true"
648+
WriteOnlyWhenDifferent="true" />
649+
650+
<!--
651+
Conditional inclusion of embedded ValidatableTypeAttribute source files for .NET 10.0+
652+
-->
653+
<ItemGroup Condition="'$(GenerateEmbeddedValidatableTypeAttribute)' == 'true' AND '$(_TargetingNET100OrLater)' == 'true'">
654+
<Compile Include="$(_EmbeddedAttributeFile)" />
655+
<Compile Include="$(_ValidatableTypeAttributeFile)" />
656+
<FileWrites Include="$(_EmbeddedAttributeFile)" />
657+
<FileWrites Include="$(_ValidatableTypeAttributeFile)" />
658+
</ItemGroup>
659+
660+
<!--
661+
Conditional global using for embedded validation namespace
662+
-->
663+
<ItemGroup Condition="'$(IncludeEmbeddedValidationGlobalUsing)' == 'true' AND '$(_TargetingNET100OrLater)' == 'true'">
664+
<Using Include="Microsoft.Extensions.Validation.Embedded" />
665+
</ItemGroup>
666+
</Target>
667+
590668
<!-- This target validates that there is at most one scoped css file per component, that there are no scoped css files without a
591669
matching component, and then adds the associated scope to the razor components that have a matching scoped css file.
592670
-->

test/Microsoft.NET.Sdk.Razor.Tests/BuildWithComponentsIntegrationTest.cs

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,5 +57,48 @@ private void Build_ComponentsWorks([CallerMemberName] string callerName = "")
5757
// Views should also appear in the app assembly.
5858
new FileInfo(Path.Combine(outputPath, "MvcWithComponents.dll")).AssemblyShould().ContainType("AspNetCoreGeneratedDocument.Views_Home_Index");
5959
}
60+
61+
[Fact]
62+
public void Build_ComponentApp_IncludesEmbeddedValidatableTypeAttributeForNet100()
63+
{
64+
var testAsset = "RazorComponentApp";
65+
var projectDirectory = CreateAspNetSdkTestAsset(testAsset);
66+
67+
// Add a target to introspect the properties and compile items
68+
var projectFile = Path.Combine(projectDirectory.Path, "ComponentApp.csproj");
69+
var originalContent = File.ReadAllText(projectFile);
70+
var modifiedContent = originalContent.Replace("</Project>",
71+
@" <Target Name=""_IntrospectValidatableTypeAttribute"" BeforeTargets=""Build"">
72+
<Message Text=""_HasRazorFiles: $(_HasRazorFiles)"" Importance=""High"" />
73+
<Message Text=""GenerateEmbeddedValidatableTypeAttribute: $(GenerateEmbeddedValidatableTypeAttribute)"" Importance=""High"" />
74+
<Message Text=""IncludeEmbeddedValidationGlobalUsing: $(IncludeEmbeddedValidationGlobalUsing)"" Importance=""High"" />
75+
<Message Text=""_TargetingNET100OrLater: $(_TargetingNET100OrLater)"" Importance=""High"" />
76+
<Message Text=""Compile items containing ValidatableTypeAttribute: @(Compile->WithMetadataValue('Filename', 'ValidatableTypeAttribute'))"" Importance=""High"" />
77+
<Message Text=""Compile items containing EmbeddedAttribute: @(Compile->WithMetadataValue('Filename', 'EmbeddedAttribute'))"" Importance=""High"" />
78+
<Message Text=""Using items: @(Using)"" Importance=""High"" />
79+
</Target>
80+
</Project>");
81+
82+
File.WriteAllText(projectFile, modifiedContent);
83+
84+
// Build with .NET 10.0 target framework
85+
var build = new BuildCommand(projectDirectory);
86+
var result = build.Execute($"/p:TargetFramework=net10.0");
87+
88+
result.Should().Pass();
89+
90+
// Check that the properties were set correctly
91+
result.Should().HaveStdOutContaining("_HasRazorFiles: true");
92+
result.Should().HaveStdOutContaining("GenerateEmbeddedValidatableTypeAttribute: true");
93+
result.Should().HaveStdOutContaining("IncludeEmbeddedValidationGlobalUsing: true");
94+
result.Should().HaveStdOutContaining("_TargetingNET100OrLater: true");
95+
96+
// Check that the source files were included
97+
result.Should().HaveStdOutContaining("ValidatableTypeAttribute");
98+
result.Should().HaveStdOutContaining("EmbeddedAttribute");
99+
100+
// Check that the global using was included
101+
result.Should().HaveStdOutContaining("Microsoft.Extensions.Validation.Embedded");
102+
}
60103
}
61104
}

test/dotnet-watch.Tests/Build/EvaluationTests.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,7 +212,10 @@ await VerifyEvaluation(testAsset,
212212
new("Project1/wwwroot/js/site.js", "wwwroot/js/site.js"),
213213
new("RCL/Code.cs"),
214214
new($"RCL/obj/Debug/{ToolsetInfo.CurrentTargetFramework}/{ToolsetInfo.CurrentTargetFrameworkMoniker}.AssemblyAttributes.cs", graphOnly: true),
215+
new($"RCL/obj/Debug/{ToolsetInfo.CurrentTargetFramework}/EmbeddedAttribute.cs", graphOnly: true),
215216
new($"RCL/obj/Debug/{ToolsetInfo.CurrentTargetFramework}/RCL.AssemblyInfo.cs", graphOnly: true),
217+
new($"RCL/obj/Debug/{ToolsetInfo.CurrentTargetFramework}/RCL.GlobalUsings.g.cs", graphOnly: true),
218+
new($"RCL/obj/Debug/{ToolsetInfo.CurrentTargetFramework}/ValidatableTypeAttribute.cs", graphOnly: true),
216219
new("RCL/Page1.razor"),
217220
new("RCL/Page1.razor.css"),
218221
new("RCL/Page2.cshtml"),

0 commit comments

Comments
 (0)