Remove some Linq usages in ComponentModel.Annotations (#56753)
authorEric Erhardt <eric.erhardt@microsoft.com>
Thu, 5 Aug 2021 03:40:07 +0000 (22:40 -0500)
committerGitHub <noreply@github.com>
Thu, 5 Aug 2021 03:40:07 +0000 (03:40 +0000)
* Remove some Linq usages in ComponentModel.Annotations

Contributes to #56631

* PR feedback

* fixup

src/libraries/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/CompareAttribute.cs
src/libraries/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/EnumDataTypeAttribute.cs
src/libraries/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/MetadataPropertyDescriptorWrapper.cs
src/libraries/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/ValidationAttributeStore.cs
src/libraries/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/Validator.cs

index cd995d2..4e3b82c 100644 (file)
@@ -1,9 +1,9 @@
 // Licensed to the .NET Foundation under one or more agreements.
 // The .NET Foundation licenses this file to you under the MIT license.
 
+using System.Collections.Generic;
 using System.Diagnostics.CodeAnalysis;
 using System.Globalization;
-using System.Linq;
 using System.Reflection;
 
 namespace System.ComponentModel.DataAnnotations
@@ -36,7 +36,7 @@ namespace System.ComponentModel.DataAnnotations
             {
                 return new ValidationResult(SR.Format(SR.CompareAttribute_UnknownProperty, OtherProperty));
             }
-            if (otherPropertyInfo.GetIndexParameters().Any())
+            if (otherPropertyInfo.GetIndexParameters().Length > 0)
             {
                 throw new ArgumentException(SR.Format(SR.Common_PropertyNotFound, validationContext.ObjectType.FullName, OtherProperty));
             }
@@ -60,11 +60,13 @@ namespace System.ComponentModel.DataAnnotations
 
         private string? GetDisplayNameForProperty(PropertyInfo property)
         {
-            var attributes = CustomAttributeExtensions.GetCustomAttributes(property, true);
-            var display = attributes.OfType<DisplayAttribute>().FirstOrDefault();
-            if (display != null)
+            IEnumerable<Attribute> attributes = CustomAttributeExtensions.GetCustomAttributes(property, true);
+            foreach (Attribute attribute in attributes)
             {
-                return display.GetName();
+                if (attribute is DisplayAttribute display)
+                {
+                   return display.GetName();
+                }
             }
 
             return OtherProperty;
index 18dd723..ab5c549 100644 (file)
@@ -3,7 +3,6 @@
 
 using System.Diagnostics;
 using System.Globalization;
-using System.Linq;
 
 namespace System.ComponentModel.DataAnnotations
 {
@@ -100,7 +99,7 @@ namespace System.ComponentModel.DataAnnotations
         }
 
         private static bool IsEnumTypeInFlagsMode(Type enumType) =>
-            enumType.GetCustomAttributes(typeof(FlagsAttribute), false).Any();
+            enumType.IsDefined(typeof(FlagsAttribute), false);
 
         private static string? GetUnderlyingTypeValueString(Type enumType, object enumValue) =>
             Convert.ChangeType(enumValue, Enum.GetUnderlyingType(enumType), CultureInfo.InvariantCulture).ToString();
index b3f21b8..1f3204b 100644 (file)
@@ -1,10 +1,6 @@
 // Licensed to the .NET Foundation under one or more agreements.
 // The .NET Foundation licenses this file to you under the MIT license.
 
-using System;
-using System.ComponentModel;
-using System.Linq;
-
 namespace System.ComponentModel.DataAnnotations
 {
     internal sealed class MetadataPropertyDescriptorWrapper : PropertyDescriptor
@@ -16,8 +12,15 @@ namespace System.ComponentModel.DataAnnotations
             : base(descriptor, newAttributes)
         {
             _descriptor = descriptor;
-            var readOnlyAttribute = newAttributes.OfType<ReadOnlyAttribute>().FirstOrDefault();
-            _isReadOnly = (readOnlyAttribute != null ? readOnlyAttribute.IsReadOnly : false);
+
+            foreach (Attribute attribute in newAttributes)
+            {
+                if (attribute is ReadOnlyAttribute readOnlyAttribute)
+                {
+                    _isReadOnly = readOnlyAttribute.IsReadOnly;
+                    break;
+                }
+            }
         }
 
         public override void AddValueChanged(object component, EventHandler handler) { _descriptor.AddValueChanged(component, handler); }
index 6fa0655..f7a2680 100644 (file)
@@ -4,7 +4,6 @@
 using System.Collections.Generic;
 using System.Diagnostics;
 using System.Diagnostics.CodeAnalysis;
-using System.Globalization;
 using System.Linq;
 using System.Reflection;
 
@@ -124,7 +123,7 @@ namespace System.ComponentModel.DataAnnotations
             {
                 if (!_typeStoreItems.TryGetValue(type, out TypeStoreItem? item))
                 {
-                    var attributes = TypeDescriptor.GetAttributes(type).Cast<Attribute>();
+                    AttributeCollection attributes = TypeDescriptor.GetAttributes(type);
                     item = new TypeStoreItem(type, attributes);
                     _typeStoreItems[type] = item;
                 }
@@ -153,7 +152,7 @@ namespace System.ComponentModel.DataAnnotations
         /// </summary>
         private abstract class StoreItem
         {
-            internal StoreItem(IEnumerable<Attribute> attributes)
+            internal StoreItem(AttributeCollection attributes)
             {
                 ValidationAttributes = attributes.OfType<ValidationAttribute>();
                 DisplayAttribute = attributes.OfType<DisplayAttribute>().SingleOrDefault();
@@ -176,7 +175,7 @@ namespace System.ComponentModel.DataAnnotations
             private readonly Type _type;
             private Dictionary<string, PropertyStoreItem>? _propertyStoreItems;
 
-            internal TypeStoreItem([DynamicallyAccessedMembers(DynamicallyAccessedTypes)] Type type, IEnumerable<Attribute> attributes)
+            internal TypeStoreItem([DynamicallyAccessedMembers(DynamicallyAccessedTypes)] Type type, AttributeCollection attributes)
                 : base(attributes)
             {
                 _type = type;
@@ -224,7 +223,7 @@ namespace System.ComponentModel.DataAnnotations
                 var properties = TypeDescriptor.GetProperties(_type);
                 foreach (PropertyDescriptor property in properties)
                 {
-                    var item = new PropertyStoreItem(property.PropertyType, GetExplicitAttributes(property).Cast<Attribute>());
+                    var item = new PropertyStoreItem(property.PropertyType, GetExplicitAttributes(property));
                     propertyStoreItems[property.Name] = item;
                 }
 
@@ -243,8 +242,14 @@ namespace System.ComponentModel.DataAnnotations
             [RequiresUnreferencedCode("The Type of propertyDescriptor.PropertyType cannot be statically discovered.")]
             private AttributeCollection GetExplicitAttributes(PropertyDescriptor propertyDescriptor)
             {
-                List<Attribute> attributes = new List<Attribute>(propertyDescriptor.Attributes.Cast<Attribute>());
-                IEnumerable<Attribute> typeAttributes = TypeDescriptor.GetAttributes(propertyDescriptor.PropertyType).Cast<Attribute>();
+                AttributeCollection propertyDescriptorAttributes = propertyDescriptor.Attributes;
+                List<Attribute> attributes = new List<Attribute>(propertyDescriptorAttributes.Count);
+                foreach (Attribute attribute in propertyDescriptorAttributes)
+                {
+                    attributes.Add(attribute);
+                }
+
+                AttributeCollection typeAttributes = TypeDescriptor.GetAttributes(propertyDescriptor.PropertyType);
                 bool removedAttribute = false;
                 foreach (Attribute attr in typeAttributes)
                 {
@@ -259,7 +264,7 @@ namespace System.ComponentModel.DataAnnotations
                         }
                     }
                 }
-                return removedAttribute ? new AttributeCollection(attributes.ToArray()) : propertyDescriptor.Attributes;
+                return removedAttribute ? new AttributeCollection(attributes.ToArray()) : propertyDescriptorAttributes;
             }
         }
 
@@ -268,7 +273,7 @@ namespace System.ComponentModel.DataAnnotations
         /// </summary>
         private sealed class PropertyStoreItem : StoreItem
         {
-            internal PropertyStoreItem(Type propertyType, IEnumerable<Attribute> attributes)
+            internal PropertyStoreItem(Type propertyType, AttributeCollection attributes)
                 : base(attributes)
             {
                 Debug.Assert(propertyType != null);
index c702de8..3e40940 100644 (file)
@@ -4,9 +4,7 @@
 using System.Collections.Generic;
 using System.Diagnostics;
 using System.Diagnostics.CodeAnalysis;
-using System.Globalization;
 using System.Linq;
-using System.Reflection;
 
 namespace System.ComponentModel.DataAnnotations
 {
@@ -190,6 +188,8 @@ namespace System.ComponentModel.DataAnnotations
         public static bool TryValidateValue(object value, ValidationContext validationContext,
             ICollection<ValidationResult>? validationResults, IEnumerable<ValidationAttribute> validationAttributes)
         {
+            ArgumentNullException.ThrowIfNull(validationAttributes);
+
             var result = true;
             var breakOnFirstError = validationResults == null;
 
@@ -224,8 +224,11 @@ namespace System.ComponentModel.DataAnnotations
 
             var attributes = _store.GetPropertyValidationAttributes(validationContext);
 
-            GetValidationErrors(value, validationContext, attributes, false).FirstOrDefault()
-                ?.ThrowValidationException();
+            List<ValidationError> errors = GetValidationErrors(value, validationContext, attributes, false);
+            if (errors.Count > 0)
+            {
+                errors[0].ThrowValidationException();
+            }
         }
 
         /// <summary>
@@ -289,7 +292,11 @@ namespace System.ComponentModel.DataAnnotations
                 throw new ArgumentException(SR.Validator_InstanceMustMatchValidationContextInstance, nameof(instance));
             }
 
-            GetObjectValidationErrors(instance, validationContext, validateAllProperties, false).FirstOrDefault()?.ThrowValidationException();
+            List<ValidationError> errors = GetObjectValidationErrors(instance, validationContext, validateAllProperties, false);
+            if (errors.Count > 0)
+            {
+                errors[0].ThrowValidationException();
+            }
         }
 
         /// <summary>
@@ -312,12 +319,14 @@ namespace System.ComponentModel.DataAnnotations
         public static void ValidateValue(object value, ValidationContext validationContext,
             IEnumerable<ValidationAttribute> validationAttributes)
         {
-            if (validationContext == null)
+            ArgumentNullException.ThrowIfNull(validationContext);
+            ArgumentNullException.ThrowIfNull(validationAttributes);
+
+            List<ValidationError> errors = GetValidationErrors(value, validationContext, validationAttributes, false);
+            if (errors.Count > 0)
             {
-                throw new ArgumentNullException(nameof(validationContext));
+                errors[0].ThrowValidationException();
             }
-
-            GetValidationErrors(value, validationContext, validationAttributes, false).FirstOrDefault()?.ThrowValidationException();
         }
 
         /// <summary>
@@ -399,7 +408,7 @@ namespace System.ComponentModel.DataAnnotations
         ///     <see cref="ValidationContext.ObjectInstance" /> on <paramref name="validationContext" />.
         /// </exception>
         [RequiresUnreferencedCode(ValidationContext.InstanceTypeNotStaticallyDiscovered)]
-        private static IEnumerable<ValidationError> GetObjectValidationErrors(object instance,
+        private static List<ValidationError> GetObjectValidationErrors(object instance,
             ValidationContext validationContext, bool validateAllProperties, bool breakOnFirstError)
         {
             Debug.Assert(instance != null);
@@ -415,7 +424,7 @@ namespace System.ComponentModel.DataAnnotations
                 breakOnFirstError));
 
             // We only proceed to Step 2 if there are no errors
-            if (errors.Any())
+            if (errors.Count > 0)
             {
                 return errors;
             }
@@ -425,7 +434,7 @@ namespace System.ComponentModel.DataAnnotations
             errors.AddRange(GetValidationErrors(instance, validationContext, attributes, breakOnFirstError));
 
             // We only proceed to Step 3 if there are no errors
-            if (errors.Any())
+            if (errors.Count > 0)
             {
                 return errors;
             }
@@ -437,9 +446,12 @@ namespace System.ComponentModel.DataAnnotations
 
                 if (results != null)
                 {
-                    foreach (var result in results.Where(r => r != ValidationResult.Success))
+                    foreach (ValidationResult result in results)
                     {
-                        errors.Add(new ValidationError(null, instance, result));
+                        if (result != ValidationResult.Success)
+                        {
+                            errors.Add(new ValidationError(null, instance, result));
+                        }
                     }
                 }
             }
@@ -477,21 +489,24 @@ namespace System.ComponentModel.DataAnnotations
                 }
                 else
                 {
-                    // only validate the Required attributes
-                    var reqAttr = attributes.OfType<RequiredAttribute>().FirstOrDefault();
-                    if (reqAttr != null)
+                    // only validate the first Required attribute
+                    foreach (ValidationAttribute attribute in attributes)
                     {
-                        // Note: we let the [Required] attribute do its own null testing,
-                        // since the user may have subclassed it and have a deeper meaning to what 'required' means
-                        var validationResult = reqAttr.GetValidationResult(property.Value, property.Key);
-                        if (validationResult != ValidationResult.Success)
+                        if (attribute is RequiredAttribute reqAttr)
                         {
-                            errors.Add(new ValidationError(reqAttr, property.Value, validationResult!));
+                            // Note: we let the [Required] attribute do its own null testing,
+                            // since the user may have subclassed it and have a deeper meaning to what 'required' means
+                            var validationResult = reqAttr.GetValidationResult(property.Value, property.Key);
+                            if (validationResult != ValidationResult.Success)
+                            {
+                                errors.Add(new ValidationError(reqAttr, property.Value, validationResult!));
+                            }
+                            break;
                         }
                     }
                 }
 
-                if (breakOnFirstError && errors.Any())
+                if (breakOnFirstError && errors.Count > 0)
                 {
                     break;
                 }
@@ -547,7 +562,7 @@ namespace System.ComponentModel.DataAnnotations
         /// </param>
         /// <returns>The collection of validation errors.</returns>
         /// <exception cref="ArgumentNullException">When <paramref name="validationContext" /> is null.</exception>
-        private static IEnumerable<ValidationError> GetValidationErrors(object? value,
+        private static List<ValidationError> GetValidationErrors(object? value,
             ValidationContext validationContext, IEnumerable<ValidationAttribute> attributes, bool breakOnFirstError)
         {
             if (validationContext == null)
@@ -559,18 +574,23 @@ namespace System.ComponentModel.DataAnnotations
             ValidationError? validationError;
 
             // Get the required validator if there is one and test it first, aborting on failure
-            var required = attributes.OfType<RequiredAttribute>().FirstOrDefault();
-            if (required != null)
+            RequiredAttribute? required = null;
+            foreach (ValidationAttribute attribute in attributes)
             {
-                if (!TryValidate(value, validationContext, required, out validationError))
+                required = attribute as RequiredAttribute;
+                if (required is not null)
                 {
-                    errors.Add(validationError);
-                    return errors;
+                    if (!TryValidate(value, validationContext, required, out validationError))
+                    {
+                        errors.Add(validationError);
+                        return errors;
+                    }
+                    break;
                 }
             }
 
             // Iterate through the rest of the validators, skipping the required validator
-            foreach (var attr in attributes)
+            foreach (ValidationAttribute attr in attributes)
             {
                 if (attr != required)
                 {