@@ -27,9 +27,6 @@ internal class DataAnnotationsMetadataProvider :
27
27
private const string NullableAttributeFullTypeName = "System.Runtime.CompilerServices.NullableAttribute" ;
28
28
private const string NullableFlagsFieldName = "NullableFlags" ;
29
29
30
- private const string NullableContextAttributeFullName = "System.Runtime.CompilerServices.NullableContextAttribute" ;
31
- private const string NullableContextFlagsFieldName = "Flag" ;
32
-
33
30
private readonly IStringLocalizerFactory ? _stringLocalizerFactory ;
34
31
private readonly MvcOptions _options ;
35
32
private readonly MvcDataAnnotationsLocalizationOptions _localizationOptions ;
@@ -377,10 +374,7 @@ public void CreateValidationMetadata(ValidationMetadataProviderContext context)
377
374
}
378
375
else
379
376
{
380
- addInferredRequiredAttribute = IsNullableReferenceType (
381
- property . DeclaringType ! ,
382
- member : null ,
383
- context . PropertyAttributes ! ) ;
377
+ addInferredRequiredAttribute = IsRequired ( context ) ;
384
378
}
385
379
}
386
380
else if ( context . Key . MetadataKind == ModelMetadataKind . Parameter )
@@ -389,11 +383,9 @@ public void CreateValidationMetadata(ValidationMetadataProviderContext context)
389
383
// since the parameter will be optional.
390
384
if ( ! context . Key . ParameterInfo ! . HasDefaultValue )
391
385
{
392
- addInferredRequiredAttribute = IsNullableReferenceType (
393
- context . Key . ParameterInfo ! . Member . ReflectedType ,
394
- context . Key . ParameterInfo . Member ,
395
- context . ParameterAttributes ! ) ;
386
+ addInferredRequiredAttribute = IsRequired ( context ) ;
396
387
}
388
+
397
389
}
398
390
else
399
391
{
@@ -467,14 +459,17 @@ private static string GetDisplayGroup(FieldInfo field)
467
459
return string . Empty ;
468
460
}
469
461
470
- internal static bool IsNullableReferenceType ( Type ? containingType , MemberInfo ? member , IEnumerable < object > attributes )
462
+ internal static bool IsRequired ( ValidationMetadataProviderContext context )
471
463
{
472
- if ( HasNullableAttribute ( attributes , out var result ) )
473
- {
474
- return result ;
475
- }
476
-
477
- return IsNullableBasedOnContext ( containingType , member ) ;
464
+ var nullabilityContext = new NullabilityInfoContext ( ) ;
465
+ var nullability = context . Key . MetadataKind switch
466
+ {
467
+ ModelMetadataKind . Parameter => nullabilityContext . Create ( context . Key . ParameterInfo ! ) ,
468
+ ModelMetadataKind . Property => nullabilityContext . Create ( context . Key . PropertyInfo ! ) ,
469
+ _ => null
470
+ } ;
471
+ var isOptional = nullability != null && nullability . ReadState != NullabilityState . NotNull ;
472
+ return ! isOptional ;
478
473
}
479
474
480
475
// Internal for testing
@@ -508,67 +503,4 @@ internal static bool HasNullableAttribute(IEnumerable<object> attributes, out bo
508
503
isNullable = false ;
509
504
return true ; // [Nullable] found but type is not an NNRT
510
505
}
511
-
512
- internal static bool IsNullableBasedOnContext ( Type ? containingType , MemberInfo ? member )
513
- {
514
- if ( containingType is null )
515
- {
516
- return false ;
517
- }
518
-
519
- // For generic types, inspecting the nullability requirement additionally requires
520
- // inspecting the nullability constraint on generic type parameters. This is fairly non-triviial
521
- // so we'll just avoid calculating it. Users should still be able to apply an explicit [Required]
522
- // attribute on these members.
523
- if ( containingType . IsGenericType )
524
- {
525
- return false ;
526
- }
527
-
528
- // The [Nullable] and [NullableContext] attributes are not inherited.
529
- //
530
- // The [NullableContext] attribute can appear on a method or on the module.
531
- var attributes = member ? . GetCustomAttributes ( inherit : false ) ?? Array . Empty < object > ( ) ;
532
- var isNullable = AttributesHasNullableContext ( attributes ) ;
533
- if ( isNullable != null )
534
- {
535
- return isNullable . Value ;
536
- }
537
-
538
- // Check on the containing type
539
- var type = containingType ;
540
- do
541
- {
542
- attributes = type . GetCustomAttributes ( inherit : false ) ;
543
- isNullable = AttributesHasNullableContext ( attributes ) ;
544
- if ( isNullable != null )
545
- {
546
- return isNullable . Value ;
547
- }
548
-
549
- type = type . DeclaringType ;
550
- }
551
- while ( type != null ) ;
552
-
553
- // If we don't find the attribute on the declaring type then repeat at the module level
554
- attributes = containingType . Module . GetCustomAttributes ( inherit : false ) ;
555
- isNullable = AttributesHasNullableContext ( attributes ) ;
556
- return isNullable ?? false ;
557
-
558
- bool ? AttributesHasNullableContext ( object [ ] attributes )
559
- {
560
- var nullableContextAttribute = attributes
561
- . FirstOrDefault ( a => string . Equals ( a . GetType ( ) . FullName , NullableContextAttributeFullName , StringComparison . Ordinal ) ) ;
562
- if ( nullableContextAttribute != null )
563
- {
564
- if ( nullableContextAttribute . GetType ( ) . GetField ( NullableContextFlagsFieldName ) is FieldInfo field &&
565
- field . GetValue ( nullableContextAttribute ) is byte @byte )
566
- {
567
- return @byte == 1 ; // [NullableContext] found
568
- }
569
- }
570
-
571
- return null ;
572
- }
573
- }
574
506
}
0 commit comments