Skip to content

Commit 125f729

Browse files
committed
Update emitted code and trim tests build
1 parent e052658 commit 125f729

15 files changed

+2367
-2
lines changed

eng/RequiresDelayedBuildProjects.props

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,5 +28,7 @@
2828
<RequiresDelayedBuild Include="$(RepoRoot)src\ProjectTemplates\test\Templates.Tests\Templates.Tests.csproj" />
2929
<RequiresDelayedBuild Include="$(RepoRoot)src\SignalR\server\SignalR\test\Microsoft.AspNetCore.SignalR.TrimmingTests\Microsoft.AspNetCore.SignalR.TrimmingTests.proj" />
3030
<RequiresDelayedBuild Include="$(RepoRoot)eng\Npm.Workspace.FunctionalTests.nodeproj" />
31+
<RequiresDelayedBuild Include="$(RepoRoot)src\Validation\test\Microsoft.Extensions.Validation.NativeAotTests\Microsoft.Extensions.Validation.NativeAotTests.proj"/>
32+
<RequiresDelayedBuild Include="$(RepoRoot)src\Validation\test\Microsoft.Extensions.Validation.TrimmingTests\Microsoft.Extensions.Validation.TrimmingTests.proj"/>
3133
</ItemGroup>
3234
</Project>

eng/testing/linker/SupportFiles/Directory.Build.targets

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,8 @@
7676
<ProjectReference Include="$(RepoRoot)src\SignalR\clients\csharp\Client\src\Microsoft.AspNetCore.SignalR.Client.csproj" />
7777
<ProjectReference Include="$(RepoRoot)src\SignalR\server\SignalR\src\Microsoft.AspNetCore.SignalR.csproj" />
7878
<ProjectReference Include="$(RepoRoot)src\OpenApi\src\Microsoft.AspNetCore.OpenApi.csproj" />
79+
<ProjectReference Include="$(RepoRoot)src\Validation\src\Microsoft.Extensions.Validation.csproj" />
80+
<ProjectReference Include="$(RepoRoot)src\Validation\gen\Microsoft.Extensions.Validation.ValidationsGenerator.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
7981
<ProjectReference Include="$(RepoRoot)src\Http\Http.Extensions\gen\Microsoft.AspNetCore.Http.RequestDelegateGenerator\Microsoft.AspNetCore.Http.RequestDelegateGenerator.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
8082
</ItemGroup>
8183

eng/testing/linker/project.csproj.template

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
<!-- Workaround while there is no SDK available that understands the TFM; suppress unsupported version errors. -->
1313
<NETCoreAppMaximumVersion>99.9</NETCoreAppMaximumVersion>
1414
<_ExtraTrimmerArgs>{ExtraTrimmerArgs} $(_ExtraTrimmerArgs)</_ExtraTrimmerArgs>
15-
<InterceptorsPreviewNamespaces>$(InterceptorsPreviewNamespaces);Microsoft.AspNetCore.Http.Generated</InterceptorsPreviewNamespaces>
15+
<InterceptorsPreviewNamespaces>$(InterceptorsPreviewNamespaces);Microsoft.AspNetCore.Http.Generated;Microsoft.Extensions.Validation.Generated</InterceptorsPreviewNamespaces>
1616
<!-- Ensure individual warnings are shown when publishing -->
1717
<TrimmerSingleWarn>false</TrimmerSingleWarn>
1818
{AdditionalProperties}

