@@ -27,9 +27,6 @@ internal class DataAnnotationsMetadataProvider :
2727 private const string NullableAttributeFullTypeName = "System.Runtime.CompilerServices.NullableAttribute" ;
2828 private const string NullableFlagsFieldName = "NullableFlags" ;
2929
30- private const string NullableContextAttributeFullName = "System.Runtime.CompilerServices.NullableContextAttribute" ;
31- private const string NullableContextFlagsFieldName = "Flag" ;
32-
3330 private readonly IStringLocalizerFactory ? _stringLocalizerFactory ;
3431 private readonly MvcOptions _options ;
3532 private readonly MvcDataAnnotationsLocalizationOptions _localizationOptions ;
@@ -377,10 +374,7 @@ public void CreateValidationMetadata(ValidationMetadataProviderContext context)
377374 }
378375 else
379376 {
380- addInferredRequiredAttribute = IsNullableReferenceType (
381- property . DeclaringType ! ,
382- member : null ,
383- context . PropertyAttributes ! ) ;
377+ addInferredRequiredAttribute = IsRequired ( context ) ;
384378 }
385379 }
386380 else if ( context . Key . MetadataKind == ModelMetadataKind . Parameter )
@@ -389,11 +383,9 @@ public void CreateValidationMetadata(ValidationMetadataProviderContext context)
389383 // since the parameter will be optional.
390384 if ( ! context . Key . ParameterInfo ! . HasDefaultValue )
391385 {
392- addInferredRequiredAttribute = IsNullableReferenceType (
393- context . Key . ParameterInfo ! . Member . ReflectedType ,
394- context . Key . ParameterInfo . Member ,
395- context . ParameterAttributes ! ) ;
386+ addInferredRequiredAttribute = IsRequired ( context ) ;
396387 }
388+
397389 }
398390 else
399391 {
@@ -467,14 +459,17 @@ private static string GetDisplayGroup(FieldInfo field)
467459 return string . Empty ;
468460 }
469461
470- internal static bool IsNullableReferenceType ( Type ? containingType , MemberInfo ? member , IEnumerable < object > attributes )
462+ internal static bool IsRequired ( ValidationMetadataProviderContext context )
471463 {
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 ;
478473 }
479474
480475 // Internal for testing
@@ -508,67 +503,4 @@ internal static bool HasNullableAttribute(IEnumerable<object> attributes, out bo
508503 isNullable = false ;
509504 return true ; // [Nullable] found but type is not an NNRT
510505 }
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- }
574506}
0 commit comments