@@ -164,4 +164,145 @@ async Task ValidateForTopLevelInvoked()
164164 }
165165 } ) ;
166166 }
167+
168+ [ Fact ]
169+ public async Task CanValidateIValidatableObject_WithoutPropertyValidations ( )
170+ {
171+ var source = """
172+ using System.Collections.Generic;
173+ using System.ComponentModel.DataAnnotations;
174+ using System.Threading.Tasks;
175+ using Microsoft.AspNetCore.Builder;
176+ using Microsoft.AspNetCore.Http;
177+ using Microsoft.Extensions.Validation;
178+ using Microsoft.AspNetCore.Mvc;
179+ using Microsoft.AspNetCore.Routing;
180+ using Microsoft.Extensions.DependencyInjection;
181+
182+ WebApplicationBuilder builder = WebApplication.CreateBuilder(args);
183+
184+ builder.Services.AddValidation();
185+
186+ WebApplication app = builder. Build();
187+
188+ app.MapPost("/base", (BaseClass model) => Results.Ok(model));
189+ app.MapPost("/derived", (DerivedClass model) => Results.Ok(model));
190+ app.MapPost("/complex", (ComplexClass model) => Results.Ok(model));
191+
192+ app.Run();
193+
194+ public class BaseClass : IValidatableObject
195+ {
196+ public string? Value { get; set; }
197+
198+ public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
199+ {
200+ if (string.IsNullOrEmpty(Value))
201+ {
202+ yield return new ValidationResult("Value cannot be null or empty.", [nameof(Value)]);
203+ }
204+ }
205+ }
206+
207+ public class DerivedClass : BaseClass
208+ {
209+ }
210+
211+ public class ComplexClass
212+ {
213+ public NestedClass? NestedObject { get; set; }
214+ }
215+
216+ public class NestedClass : IValidatableObject
217+ {
218+ public string? Value { get; set; }
219+ public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
220+ {
221+ if (string.IsNullOrEmpty(Value))
222+ {
223+ yield return new ValidationResult("Value cannot be null or empty.", [nameof(Value)]);
224+ }
225+ }
226+ }
227+ """ ;
228+
229+ await Verify ( source , out var compilation ) ;
230+
231+ await VerifyEndpoint ( compilation , "/base" , async ( endpoint , serviceProvider ) =>
232+ {
233+ await ValidateMethodCalled ( ) ;
234+
235+ async Task ValidateMethodCalled ( )
236+ {
237+ var httpContext = CreateHttpContextWithPayload ( """
238+ {
239+ "Value": ""
240+ }
241+ """ , serviceProvider ) ;
242+
243+ await endpoint . RequestDelegate ( httpContext ) ;
244+
245+ var problemDetails = await AssertBadRequest ( httpContext ) ;
246+ Assert . Collection ( problemDetails . Errors ,
247+ error =>
248+ {
249+ Assert . Equal ( "Value" , error . Key ) ;
250+ Assert . Collection ( error . Value ,
251+ msg => Assert . Equal ( "Value cannot be null or empty." , msg ) ) ;
252+ } ) ;
253+ }
254+ } ) ;
255+
256+ await VerifyEndpoint ( compilation , "/derived" , async ( endpoint , serviceProvider ) =>
257+ {
258+ await ValidateMethodCalled ( ) ;
259+
260+ async Task ValidateMethodCalled ( )
261+ {
262+ var httpContext = CreateHttpContextWithPayload ( """
263+ {
264+ "Value": ""
265+ }
266+ """ , serviceProvider ) ;
267+
268+ await endpoint . RequestDelegate ( httpContext ) ;
269+
270+ var problemDetails = await AssertBadRequest ( httpContext ) ;
271+ Assert . Collection ( problemDetails . Errors ,
272+ error =>
273+ {
274+ Assert . Equal ( "Value" , error . Key ) ;
275+ Assert . Collection ( error . Value ,
276+ msg => Assert . Equal ( "Value cannot be null or empty." , msg ) ) ;
277+ } ) ;
278+ }
279+ } ) ;
280+
281+ await VerifyEndpoint ( compilation , "/complex" , async ( endpoint , serviceProvider ) =>
282+ {
283+ await ValidateMethodCalled ( ) ;
284+
285+ async Task ValidateMethodCalled ( )
286+ {
287+ var httpContext = CreateHttpContextWithPayload ( """
288+ {
289+ "NestedObject": {
290+ "Value": ""
291+ }
292+ }
293+ """ , serviceProvider ) ;
294+
295+ await endpoint . RequestDelegate ( httpContext ) ;
296+
297+ var problemDetails = await AssertBadRequest ( httpContext ) ;
298+ Assert . Collection ( problemDetails . Errors ,
299+ error =>
300+ {
301+ Assert . Equal ( "NestedObject.Value" , error . Key ) ;
302+ Assert . Collection ( error . Value ,
303+ msg => Assert . Equal ( "Value cannot be null or empty." , msg ) ) ;
304+ } ) ;
305+ }
306+ } ) ;
307+ }
167308}
0 commit comments