Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add validation for MetadataType attribute - Issue #46678 #51772

Merged
2 commits merged into from
Jul 28, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -124,8 +124,7 @@ private TypeStoreItem GetTypeStoreItem([DynamicallyAccessedMembers(TypeStoreItem
{
if (!_typeStoreItems.TryGetValue(type, out TypeStoreItem? item))
{
// use CustomAttributeExtensions.GetCustomAttributes() to get inherited attributes as well as direct ones
var attributes = CustomAttributeExtensions.GetCustomAttributes(type, true);
var attributes = TypeDescriptor.GetAttributes(type).Cast<Attribute>();
item = new TypeStoreItem(type, attributes);
_typeStoreItems[type] = item;
}
Expand Down Expand Up @@ -170,7 +169,7 @@ internal StoreItem(IEnumerable<Attribute> attributes)
/// </summary>
private sealed class TypeStoreItem : StoreItem
{
internal const DynamicallyAccessedMemberTypes DynamicallyAccessedTypes = DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties;
internal const DynamicallyAccessedMemberTypes DynamicallyAccessedTypes = DynamicallyAccessedMemberTypes.All;

private readonly object _syncRoot = new object();
[DynamicallyAccessedMembers(DynamicallyAccessedTypes)]
Expand All @@ -183,6 +182,7 @@ internal TypeStoreItem([DynamicallyAccessedMembers(DynamicallyAccessedTypes)] Ty
_type = type;
}

[RequiresUnreferencedCode("The Types of _type's properties cannot be statically discovered.")]
internal PropertyStoreItem GetPropertyStoreItem(string propertyName)
{
if (!TryGetPropertyStoreItem(propertyName, out PropertyStoreItem? item))
Expand All @@ -194,6 +194,7 @@ internal PropertyStoreItem GetPropertyStoreItem(string propertyName)
return item;
}

[RequiresUnreferencedCode("The Types of _type's properties cannot be statically discovered.")]
internal bool TryGetPropertyStoreItem(string propertyName, [NotNullWhen(true)] out PropertyStoreItem? item)
{
if (string.IsNullOrEmpty(propertyName))
Expand All @@ -215,23 +216,51 @@ internal bool TryGetPropertyStoreItem(string propertyName, [NotNullWhen(true)] o
return _propertyStoreItems.TryGetValue(propertyName, out item);
}

[RequiresUnreferencedCode("The Types of _type's properties cannot be statically discovered.")]
private Dictionary<string, PropertyStoreItem> CreatePropertyStoreItems()
{
var propertyStoreItems = new Dictionary<string, PropertyStoreItem>();

// exclude index properties to match old TypeDescriptor functionality
var properties = _type.GetRuntimeProperties()
.Where(prop => IsPublic(prop) && !prop.GetIndexParameters().Any());
foreach (PropertyInfo property in properties)
var properties = TypeDescriptor.GetProperties(_type);
foreach (PropertyDescriptor property in properties)
{
// use CustomAttributeExtensions.GetCustomAttributes() to get inherited attributes as well as direct ones
var item = new PropertyStoreItem(property.PropertyType,
CustomAttributeExtensions.GetCustomAttributes(property, true));
var item = new PropertyStoreItem(property.PropertyType, GetExplicitAttributes(property).Cast<Attribute>());
propertyStoreItems[property.Name] = item;
}

return propertyStoreItems;
}

/// <summary>
/// Method to extract only the explicitly specified attributes from a <see cref="PropertyDescriptor"/>
/// </summary>
/// <remarks>
/// Normal TypeDescriptor semantics are to inherit the attributes of a property's type. This method
/// exists to suppress those inherited attributes.
/// </remarks>
/// <param name="propertyDescriptor">The property descriptor whose attributes are needed.</param>
/// <returns>A new <see cref="AttributeCollection"/> stripped of any attributes from the property's type.</returns>
[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>();
bool removedAttribute = false;
foreach (Attribute attr in typeAttributes)
{
for (int i = attributes.Count - 1; i >= 0; --i)
{
// We must use ReferenceEquals since attributes could Match if they are the same.
// Only ReferenceEquals will catch actual duplications.
if (object.ReferenceEquals(attr, attributes[i]))
{
attributes.RemoveAt(i);
removedAttribute = true;
}
}
}
return removedAttribute ? new AttributeCollection(attributes.ToArray()) : propertyDescriptor.Attributes;
}
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -514,17 +514,16 @@ private static IEnumerable<ValidationError> GetObjectPropertyValidationErrors(ob
private static ICollection<KeyValuePair<ValidationContext, object?>> GetPropertyValues(object instance,
ValidationContext validationContext)
{
var properties = instance.GetType().GetRuntimeProperties()
.Where(p => ValidationAttributeStore.IsPublic(p) && !p.GetIndexParameters().Any());
var items = new List<KeyValuePair<ValidationContext, object?>>(properties.Count());
foreach (var property in properties)
var properties = TypeDescriptor.GetProperties(instance);
var items = new List<KeyValuePair<ValidationContext, object?>>(properties.Count);
foreach (PropertyDescriptor property in properties)
{
var context = CreateValidationContext(instance, validationContext);
context.MemberName = property.Name;

if (_store.GetPropertyValidationAttributes(context).Any())
{
items.Add(new KeyValuePair<ValidationContext, object?>(context, property.GetValue(instance, null)));
items.Add(new KeyValuePair<ValidationContext, object?>(context, property.GetValue(instance)));
}
}

Expand Down
Loading