src/Validation/gen/Emitters/ValidationsGenerator.Emitter.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,11 @@ file static class GeneratedServiceCollectionExtensions
123123
{{GeneratedCodeAttribute}}
124124
file static class ValidationAttributeCache
125125
{
126-
private sealed record CacheKey([property: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] global::System.Type ContainingType, string PropertyName);
126+
private sealed record CacheKey(
127+
[param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)]
128+
[property: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)]
129+
global::System.Type ContainingType,
130+
string PropertyName);
127131
private static readonly global::System.Collections.Concurrent.ConcurrentDictionary<CacheKey, global::System.ComponentModel.DataAnnotations.ValidationAttribute[]> _cache = new();
128132
129133
public static global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes(
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,242 @@
1+
//HintName: ValidatableInfoResolver.g.cs
2+
#nullable enable annotations
3+
//------------------------------------------------------------------------------
4+
// <auto-generated>
5+
// This code was generated by a tool.
6+
//
7+
// Changes to this file may cause incorrect behavior and will be lost if
8+
// the code is regenerated.
9+
// </auto-generated>
10+
//------------------------------------------------------------------------------
11+
#nullable enable
12+
#pragma warning disable ASP0029
13+
14+
namespace System.Runtime.CompilerServices
15+
{
16+
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")]
17+
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
18+
file sealed class InterceptsLocationAttribute : System.Attribute
19+
{
20+
public InterceptsLocationAttribute(int version, string data)
21+
{
22+
}
23+
}
24+
}
25+
26+
namespace Microsoft.Extensions.Validation.Generated
27+
{
28+
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")]
29+
file sealed class GeneratedValidatablePropertyInfo : global::Microsoft.Extensions.Validation.ValidatablePropertyInfo
30+
{
31+
public GeneratedValidatablePropertyInfo(
32+
[param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)]
33+
global::System.Type containingType,
34+
global::System.Type propertyType,
35+
string name,
36+
string displayName) : base(containingType, propertyType, name, displayName)
37+
{
38+
ContainingType = containingType;
39+
Name = name;
40+
}
41+
42+
[global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)]
43+
internal global::System.Type ContainingType { get; }
44+
internal string Name { get; }
45+
46+
protected override global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes()
47+
=> ValidationAttributeCache.GetValidationAttributes(ContainingType, Name);
48+
}
49+
50+
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")]
51+
file sealed class GeneratedValidatableTypeInfo : global::Microsoft.Extensions.Validation.ValidatableTypeInfo
52+
{
53+
public GeneratedValidatableTypeInfo(
54+
[param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.Interfaces)]
55+
global::System.Type type,
56+
ValidatablePropertyInfo[] members) : base(type, members) { }
57+
}
58+
59+
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")]
60+
file class GeneratedValidatableInfoResolver : global::Microsoft.Extensions.Validation.IValidatableInfoResolver
61+
{
62+
public bool TryGetValidatableTypeInfo(global::System.Type type, [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out global::Microsoft.Extensions.Validation.IValidatableInfo? validatableInfo)
63+
{
64+
validatableInfo = null;
65+
if (type == typeof(global::SubType))
66+
{
67+
validatableInfo = new GeneratedValidatableTypeInfo(
68+
type: typeof(global::SubType),
69+
members: [
70+
new GeneratedValidatablePropertyInfo(
71+
containingType: typeof(global::SubType),
72+
propertyType: typeof(string),
73+
name: "RequiredProperty",
74+
displayName: "RequiredProperty"
75+
),
76+
new GeneratedValidatablePropertyInfo(
77+
containingType: typeof(global::SubType),
78+
propertyType: typeof(string),
79+
name: "StringWithLength",
80+
displayName: "StringWithLength"
81+
),
82+
]
83+
);
84+
return true;
85+
}
86+
if (type == typeof(global::SubTypeWithInheritance))
87+
{
88+
validatableInfo = new GeneratedValidatableTypeInfo(
89+
type: typeof(global::SubTypeWithInheritance),
90+
members: [
91+
new GeneratedValidatablePropertyInfo(
92+
containingType: typeof(global::SubTypeWithInheritance),
93+
propertyType: typeof(string),
94+
name: "EmailString",
95+
displayName: "EmailString"
96+
),
97+
]
98+
);
99+
return true;
100+
}
101+
if (type == typeof(global::ComplexType))
102+
{
103+
validatableInfo = new GeneratedValidatableTypeInfo(
104+
type: typeof(global::ComplexType),
105+
members: [
106+
new GeneratedValidatablePropertyInfo(
107+
containingType: typeof(global::ComplexType),
108+
propertyType: typeof(int),
109+
name: "IntegerWithRange",
110+
displayName: "IntegerWithRange"
111+
),
112+
new GeneratedValidatablePropertyInfo(
113+
containingType: typeof(global::ComplexType),
114+
propertyType: typeof(int),
115+
name: "IntegerWithRangeAndDisplayName",
116+
displayName: "Valid identifier"
117+
),
118+
new GeneratedValidatablePropertyInfo(
119+
containingType: typeof(global::ComplexType),
120+
propertyType: typeof(global::SubType),
121+
name: "PropertyWithMemberAttributes",
122+
displayName: "PropertyWithMemberAttributes"
123+
),
124+
new GeneratedValidatablePropertyInfo(
125+
containingType: typeof(global::ComplexType),
126+
propertyType: typeof(global::SubType),
127+
name: "PropertyWithoutMemberAttributes",
128+
displayName: "PropertyWithoutMemberAttributes"
129+
),
130+
new GeneratedValidatablePropertyInfo(
131+
containingType: typeof(global::ComplexType),
132+
propertyType: typeof(global::SubTypeWithInheritance),
133+
name: "PropertyWithInheritance",
134+
displayName: "PropertyWithInheritance"
135+
),
136+
new GeneratedValidatablePropertyInfo(
137+
containingType: typeof(global::ComplexType),
138+
propertyType: typeof(global::System.Collections.Generic.List<global::SubType>),
139+
name: "ListOfSubTypes",
140+
displayName: "ListOfSubTypes"
141+
),
142+
new GeneratedValidatablePropertyInfo(
143+
containingType: typeof(global::ComplexType),
144+
propertyType: typeof(int),
145+
name: "IntegerWithCustomValidationAttribute",
146+
displayName: "IntegerWithCustomValidationAttribute"
147+
),
148+
new GeneratedValidatablePropertyInfo(
149+
containingType: typeof(global::ComplexType),
150+
propertyType: typeof(int),
151+
name: "PropertyWithMultipleAttributes",
152+
displayName: "PropertyWithMultipleAttributes"
153+
),
154+
]
155+
);
156+
return true;
157+
}
158+
159+
return false;
160+
}
161+
162+
// No-ops, rely on runtime code for ParameterInfo-based resolution
163+
public bool TryGetValidatableParameterInfo(global::System.Reflection.ParameterInfo parameterInfo, [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out global::Microsoft.Extensions.Validation.IValidatableInfo? validatableInfo)
164+
{
165+
validatableInfo = null;
166+
return false;
167+
}
168+
}
169+
170+
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")]
171+
file static class GeneratedServiceCollectionExtensions
172+
{
173+
[InterceptsLocation]
174+
public static global::Microsoft.Extensions.DependencyInjection.IServiceCollection AddValidation(this global::Microsoft.Extensions.DependencyInjection.IServiceCollection services, global::System.Action<global::Microsoft.Extensions.Validation.ValidationOptions>? configureOptions = null)
175+
{
176+
// Use non-extension method to avoid infinite recursion.
177+
return global::Microsoft.Extensions.DependencyInjection.ValidationServiceCollectionExtensions.AddValidation(services, options =>
178+
{
179+
options.Resolvers.Insert(0, new GeneratedValidatableInfoResolver());
180+
if (configureOptions is not null)
181+
{
182+
configureOptions(options);
183+
}
184+
});
185+
}
186+
}
187+
188+
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")]
189+
file static class ValidationAttributeCache
190+
{
191+
private sealed record CacheKey(
192+
[param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)]
193+
[property: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)]
194+
global::System.Type ContainingType,
195+
string PropertyName);
196+
private static readonly global::System.Collections.Concurrent.ConcurrentDictionary<CacheKey, global::System.ComponentModel.DataAnnotations.ValidationAttribute[]> _cache = new();
197+
198+
public static global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes(
199+
[global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)]
200+
global::System.Type containingType,
201+
string propertyName)
202+
{
203+
var key = new CacheKey(containingType, propertyName);
204+
return _cache.GetOrAdd(key, static k =>
205+
{
206+
var results = new global::System.Collections.Generic.List<global::System.ComponentModel.DataAnnotations.ValidationAttribute>();
207+
208+
// Get attributes from the property
209+
var property = k.ContainingType.GetProperty(k.PropertyName);
210+
if (property != null)
211+
{
212+
var propertyAttributes = global::System.Reflection.CustomAttributeExtensions
213+
.GetCustomAttributes<global::System.ComponentModel.DataAnnotations.ValidationAttribute>(property, inherit: true);
214+
215+
results.AddRange(propertyAttributes);
216+
}
217+
218+
// Check constructors for parameters that match the property name
219+
// to handle record scenarios
220+
foreach (var constructor in k.ContainingType.GetConstructors())
221+
{
222+
// Look for parameter with matching name (case insensitive)
223+
var parameter = global::System.Linq.Enumerable.FirstOrDefault(
224+
constructor.GetParameters(),
225+
p => string.Equals(p.Name, k.PropertyName, global::System.StringComparison.OrdinalIgnoreCase));
226+
227+
if (parameter != null)
228+
{
229+
var paramAttributes = global::System.Reflection.CustomAttributeExtensions
230+
.GetCustomAttributes<global::System.ComponentModel.DataAnnotations.ValidationAttribute>(parameter, inherit: true);
231+
232+
results.AddRange(paramAttributes);
233+
234+
break;
235+
}
236+
}
237+
238+
return results.ToArray();
239+
});
240+
}
241+
}
242+
}

0 commit comments

Comments
 (0)