Skip to content

Commit ea13cf1

Browse files
committed
Separate caches for property and type attributes
1 parent 2d5d9c2 commit ea13cf1

File tree

20 files changed

+940
-720
lines changed

20 files changed

+940
-720
lines changed

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

Lines changed: 47 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ public GeneratedValidatablePropertyInfo(
7272
internal string Name { get; }
7373
7474
protected override global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes()
75-
=> ValidationAttributeCache.GetValidationAttributes(ContainingType, Name);
75+
=> ValidationAttributeCache.GetPropertyValidationAttributes(ContainingType, Name);
7676
}
7777
7878
{{GeneratedCodeAttribute}}
@@ -90,7 +90,7 @@ public GeneratedValidatableTypeInfo(
9090
internal global::System.Type Type { get; }
9191
9292
protected override global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes()
93-
=> ValidationAttributeCache.GetValidationAttributes(Type, null);
93+
=> ValidationAttributeCache.GetTypeValidationAttributes(Type);
9494
}
9595
9696
{{GeneratedCodeAttribute}}
@@ -137,59 +137,70 @@ private sealed record CacheKey(
137137
[property: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)]
138138
global::System.Type ContainingType,
139139
string? PropertyName);
140-
private static readonly global::System.Collections.Concurrent.ConcurrentDictionary<CacheKey, global::System.ComponentModel.DataAnnotations.ValidationAttribute[]> _cache = new();
140+
private static readonly global::System.Collections.Concurrent.ConcurrentDictionary<CacheKey, global::System.ComponentModel.DataAnnotations.ValidationAttribute[]> _propertyCache = new();
141+
private static readonly global::System.Lazy<global::System.Collections.Concurrent.ConcurrentDictionary<global::System.Type, global::System.ComponentModel.DataAnnotations.ValidationAttribute[]>> _lazyTypeCache = new (() => new ());
142+
private static global::System.Collections.Concurrent.ConcurrentDictionary<global::System.Type, global::System.ComponentModel.DataAnnotations.ValidationAttribute[]> TypeCache => _lazyTypeCache.Value;
141143
142-
public static global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes(
144+
public static global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetPropertyValidationAttributes(
143145
[global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)]
144146
global::System.Type containingType,
145147
string? propertyName)
146148
{
147149
var key = new CacheKey(containingType, propertyName);
148-
return _cache.GetOrAdd(key, static k =>
150+
return _propertyCache.GetOrAdd(key, static k =>
149151
{
150152
var results = new global::System.Collections.Generic.List<global::System.ComponentModel.DataAnnotations.ValidationAttribute>();
151153
152-
if (k.PropertyName is not null)
154+
// Get attributes from the property
155+
var property = k.ContainingType.GetProperty(k.PropertyName);
156+
if (property != null)
153157
{
154-
// Get attributes from the property
155-
var property = k.ContainingType.GetProperty(k.PropertyName);
156-
if (property != null)
157-
{
158-
var propertyAttributes = global::System.Reflection.CustomAttributeExtensions
159-
.GetCustomAttributes<global::System.ComponentModel.DataAnnotations.ValidationAttribute>(property, inherit: true);
158+
var propertyAttributes = global::System.Reflection.CustomAttributeExtensions
159+
.GetCustomAttributes<global::System.ComponentModel.DataAnnotations.ValidationAttribute>(property, inherit: true);
160160
161-
results.AddRange(propertyAttributes);
162-
}
161+
results.AddRange(propertyAttributes);
162+
}
163163
164-
// Check constructors for parameters that match the property name
165-
// to handle record scenarios
166-
foreach (var constructor in k.ContainingType.GetConstructors())
167-
{
168-
// Look for parameter with matching name (case insensitive)
169-
var parameter = global::System.Linq.Enumerable.FirstOrDefault(
170-
constructor.GetParameters(),
171-
p => string.Equals(p.Name, k.PropertyName, global::System.StringComparison.OrdinalIgnoreCase));
164+
// Check constructors for parameters that match the property name
165+
// to handle record scenarios
166+
foreach (var constructor in k.ContainingType.GetConstructors())
167+
{
168+
// Look for parameter with matching name (case insensitive)
169+
var parameter = global::System.Linq.Enumerable.FirstOrDefault(
170+
constructor.GetParameters(),
171+
p => string.Equals(p.Name, k.PropertyName, global::System.StringComparison.OrdinalIgnoreCase));
172172
173-
if (parameter != null)
174-
{
175-
var paramAttributes = global::System.Reflection.CustomAttributeExtensions
176-
.GetCustomAttributes<global::System.ComponentModel.DataAnnotations.ValidationAttribute>(parameter, inherit: true);
173+
if (parameter != null)
174+
{
175+
var paramAttributes = global::System.Reflection.CustomAttributeExtensions
176+
.GetCustomAttributes<global::System.ComponentModel.DataAnnotations.ValidationAttribute>(parameter, inherit: true);
177177
178-
results.AddRange(paramAttributes);
178+
results.AddRange(paramAttributes);
179179
180-
break;
181-
}
180+
break;
182181
}
183182
}
184-
else
183+
184+
return results.ToArray();
185+
});
186+
}
187+
188+
189+
public static global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetTypeValidationAttributes(
190+
[global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.Interfaces)]
191+
global::System.Type type
192+
)
193+
{
194+
return TypeCache.GetOrAdd(type, static t =>
195+
{
196+
var results = new global::System.Collections.Generic.List<global::System.ComponentModel.DataAnnotations.ValidationAttribute>();
197+
198+
// Get attributes from the type itself and its super types
199+
foreach (var attr in t.GetCustomAttributes(typeof(global::System.ComponentModel.DataAnnotations.ValidationAttribute), true))
185200
{
186-
// Get attributes from the type itself and its super types
187-
foreach (var attr in k.ContainingType.GetCustomAttributes(typeof(global::System.ComponentModel.DataAnnotations.ValidationAttribute), true))
201+
if (attr is global::System.ComponentModel.DataAnnotations.ValidationAttribute validationAttribute)
188202
{
189-
if (attr is global::System.ComponentModel.DataAnnotations.ValidationAttribute validationAttribute)
190-
{
191-
results.Add(validationAttribute);
192-
}
203+
results.Add(validationAttribute);
193204
}
194205
}
195206

src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanDiscoverGeneratedValidatableTypeAttribute#ValidatableInfoResolver.g.verified.cs

Lines changed: 47 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ public GeneratedValidatablePropertyInfo(
4444
internal string Name { get; }
4545

4646
protected override global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes()
47-
=> ValidationAttributeCache.GetValidationAttributes(ContainingType, Name);
47+
=> ValidationAttributeCache.GetPropertyValidationAttributes(ContainingType, Name);
4848
}
4949

5050
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")]
@@ -62,7 +62,7 @@ public GeneratedValidatableTypeInfo(
6262
internal global::System.Type Type { get; }
6363

6464
protected override global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes()
65-
=> ValidationAttributeCache.GetValidationAttributes(Type, null);
65+
=> ValidationAttributeCache.GetTypeValidationAttributes(Type);
6666
}
6767

6868
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")]
@@ -130,59 +130,70 @@ private sealed record CacheKey(
130130
[property: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)]
131131
global::System.Type ContainingType,
132132
string? PropertyName);
133-
private static readonly global::System.Collections.Concurrent.ConcurrentDictionary<CacheKey, global::System.ComponentModel.DataAnnotations.ValidationAttribute[]> _cache = new();
133+
private static readonly global::System.Collections.Concurrent.ConcurrentDictionary<CacheKey, global::System.ComponentModel.DataAnnotations.ValidationAttribute[]> _propertyCache = new();
134+
private static readonly global::System.Lazy<global::System.Collections.Concurrent.ConcurrentDictionary<global::System.Type, global::System.ComponentModel.DataAnnotations.ValidationAttribute[]>> _lazyTypeCache = new (() => new ());
135+
private static global::System.Collections.Concurrent.ConcurrentDictionary<global::System.Type, global::System.ComponentModel.DataAnnotations.ValidationAttribute[]> TypeCache => _lazyTypeCache.Value;
134136

135-
public static global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes(
137+
public static global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetPropertyValidationAttributes(
136138
[global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)]
137139
global::System.Type containingType,
138140
string? propertyName)
139141
{
140142
var key = new CacheKey(containingType, propertyName);
141-
return _cache.GetOrAdd(key, static k =>
143+
return _propertyCache.GetOrAdd(key, static k =>
142144
{
143145
var results = new global::System.Collections.Generic.List<global::System.ComponentModel.DataAnnotations.ValidationAttribute>();
144146

145-
if (k.PropertyName is not null)
147+
// Get attributes from the property
148+
var property = k.ContainingType.GetProperty(k.PropertyName);
149+
if (property != null)
146150
{
147-
// Get attributes from the property
148-
var property = k.ContainingType.GetProperty(k.PropertyName);
149-
if (property != null)
150-
{
151-
var propertyAttributes = global::System.Reflection.CustomAttributeExtensions
152-
.GetCustomAttributes<global::System.ComponentModel.DataAnnotations.ValidationAttribute>(property, inherit: true);
151+
var propertyAttributes = global::System.Reflection.CustomAttributeExtensions
152+
.GetCustomAttributes<global::System.ComponentModel.DataAnnotations.ValidationAttribute>(property, inherit: true);
153153

154-
results.AddRange(propertyAttributes);
155-
}
154+
results.AddRange(propertyAttributes);
155+
}
156156

157-
// Check constructors for parameters that match the property name
158-
// to handle record scenarios
159-
foreach (var constructor in k.ContainingType.GetConstructors())
160-
{
161-
// Look for parameter with matching name (case insensitive)
162-
var parameter = global::System.Linq.Enumerable.FirstOrDefault(
163-
constructor.GetParameters(),
164-
p => string.Equals(p.Name, k.PropertyName, global::System.StringComparison.OrdinalIgnoreCase));
157+
// Check constructors for parameters that match the property name
158+
// to handle record scenarios
159+
foreach (var constructor in k.ContainingType.GetConstructors())
160+
{
161+
// Look for parameter with matching name (case insensitive)
162+
var parameter = global::System.Linq.Enumerable.FirstOrDefault(
163+
constructor.GetParameters(),
164+
p => string.Equals(p.Name, k.PropertyName, global::System.StringComparison.OrdinalIgnoreCase));
165165

166-
if (parameter != null)
167-
{
168-
var paramAttributes = global::System.Reflection.CustomAttributeExtensions
169-
.GetCustomAttributes<global::System.ComponentModel.DataAnnotations.ValidationAttribute>(parameter, inherit: true);
166+
if (parameter != null)
167+
{
168+
var paramAttributes = global::System.Reflection.CustomAttributeExtensions
169+
.GetCustomAttributes<global::System.ComponentModel.DataAnnotations.ValidationAttribute>(parameter, inherit: true);
170170

171-
results.AddRange(paramAttributes);
171+
results.AddRange(paramAttributes);
172172

173-
break;
174-
}
173+
break;
175174
}
176175
}
177-
else
176+
177+
return results.ToArray();
178+
});
179+
}
180+
181+
182+
public static global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetTypeValidationAttributes(
183+
[global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.Interfaces)]
184+
global::System.Type type
185+
)
186+
{
187+
return TypeCache.GetOrAdd(type, static t =>
188+
{
189+
var results = new global::System.Collections.Generic.List<global::System.ComponentModel.DataAnnotations.ValidationAttribute>();
190+
191+
// Get attributes from the type itself and its super types
192+
foreach (var attr in t.GetCustomAttributes(typeof(global::System.ComponentModel.DataAnnotations.ValidationAttribute), true))
178193
{
179-
// Get attributes from the type itself and its super types
180-
foreach (var attr in k.ContainingType.GetCustomAttributes(typeof(global::System.ComponentModel.DataAnnotations.ValidationAttribute), true))
194+
if (attr is global::System.ComponentModel.DataAnnotations.ValidationAttribute validationAttribute)
181195
{
182-
if (attr is global::System.ComponentModel.DataAnnotations.ValidationAttribute validationAttribute)
183-
{
184-
results.Add(validationAttribute);
185-
}
196+
results.Add(validationAttribute);
186197
}
187198
}
188199

0 commit comments

Comments
 (0)