Munch on less memory when handling property metadata

- #5499
- switch `foreach` to `for` and use less Linq when accessing `modelMetadata.Properties`
- change backing field for `ModelExplorer.Properties` from a list to an array
This commit is contained in:
Doug Bunting 2016-11-24 17:11:29 -08:00
parent 48546dbb28
commit 7178464ed2
7 changed files with 50 additions and 28 deletions

View File

@ -623,7 +623,7 @@ namespace Microsoft.AspNetCore.Mvc.ApiExplorer
// //
if (modelMetadata.IsEnumerableType || if (modelMetadata.IsEnumerableType ||
!modelMetadata.IsComplexType || !modelMetadata.IsComplexType ||
!modelMetadata.Properties.Any()) modelMetadata.Properties.Count == 0)
{ {
Context.Results.Add(CreateResult(bindingContext, source ?? ambientSource, containerName)); Context.Results.Add(CreateResult(bindingContext, source ?? ambientSource, containerName));
return; return;
@ -656,10 +656,10 @@ namespace Microsoft.AspNetCore.Mvc.ApiExplorer
newContainerName = GetName(containerName, bindingContext); newContainerName = GetName(containerName, bindingContext);
} }
foreach (var propertyMetadata in modelMetadata.Properties) for (var i = 0; i < modelMetadata.Properties.Count; i++)
{ {
var propertyMetadata = modelMetadata.Properties[i];
var key = new PropertyKey(propertyMetadata, source); var key = new PropertyKey(propertyMetadata, source);
var propertyContext = ApiParameterDescriptionContext.GetContext( var propertyContext = ApiParameterDescriptionContext.GetContext(
propertyMetadata, propertyMetadata,
bindingInfo: null, bindingInfo: null,

View File

@ -60,8 +60,9 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Binders
bindingContext.Model = CreateModel(bindingContext); bindingContext.Model = CreateModel(bindingContext);
} }
foreach (var property in bindingContext.ModelMetadata.Properties) for (var i = 0; i < bindingContext.ModelMetadata.Properties.Count; i++)
{ {
var property = bindingContext.ModelMetadata.Properties[i];
if (!CanBindProperty(bindingContext, property)) if (!CanBindProperty(bindingContext, property))
{ {
continue; continue;
@ -228,8 +229,9 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Binders
// //
var hasBindableProperty = false; var hasBindableProperty = false;
var isAnyPropertyEnabledForValueProviderBasedBinding = false; var isAnyPropertyEnabledForValueProviderBasedBinding = false;
foreach (var propertyMetadata in bindingContext.ModelMetadata.Properties) for (var i = 0; i < bindingContext.ModelMetadata.Properties.Count; i++)
{ {
var propertyMetadata = bindingContext.ModelMetadata.Properties[i];
if (!CanBindProperty(bindingContext, propertyMetadata)) if (!CanBindProperty(bindingContext, propertyMetadata))
{ {
continue; continue;

View File

@ -2,8 +2,8 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System; using System;
using System.Reflection;
using System.Collections.Generic; using System.Collections.Generic;
using System.Reflection;
namespace Microsoft.AspNetCore.Mvc.ModelBinding.Binders namespace Microsoft.AspNetCore.Mvc.ModelBinding.Binders
{ {
@ -25,8 +25,9 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Binders
HasDefaultConstructor(context.Metadata.ModelType.GetTypeInfo())) HasDefaultConstructor(context.Metadata.ModelType.GetTypeInfo()))
{ {
var propertyBinders = new Dictionary<ModelMetadata, IModelBinder>(); var propertyBinders = new Dictionary<ModelMetadata, IModelBinder>();
foreach (var property in context.Metadata.Properties) for (var i = 0; i < context.Metadata.Properties.Count; i++)
{ {
var property = context.Metadata.Properties[i];
propertyBinders.Add(property, context.CreateBinder(property)); propertyBinders.Add(property, context.CreateBinder(property));
} }

View File

@ -463,8 +463,9 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Internal
} }
else if (modelMetadata.IsComplexType) else if (modelMetadata.IsComplexType)
{ {
foreach (var property in modelMetadata.Properties) for (var i = 0; i < modelMetadata.Properties.Count; i++)
{ {
var property = modelMetadata.Properties[i];
modelState.ClearValidationState(property.BinderModelName ?? property.PropertyName); modelState.ClearValidationState(property.BinderModelName ?? property.PropertyName);
} }
} }

View File

@ -129,9 +129,9 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
if (string.IsNullOrEmpty(modelKey)) if (string.IsNullOrEmpty(modelKey))
{ {
var modelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(typeof(TModel)); var modelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(typeof(TModel));
for (var i = 0; i < modelMetadata.Properties.Count; i++)
foreach (var property in modelMetadata.Properties)
{ {
var property = modelMetadata.Properties[i];
var childKey = property.BinderModelName ?? property.PropertyName; var childKey = property.BinderModelName ?? property.PropertyName;
var entries = modelState.FindKeysWithPrefix(childKey).ToArray(); var entries = modelState.FindKeysWithPrefix(childKey).ToArray();
foreach (var entry in entries) foreach (var entry in entries)

View File

@ -4,7 +4,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Linq;
using Microsoft.AspNetCore.Mvc.ModelBinding; using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.Extensions.Internal; using Microsoft.Extensions.Internal;
@ -20,7 +19,7 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures
private object _model; private object _model;
private Func<object, object> _modelAccessor; private Func<object, object> _modelAccessor;
private List<ModelExplorer> _properties; private ModelExplorer[] _properties;
/// <summary> /// <summary>
/// Creates a new <see cref="ModelExplorer"/>. /// Creates a new <see cref="ModelExplorer"/>.
@ -198,24 +197,38 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures
/// Includes a <see cref="ModelExplorer"/> for each property of the <see cref="ModelMetadata"/> /// Includes a <see cref="ModelExplorer"/> for each property of the <see cref="ModelMetadata"/>
/// for <see cref="ModelType"/>. /// for <see cref="ModelType"/>.
/// </remarks> /// </remarks>
public IEnumerable<ModelExplorer> Properties public IEnumerable<ModelExplorer> Properties => PropertiesInternal;
private ModelExplorer[] PropertiesInternal
{ {
get get
{ {
if (_properties == null) if (_properties == null)
{ {
_properties = new List<ModelExplorer>();
var metadata = GetMetadataForRuntimeType(); var metadata = GetMetadataForRuntimeType();
var properties = metadata.Properties;
var propertyHelpers = PropertyHelper.GetProperties(ModelType);
var properties = Enumerable.Join( _properties = new ModelExplorer[properties.Count];
metadata.Properties, for (var i = 0; i < properties.Count; i++)
PropertyHelper.GetProperties(ModelType), {
m => m.PropertyName, var propertyMetadata = properties[i];
ph => ph.Property.Name, PropertyHelper propertyHelper = null;
(m, ph) => CreateExplorerForProperty(m, ph)); for (var j = 0; j < propertyHelpers.Length; j++)
{
if (string.Equals(
propertyMetadata.PropertyName,
propertyHelpers[j].Property.Name,
StringComparison.Ordinal))
{
propertyHelper = propertyHelpers[j];
break;
}
}
_properties.AddRange(properties); Debug.Assert(propertyHelper != null);
_properties[i] = CreateExplorerForProperty(propertyMetadata, propertyHelper);
}
} }
return _properties; return _properties;
@ -252,10 +265,16 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures
throw new ArgumentNullException(nameof(name)); throw new ArgumentNullException(nameof(name));
} }
return Properties.FirstOrDefault(p => string.Equals( for (var i = 0; i < PropertiesInternal.Length; i++)
p.Metadata.PropertyName, {
name, var property = PropertiesInternal[i];
StringComparison.Ordinal)); if (string.Equals(name, property.Metadata.PropertyName, StringComparison.Ordinal))
{
return property;
}
}
return null;
} }
/// <summary> /// <summary>

View File

@ -2,9 +2,8 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System; using System;
using System.Linq;
using System.Globalization; using System.Globalization;
using Microsoft.AspNetCore.Mvc.ModelBinding; using System.Linq;
namespace Microsoft.AspNetCore.Mvc.ViewFeatures namespace Microsoft.AspNetCore.Mvc.ViewFeatures
{ {