Skip to content

Commit 1ec6dcf

Browse files
committed
Make ValidationContext a required property
1 parent 52024a6 commit 1ec6dcf

8 files changed

+98
-207
lines changed

src/Http/Http.Abstractions/src/PublicAPI.Unshipped.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ Microsoft.AspNetCore.Http.Validation.ValidateContext.CurrentDepth.set -> void
2424
Microsoft.AspNetCore.Http.Validation.ValidateContext.CurrentValidationPath.get -> string!
2525
Microsoft.AspNetCore.Http.Validation.ValidateContext.CurrentValidationPath.set -> void
2626
Microsoft.AspNetCore.Http.Validation.ValidateContext.ValidateContext() -> void
27-
Microsoft.AspNetCore.Http.Validation.ValidateContext.ValidationContext.get -> System.ComponentModel.DataAnnotations.ValidationContext?
27+
Microsoft.AspNetCore.Http.Validation.ValidateContext.ValidationContext.get -> System.ComponentModel.DataAnnotations.ValidationContext!
2828
Microsoft.AspNetCore.Http.Validation.ValidateContext.ValidationContext.set -> void
2929
Microsoft.AspNetCore.Http.Validation.ValidateContext.ValidationErrors.get -> System.Collections.Generic.Dictionary<string!, string![]!>?
3030
Microsoft.AspNetCore.Http.Validation.ValidateContext.ValidationErrors.set -> void

src/Http/Http.Abstractions/src/Validation/ValidatableParameterInfo.cs

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -65,11 +65,6 @@ public virtual async Task ValidateAsync(object? value, ValidateContext context,
6565
return;
6666
}
6767

68-
// ValidationContext requires a non-null value although the invocation pattern that we use
69-
// calls `GetValidationResult` and passes the value there. `GetValidationResult` tolerates
70-
// null values so we only need to set a non-null value to the ValidationContext here.
71-
context.ValidationContext ??= new ValidationContext(value ?? new object(), displayName: DisplayName, serviceProvider: null, items: null);
72-
7368
context.ValidationContext.DisplayName = DisplayName;
7469
context.ValidationContext.MemberName = Name;
7570

src/Http/Http.Abstractions/src/Validation/ValidatablePropertyInfo.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,6 @@ public virtual async Task ValidateAsync(object? value, ValidateContext context,
7575
context.CurrentValidationPath = $"{originalPrefix}.{Name}";
7676
}
7777

78-
context.ValidationContext ??= new ValidationContext(value ?? new object(), displayName: DisplayName, serviceProvider: null, items: null);
79-
8078
context.ValidationContext.DisplayName = DisplayName;
8179
context.ValidationContext.MemberName = Name;
8280

src/Http/Http.Abstractions/src/Validation/ValidatableTypeInfo.cs

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,10 +49,6 @@ public virtual async Task ValidateAsync(object? value, ValidateContext context,
4949
return;
5050
}
5151

52-
// Although classes can be annotated with [DisplayName], we only process display names when producing
53-
// errors for properties so we can pass the `Type.Name` as the display name for the type here.
54-
context.ValidationContext ??= new ValidationContext(value, displayName: Type.Name, serviceProvider: null, items: null);
55-
5652
// Check if we've exceeded the maximum depth
5753
if (context.CurrentDepth >= context.ValidationOptions.MaxDepth)
5854
{

src/Http/Http.Abstractions/src/Validation/ValidateContext.cs

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,24 @@ public sealed class ValidateContext
1616
/// Gets or sets the validation context used for validating objects that implement <see cref="IValidatableObject"/> or have <see cref="ValidationAttribute"/>.
1717
/// This context provides access to service provider and other validation metadata.
1818
/// </summary>
19-
public ValidationContext? ValidationContext { get; set; }
19+
/// <remarks>
20+
/// This property should be set by the consumer of the <see cref="IValidatableInfo"/>
21+
/// interface to provide the necessary context for validation. The object should be initialized
22+
/// with the current object being validated, the display name, and the service provider to support
23+
/// the complete set of validation scenarios.
24+
/// </remarks>
25+
/// <example>
26+
/// <code>
27+
/// var validationContext = new ValidationContext(objectToValidate, serviceProvider, items);
28+
/// var validationOptions = serviceProvider.GetService&lt;IOptions&lt;ValidationOptions&gt;&gt;()?.Value;
29+
/// var validateContext = new ValidateContext
30+
/// {
31+
/// ValidationContext = validationContext,
32+
/// ValidationOptions = validationOptions
33+
/// };
34+
/// </code>
35+
/// </example>
36+
public required ValidationContext ValidationContext { get; set; }
2037

2138
/// <summary>
2239
/// Gets or sets the prefix used to identify the current object being validated in a complex object graph.

src/Http/Http.Abstractions/test/Validation/ValidatableParameterInfoTests.cs

Lines changed: 0 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -287,60 +287,6 @@ public async Task Validate_ExceptionDuringValidation_CapturesExceptionAsError()
287287
Assert.Equal("Test exception", error.Value.First());
288288
}
289289

290-
[Fact]
291-
public async Task Validate_WithoutValidationContext_RequiredParameter_AddsErrorAndInitializesValidationContext()
292-
{
293-
// Arrange
294-
var paramInfo = CreateTestParameterInfo(
295-
parameterType: typeof(string),
296-
name: "testParam",
297-
displayName: "Test Parameter",
298-
validationAttributes: [new RequiredAttribute()]);
299-
300-
// Create a ValidateContext without a pre-populated ValidationContext
301-
var context = new ValidateContext
302-
{
303-
ValidationOptions = new TestValidationOptions(new Dictionary<Type, ValidatableTypeInfo>())
304-
};
305-
306-
// Sanity check
307-
Assert.Null(context.ValidationContext);
308-
309-
// Act
310-
await paramInfo.ValidateAsync(null, context, default);
311-
312-
// Assert – a ValidationContext should have been created and the error recorded
313-
Assert.NotNull(context.ValidationContext);
314-
var errors = context.ValidationErrors;
315-
Assert.NotNull(errors);
316-
var error = Assert.Single(errors);
317-
Assert.Equal("testParam", error.Key);
318-
Assert.Equal("The Test Parameter field is required.", error.Value.Single());
319-
}
320-
321-
[Fact]
322-
public async Task Validate_WithoutValidationContext_ValidValue_NoErrors()
323-
{
324-
// Arrange
325-
var paramInfo = CreateTestParameterInfo(
326-
parameterType: typeof(int),
327-
name: "testParam",
328-
displayName: "Test Parameter",
329-
validationAttributes: [new RangeAttribute(10, 100)]);
330-
331-
var context = new ValidateContext
332-
{
333-
ValidationOptions = new TestValidationOptions(new Dictionary<Type, ValidatableTypeInfo>())
334-
};
335-
336-
// Act
337-
await paramInfo.ValidateAsync(50, context, default);
338-
339-
// Assert – ValidationContext initialized, but no errors added
340-
Assert.NotNull(context.ValidationContext);
341-
Assert.Null(context.ValidationErrors);
342-
}
343-
344290
private TestValidatableParameterInfo CreateTestParameterInfo(
345291
Type parameterType,
346292
string name,

0 commit comments

Comments
 (0)