Add a custom collection type for ModelMetadata.Properties
This is a cleanup PR to improve the common usage of ModelMetadata.Properties. We found placed in code where both .Count and the ability to index by property name would be useful. I was able to cascade this and simplify the ModelBindingContext as well.
This commit is contained in:
parent
8a9dc991ce
commit
8399dc5f4e
|
|
@ -541,7 +541,6 @@ namespace Microsoft.AspNet.Mvc.Description
|
|||
// Order - source: Body
|
||||
//
|
||||
|
||||
var propertyCount = 0;
|
||||
var unboundProperties = new HashSet<ModelMetadata>();
|
||||
|
||||
// We don't want to append the **parameter** name when building a model name.
|
||||
|
|
@ -553,7 +552,6 @@ namespace Microsoft.AspNet.Mvc.Description
|
|||
|
||||
foreach (var propertyMetadata in modelMetadata.Properties)
|
||||
{
|
||||
propertyCount++;
|
||||
var key = new PropertyKey(propertyMetadata, source);
|
||||
|
||||
if (Visited.Add(key))
|
||||
|
|
@ -569,7 +567,7 @@ namespace Microsoft.AspNet.Mvc.Description
|
|||
}
|
||||
}
|
||||
|
||||
if (unboundProperties.Count == propertyCount)
|
||||
if (unboundProperties.Count == modelMetadata.Properties.Count)
|
||||
{
|
||||
if (source == null || source == ambientSource)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -118,8 +118,7 @@ namespace Microsoft.AspNet.Mvc.Rendering.Expressions
|
|||
else
|
||||
{
|
||||
// Try getting a property from ModelMetadata if we couldn't find an answer in ViewData
|
||||
var propertyMetadata =
|
||||
viewData.ModelMetadata.Properties.Where(p => p.PropertyName == expression).FirstOrDefault();
|
||||
var propertyMetadata = viewData.ModelMetadata.Properties[expression];
|
||||
if (propertyMetadata != null)
|
||||
{
|
||||
return propertyMetadata;
|
||||
|
|
|
|||
|
|
@ -315,7 +315,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
foreach (var property in properties)
|
||||
{
|
||||
var propertyName = property.Name;
|
||||
var propertyMetadata = bindingContext.PropertyMetadata[propertyName];
|
||||
var propertyMetadata = bindingContext.ModelMetadata.Properties[propertyName];
|
||||
var requiredValidator = bindingContext.OperationBindingContext
|
||||
.ValidatorProvider
|
||||
.GetValidators(propertyMetadata)
|
||||
|
|
@ -366,7 +366,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
// Update Model as SetProperty() would: Place null value where validator will check for non-null. This
|
||||
// ensures a failure result from a required validator (if any) even for a non-nullable property.
|
||||
// (Otherwise, propertyMetadata.Model is likely already null.)
|
||||
var propertyMetadata = bindingContext.PropertyMetadata[missingRequiredProperty];
|
||||
var propertyMetadata = bindingContext.ModelMetadata.Properties[missingRequiredProperty];
|
||||
propertyMetadata.Model = null;
|
||||
|
||||
// Execute validator (if any) to get custom error message.
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
private Func<object> _modelAccessor;
|
||||
private int _order = DefaultOrder;
|
||||
private bool _isRequired;
|
||||
private IEnumerable<ModelMetadata> _properties;
|
||||
private ModelPropertyCollection _properties;
|
||||
private Type _realModelType;
|
||||
private string _simpleDisplayText;
|
||||
|
||||
|
|
@ -193,14 +193,17 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
|
||||
public virtual string NullDisplayText { get; set; }
|
||||
|
||||
public virtual IEnumerable<ModelMetadata> Properties
|
||||
/// <summary>
|
||||
/// Gets the collection of <see cref="ModelMetadata"/> instances for the model's properties.
|
||||
/// </summary>
|
||||
public virtual ModelPropertyCollection Properties
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_properties == null)
|
||||
{
|
||||
var properties = Provider.GetMetadataForProperties(Model, RealModelType);
|
||||
_properties = properties.OrderBy(m => m.Order).ToList();
|
||||
_properties = new ModelPropertyCollection(properties.OrderBy(m => m.Order));
|
||||
}
|
||||
|
||||
return _properties;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,83 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||
{
|
||||
/// <summary>
|
||||
/// A read-only list of <see cref="ModelMetadata"/> objects which represent model properties.
|
||||
/// </summary>
|
||||
public class ModelPropertyCollection : IReadOnlyList<ModelMetadata>
|
||||
{
|
||||
private readonly List<ModelMetadata> _properties;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="ModelPropertyCollection"/>.
|
||||
/// </summary>
|
||||
/// <param name="properties">The properties.</param>
|
||||
public ModelPropertyCollection([NotNull] IEnumerable<ModelMetadata> properties)
|
||||
{
|
||||
_properties = new List<ModelMetadata>(properties);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public ModelMetadata this[int index]
|
||||
{
|
||||
get
|
||||
{
|
||||
return _properties[index];
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a <see cref="ModelMetadata"/> instance for the property corresponding to <paramref name="propertyName"/>.
|
||||
/// </summary>
|
||||
/// <param name="propertyName">
|
||||
/// The property name. Property names are compared using <see cref="StringComparison.Ordinal"/>
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// The <see cref="ModelMetadata"/> instance for the property specified by <paramref name="propertyName"/>, or null
|
||||
/// if no match can be found.
|
||||
/// </returns>
|
||||
public ModelMetadata this[[NotNull] string propertyName]
|
||||
{
|
||||
|
||||
get
|
||||
{
|
||||
foreach (var property in _properties)
|
||||
{
|
||||
if (string.Equals(property.PropertyName, propertyName, StringComparison.Ordinal))
|
||||
{
|
||||
return property;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public int Count
|
||||
{
|
||||
get
|
||||
{
|
||||
return _properties.Count;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerator<ModelMetadata> GetEnumerator()
|
||||
{
|
||||
return _properties.GetEnumerator();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -17,7 +17,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
|
||||
private string _modelName;
|
||||
private ModelStateDictionary _modelState;
|
||||
private Dictionary<string, ModelMetadata> _propertyMetadata;
|
||||
private ModelValidationNode _validationNode;
|
||||
private Func<ModelBindingContext, string, bool> _propertyFilter;
|
||||
|
||||
|
|
@ -152,25 +151,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
/// </summary>
|
||||
public IValueProvider ValueProvider { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a dictionary of property name to <see cref="ModelMetadata"/> instances for
|
||||
/// <see cref="ModelMetadata.Properties"/>
|
||||
/// </summary>
|
||||
public IDictionary<string, ModelMetadata> PropertyMetadata
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_propertyMetadata == null)
|
||||
{
|
||||
_propertyMetadata = ModelMetadata.Properties
|
||||
.ToDictionary(m => m.PropertyName,
|
||||
StringComparer.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
return _propertyMetadata;
|
||||
}
|
||||
}
|
||||
|
||||
public Func<ModelBindingContext, string, bool> PropertyFilter
|
||||
{
|
||||
get
|
||||
|
|
|
|||
|
|
@ -141,8 +141,8 @@ namespace Microsoft.AspNet.Mvc.Core
|
|||
|
||||
var model = new DefaultTemplatesUtilities.ObjectTemplateModel { Property1 = "p1", Property2 = null };
|
||||
var html = DefaultTemplatesUtilities.GetHtmlHelper(model);
|
||||
var metadata =
|
||||
html.ViewData.ModelMetadata.Properties.First(m => string.Equals(m.PropertyName, "Property1"));
|
||||
|
||||
var metadata = html.ViewData.ModelMetadata.Properties["Property1"];
|
||||
metadata.HideSurroundingHtml = true;
|
||||
|
||||
// Act
|
||||
|
|
|
|||
|
|
@ -187,8 +187,8 @@ Environment.NewLine;
|
|||
|
||||
var model = new DefaultTemplatesUtilities.ObjectTemplateModel { Property1 = "p1", Property2 = null };
|
||||
var html = DefaultTemplatesUtilities.GetHtmlHelper(model);
|
||||
var metadata =
|
||||
html.ViewData.ModelMetadata.Properties.First(m => string.Equals(m.PropertyName, "Property1"));
|
||||
|
||||
var metadata = html.ViewData.ModelMetadata.Properties["Property1"];
|
||||
metadata.HideSurroundingHtml = true;
|
||||
|
||||
// Act
|
||||
|
|
@ -340,8 +340,8 @@ Environment.NewLine;
|
|||
viewEngine.Object,
|
||||
innerHelper => new StubbyHtmlHelper(innerHelper));
|
||||
helper.ViewData["Property1"] = "True";
|
||||
var metadata =
|
||||
helper.ViewData.ModelMetadata.Properties.First(m => string.Equals(m.PropertyName, "Property1"));
|
||||
|
||||
var metadata = helper.ViewData.ModelMetadata.Properties["Property1"];
|
||||
metadata.DataTypeName = templateName;
|
||||
|
||||
// TemplateBuilder sets FormattedModelValue before calling TemplateRenderer and it's used in most templates.
|
||||
|
|
@ -372,8 +372,8 @@ Environment.NewLine;
|
|||
model,
|
||||
viewEngine.Object,
|
||||
innerHelper => new StubbyHtmlHelper(innerHelper));
|
||||
var metadata =
|
||||
helper.ViewData.ModelMetadata.Properties.First(m => string.Equals(m.PropertyName, "Property1"));
|
||||
|
||||
var metadata = helper.ViewData.ModelMetadata.Properties["Property1"];
|
||||
metadata.DataTypeName = templateName;
|
||||
|
||||
// TemplateBuilder sets FormattedModelValue before calling TemplateRenderer and it's used in most templates.
|
||||
|
|
@ -405,8 +405,8 @@ Environment.NewLine;
|
|||
viewEngine.Object,
|
||||
innerHelper => new StubbyHtmlHelper(innerHelper));
|
||||
helper.ViewData["Property1"] = "True";
|
||||
var metadata =
|
||||
helper.ViewData.ModelMetadata.Properties.First(m => string.Equals(m.PropertyName, "Property1"));
|
||||
|
||||
var metadata = helper.ViewData.ModelMetadata.Properties["Property1"];
|
||||
metadata.TemplateHint = templateName;
|
||||
|
||||
// TemplateBuilder sets FormattedModelValue before calling TemplateRenderer and it's used in most templates.
|
||||
|
|
@ -437,8 +437,8 @@ Environment.NewLine;
|
|||
model,
|
||||
viewEngine.Object,
|
||||
innerHelper => new StubbyHtmlHelper(innerHelper));
|
||||
var metadata =
|
||||
helper.ViewData.ModelMetadata.Properties.First(m => string.Equals(m.PropertyName, "Property1"));
|
||||
|
||||
var metadata = helper.ViewData.ModelMetadata.Properties["Property1"];
|
||||
metadata.TemplateHint = templateName;
|
||||
|
||||
// TemplateBuilder sets FormattedModelValue before calling TemplateRenderer and it's used in most templates.
|
||||
|
|
|
|||
|
|
@ -313,6 +313,7 @@ namespace Microsoft.AspNet.Mvc.Core
|
|||
{
|
||||
MetadataProvider = new Mock<IModelMetadataProvider>();
|
||||
Metadata = new Mock<ModelMetadata>(MetadataProvider.Object, null, null, typeof(object), null);
|
||||
Metadata.SetupGet(m => m.Properties).CallBase();
|
||||
|
||||
MetadataProvider.Setup(p => p.GetMetadataForProperties(It.IsAny<object>(), It.IsAny<Type>()))
|
||||
.Returns(new ModelMetadata[0]);
|
||||
|
|
|
|||
|
|
@ -319,6 +319,7 @@ namespace Microsoft.AspNet.Mvc.Core
|
|||
{
|
||||
MetadataProvider = new Mock<IModelMetadataProvider>();
|
||||
Metadata = new Mock<ModelMetadata>(MetadataProvider.Object, null, null, typeof(object), null);
|
||||
Metadata.SetupGet(m => m.Properties).CallBase();
|
||||
|
||||
MetadataProvider.Setup(p => p.GetMetadataForProperties(It.IsAny<object>(), It.IsAny<Type>()))
|
||||
.Returns(new ModelMetadata[0]);
|
||||
|
|
|
|||
|
|
@ -185,7 +185,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
// Arrange
|
||||
var provider = new DataAnnotationsModelMetadataProvider();
|
||||
var metadata = provider.GetMetadataForType(modelAccessor: null, modelType: typeof(ClassWithHiddenProperties));
|
||||
var property = metadata.Properties.First(m => string.Equals("DirectlyHidden", m.PropertyName));
|
||||
var property = metadata.Properties["DirectlyHidden"];
|
||||
|
||||
// Act
|
||||
var result = property.HideSurroundingHtml;
|
||||
|
|
@ -200,7 +200,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
// Arrange
|
||||
var provider = new DataAnnotationsModelMetadataProvider();
|
||||
var metadata = provider.GetMetadataForType(modelAccessor: null, modelType: typeof(ClassWithHiddenProperties));
|
||||
var property = metadata.Properties.First(m => string.Equals("OfHiddenType", m.PropertyName));
|
||||
var property = metadata.Properties["OfHiddenType"];
|
||||
|
||||
// Act
|
||||
var result = property.HideSurroundingHtml;
|
||||
|
|
|
|||
Loading…
Reference in New Issue