@@ -373,4 +373,176 @@ async Task ValidInputProducesNoWarnings(Endpoint endpoint)
373373 }
374374 } ) ;
375375 }
376+
377+ [ Fact ]
378+ public async Task DoesNotValidatePropertiesWithFromServicesAttribute ( )
379+ {
380+ // Arrange
381+ var source = """
382+ using System;
383+ using System.ComponentModel.DataAnnotations;
384+ using System.Collections.Generic;
385+ using System.Threading.Tasks;
386+ using Microsoft.AspNetCore.Builder;
387+ using Microsoft.AspNetCore.Http;
388+ using Microsoft.Extensions.Validation;
389+ using Microsoft.AspNetCore.Routing;
390+ using Microsoft.Extensions.DependencyInjection;
391+ using Microsoft.AspNetCore.Mvc;
392+
393+ var builder = WebApplication.CreateBuilder();
394+
395+ builder.Services.AddValidation();
396+ builder.Services.AddSingleton<TestService>();
397+
398+ var app = builder.Build();
399+
400+ app.MapPost("/with-from-services", ([AsParameters] ComplexTypeWithFromServices complexType) => Results.Ok("Passed"!));
401+
402+ app.Run();
403+
404+ public class ComplexTypeWithFromServices
405+ {
406+ [Range(10, 100)]
407+ public int ValidatableProperty { get; set; } = 10;
408+
409+ [FromServices]
410+ [Required] // This should be ignored because of [FromServices]
411+ public TestService ServiceProperty { get; set; } = null!;
412+
413+ [FromKeyedServices("serviceKey")]
414+ [Range(10, 100)] // This should be ignored because of [FromKeyedServices]
415+ public int KeyedServiceProperty { get; set; } = 5;
416+ }
417+
418+ public class TestService
419+ {
420+ [Range(10, 100)]
421+ public int Value { get; set; } = 4;
422+ }
423+ """ ;
424+ await Verify ( source , out var compilation ) ;
425+ await VerifyEndpoint ( compilation , "/with-from-services" , async ( endpoint , serviceProvider ) =>
426+ {
427+ await ValidInputWithFromServicesProducesNoWarnings ( endpoint ) ;
428+ await InvalidValidatablePropertyProducesError ( endpoint ) ;
429+
430+ async Task ValidInputWithFromServicesProducesNoWarnings ( Endpoint endpoint )
431+ {
432+ var payload = """
433+ {
434+ "ValidatableProperty": 50,
435+ "ServiceProperty": null,
436+ "KeyedServiceProperty": 5
437+ }
438+ """ ;
439+ var context = CreateHttpContextWithPayload ( payload , serviceProvider ) ;
440+ await endpoint . RequestDelegate ( context ) ;
441+
442+ Assert . Equal ( 200 , context . Response . StatusCode ) ;
443+ }
444+
445+ async Task InvalidValidatablePropertyProducesError ( Endpoint endpoint )
446+ {
447+ var payload = """
448+ {
449+ "ValidatableProperty": 5,
450+ "ServiceProperty": null,
451+ "KeyedServiceProperty": 5
452+ }
453+ """ ;
454+ var context = CreateHttpContextWithPayload ( payload , serviceProvider ) ;
455+ await endpoint . RequestDelegate ( context ) ;
456+
457+ var problemDetails = await AssertBadRequest ( context ) ;
458+ Assert . Collection ( problemDetails . Errors , kvp =>
459+ {
460+ Assert . Equal ( "ValidatableProperty" , kvp . Key ) ;
461+ Assert . Equal ( "The field ValidatableProperty must be between 10 and 100." , kvp . Value . Single ( ) ) ;
462+ } ) ;
463+ }
464+ } ) ;
465+ }
466+
467+ [ Fact ]
468+ public async Task DoesNotValidateRecordPropertiesWithFromServicesAttribute ( )
469+ {
470+ // Arrange
471+ var source = """
472+ using System;
473+ using System.ComponentModel.DataAnnotations;
474+ using System.Collections.Generic;
475+ using System.Threading.Tasks;
476+ using Microsoft.AspNetCore.Builder;
477+ using Microsoft.AspNetCore.Http;
478+ using Microsoft.Extensions.Validation;
479+ using Microsoft.AspNetCore.Routing;
480+ using Microsoft.Extensions.DependencyInjection;
481+ using Microsoft.AspNetCore.Mvc;
482+
483+ var builder = WebApplication.CreateBuilder();
484+
485+ builder.Services.AddValidation();
486+ builder.Services.AddSingleton<TestService>();
487+
488+ var app = builder.Build();
489+
490+ app.MapPost("/with-from-services-record", ([AsParameters] ComplexRecordWithFromServices complexType) => Results.Ok("Passed"!));
491+
492+ app.Run();
493+
494+ public record ComplexRecordWithFromServices(
495+ [Range(10, 100)] int ValidatableProperty,
496+ [FromServices] [Required] TestService ServiceProperty, // This should be ignored because of [FromServices]
497+ [FromKeyedServices("serviceKey")] [Range(10, 100)] int KeyedServiceProperty // This should be ignored because of [FromKeyedServices]
498+ );
499+
500+ public class TestService
501+ {
502+ [Range(10, 100)]
503+ public int Value { get; set; } = 4;
504+ }
505+ """ ;
506+ await Verify ( source , out var compilation ) ;
507+ await VerifyEndpoint ( compilation , "/with-from-services-record" , async ( endpoint , serviceProvider ) =>
508+ {
509+ await ValidInputWithFromServicesProducesNoWarnings ( endpoint ) ;
510+ await InvalidValidatablePropertyProducesError ( endpoint ) ;
511+
512+ async Task ValidInputWithFromServicesProducesNoWarnings ( Endpoint endpoint )
513+ {
514+ var payload = """
515+ {
516+ "ValidatableProperty": 50,
517+ "ServiceProperty": null,
518+ "KeyedServiceProperty": 5
519+ }
520+ """ ;
521+ var context = CreateHttpContextWithPayload ( payload , serviceProvider ) ;
522+ await endpoint . RequestDelegate ( context ) ;
523+
524+ Assert . Equal ( 200 , context . Response . StatusCode ) ;
525+ }
526+
527+ async Task InvalidValidatablePropertyProducesError ( Endpoint endpoint )
528+ {
529+ var payload = """
530+ {
531+ "ValidatableProperty": 5,
532+ "ServiceProperty": null,
533+ "KeyedServiceProperty": 5
534+ }
535+ """ ;
536+ var context = CreateHttpContextWithPayload ( payload , serviceProvider ) ;
537+ await endpoint . RequestDelegate ( context ) ;
538+
539+ var problemDetails = await AssertBadRequest ( context ) ;
540+ Assert . Collection ( problemDetails . Errors , kvp =>
541+ {
542+ Assert . Equal ( "ValidatableProperty" , kvp . Key ) ;
543+ Assert . Equal ( "The field ValidatableProperty must be between 10 and 100." , kvp . Value . Single ( ) ) ;
544+ } ) ;
545+ }
546+ } ) ;
547+ }
376548}
0 commit comments