More read interface IList.Count once rather than per iteration + miscellany (#11253)

* Read interface IList.Count once rather than per iteration

Inspired by #9962, read .Count once rather than once per loop iteration.

* Use nameof() instead of ToString()

Use constant nameof() on enum value, rather than runtime ToString().

* Right-size dictionary

Initialize dictionary with a fixed number of elements to the number of elements it contains.

* Use DisposeAsync()

Use DisposeAsync() on FileBufferingReadStream in input formatters.

* Override DisposeAsync()

Override DisposeAsync() to call DisposeAsync() on the inner stream.

* Use GetValueOrDefault() for content-length

Use GetValueOrDefault() to read the content length once instead of checking HasValue once and Value up to three times.

* Update Microsoft.AspNetCore.WebUtilities.netcoreapp3.0.cs

Add DisposeAsync().

* Use DisposeAsync()

Use DisposeAsync() on transcoding streams as other formatters do.
This commit is contained in:
Martin Costello 2019-06-17 19:22:45 +01:00 committed by Pranav K
parent 4f67ff9216
commit cc96e988f4
16 changed files with 88 additions and 39 deletions

View File

@ -54,6 +54,8 @@ namespace Microsoft.AspNetCore.WebUtilities
public override long Position { get { throw null; } set { } } public override long Position { get { throw null; } set { } }
public string TempFileName { get { throw null; } } public string TempFileName { get { throw null; } }
protected override void Dispose(bool disposing) { } protected override void Dispose(bool disposing) { }
[System.Diagnostics.DebuggerStepThroughAttribute]
public override System.Threading.Tasks.ValueTask DisposeAsync() { throw null; }
public override void Flush() { } public override void Flush() { }
public override int Read(byte[] buffer, int offset, int count) { throw null; } public override int Read(byte[] buffer, int offset, int count) { throw null; }
[System.Diagnostics.DebuggerStepThroughAttribute] [System.Diagnostics.DebuggerStepThroughAttribute]

View File

@ -366,6 +366,20 @@ namespace Microsoft.AspNetCore.WebUtilities
} }
} }
public async override ValueTask DisposeAsync()
{
if (!_disposed)
{
_disposed = true;
if (_rentedBuffer != null)
{
_bytePool.Return(_rentedBuffer);
}
await _buffer.DisposeAsync();
}
}
private void ThrowIfDisposed() private void ThrowIfDisposed()
{ {
if (_disposed) if (_disposed)

View File

@ -569,9 +569,11 @@ namespace Microsoft.AspNetCore.Mvc.ApiExplorer
newContainerName = GetName(containerName, bindingContext); newContainerName = GetName(containerName, bindingContext);
} }
for (var i = 0; i < modelMetadata.Properties.Count; i++) var metadataProperties = modelMetadata.Properties;
var metadataPropertiesCount = metadataProperties.Count;
for (var i = 0; i < metadataPropertiesCount; i++)
{ {
var propertyMetadata = modelMetadata.Properties[i]; var propertyMetadata = metadataProperties[i];
var key = new PropertyKey(propertyMetadata, source); var key = new PropertyKey(propertyMetadata, source);
var bindingInfo = BindingInfo.GetBindingInfo(Enumerable.Empty<object>(), propertyMetadata); var bindingInfo = BindingInfo.GetBindingInfo(Enumerable.Empty<object>(), propertyMetadata);

View File

@ -71,7 +71,7 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
{ {
if (inputStream is TranscodingReadStream transcoding) if (inputStream is TranscodingReadStream transcoding)
{ {
transcoding.Dispose(); await transcoding.DisposeAsync();
} }
} }

View File

@ -66,7 +66,7 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
{ {
if (writeStream is TranscodingWriteStream transcoding) if (writeStream is TranscodingWriteStream transcoding)
{ {
transcoding.Dispose(); await transcoding.DisposeAsync();
} }
} }
} }

View File

@ -97,15 +97,22 @@ namespace Microsoft.AspNetCore.Mvc.Cors
private static void ConfigureCorsActionConstraint(ActionModel actionModel) private static void ConfigureCorsActionConstraint(ActionModel actionModel)
{ {
for (var i = 0; i < actionModel.Selectors.Count; i++) var selectors = actionModel.Selectors;
{ // Read interface .Count once rather than per iteration
var selectorModel = actionModel.Selectors[i]; var selectorsCount = selectors.Count;
for (var j = 0; j < selectorModel.ActionConstraints.Count; j++) for (var i = 0; i < selectorsCount; i++)
{
var selectorModel = selectors[i];
var actionConstraints = selectorModel.ActionConstraints;
// Read interface .Count once rather than per iteration
var actionConstraintsCount = actionConstraints.Count;
for (var j = 0; j < actionConstraintsCount; j++)
{ {
if (selectorModel.ActionConstraints[j] is HttpMethodActionConstraint httpConstraint) if (actionConstraints[j] is HttpMethodActionConstraint httpConstraint)
{ {
selectorModel.ActionConstraints[j] = new CorsHttpMethodActionConstraint(httpConstraint); actionConstraints[j] = new CorsHttpMethodActionConstraint(httpConstraint);
} }
} }
} }
@ -131,11 +138,14 @@ namespace Microsoft.AspNetCore.Mvc.Cors
foreach (var selector in action.Selectors) foreach (var selector in action.Selectors)
{ {
for (var i = 0; i < selector.EndpointMetadata.Count; i++) var metadata = selector.EndpointMetadata;
// Read interface .Count once rather than per iteration
var metadataCount = metadata.Count;
for (var i = 0; i < metadataCount; i++)
{ {
if (selector.EndpointMetadata[i] is HttpMethodMetadata httpMethodMetadata) if (metadata[i] is HttpMethodMetadata httpMethodMetadata)
{ {
selector.EndpointMetadata[i] = new HttpMethodMetadata(httpMethodMetadata.HttpMethods, acceptCorsPreflight: true); metadata[i] = new HttpMethodMetadata(httpMethodMetadata.HttpMethods, acceptCorsPreflight: true);
} }
} }
} }

View File

@ -39,7 +39,9 @@ namespace Microsoft.AspNetCore.Mvc.Cors
request.Headers.TryGetValue(AccessControlRequestMethod, out var accessControlRequestMethod) && request.Headers.TryGetValue(AccessControlRequestMethod, out var accessControlRequestMethod) &&
!StringValues.IsNullOrEmpty(accessControlRequestMethod)) !StringValues.IsNullOrEmpty(accessControlRequestMethod))
{ {
for (var i = 0; i < methods.Count; i++) // Read interface .Count once rather than per iteration
var methodsCount = methods.Count;
for (var i = 0; i < methodsCount; i++)
{ {
var supportedMethod = methods[i]; var supportedMethod = methods[i];
if (string.Equals(supportedMethod, accessControlRequestMethod, StringComparison.OrdinalIgnoreCase)) if (string.Equals(supportedMethod, accessControlRequestMethod, StringComparison.OrdinalIgnoreCase))

View File

@ -67,9 +67,12 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations
var hasRequiredAttribute = false; var hasRequiredAttribute = false;
for (var i = 0; i < context.Results.Count; i++) var results = context.Results;
// Read interface .Count once rather than per iteration
var resultsCount = results.Count;
for (var i = 0; i < resultsCount; i++)
{ {
var validatorItem = context.Results[i]; var validatorItem = results[i];
if (validatorItem.Validator != null) if (validatorItem.Validator != null)
{ {
// Check if a required attribute is already cached. // Check if a required attribute is already cached.

View File

@ -109,7 +109,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations
} }
else if (displayFormatAttribute != null && !displayFormatAttribute.HtmlEncode) else if (displayFormatAttribute != null && !displayFormatAttribute.HtmlEncode)
{ {
displayMetadata.DataTypeName = DataType.Html.ToString(); displayMetadata.DataTypeName = nameof(DataType.Html);
} }
var containerType = context.Key.ContainerType ?? context.Key.ModelType; var containerType = context.Key.ContainerType ?? context.Key.ModelType;
@ -322,11 +322,14 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations
throw new ArgumentNullException(nameof(context)); throw new ArgumentNullException(nameof(context));
} }
var attributes = new List<object>(context.Attributes.Count); // Read interface .Count once rather than per iteration
var contextAttributes = context.Attributes;
for (var i = 0; i < context.Attributes.Count; i++) var contextAttributesCount = contextAttributes.Count;
var attributes = new List<object>(contextAttributesCount);
for (var i = 0; i < contextAttributesCount; i++)
{ {
var attribute = context.Attributes[i]; var attribute = contextAttributes[i];
if (attribute is ValidationProviderAttribute validationProviderAttribute) if (attribute is ValidationProviderAttribute validationProviderAttribute)
{ {
attributes.AddRange(validationProviderAttribute.GetValidationAttributes()); attributes.AddRange(validationProviderAttribute.GetValidationAttributes());

View File

@ -59,9 +59,12 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations
_stringLocalizerFactory); _stringLocalizerFactory);
} }
for (var i = 0; i < context.Results.Count; i++) var results = context.Results;
// Read interface .Count once rather than per iteration
var resultsCount = results.Count;
for (var i = 0; i < resultsCount; i++)
{ {
var validatorItem = context.Results[i]; var validatorItem = results[i];
if (validatorItem.Validator != null) if (validatorItem.Validator != null)
{ {
continue; continue;
@ -106,7 +109,9 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations
return true; return true;
} }
for (var i = 0; i < validatorMetadata.Count; i++) // Read interface .Count once rather than per iteration
var validatorMetadataCount = validatorMetadata.Count;
for (var i = 0; i < validatorMetadataCount; i++)
{ {
if (validatorMetadata[i] is ValidationAttribute) if (validatorMetadata[i] is ValidationAttribute)
{ {

View File

@ -24,9 +24,11 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations
} }
// Perf: Avoid allocations // Perf: Avoid allocations
for (var i = 0; i < context.Results.Count; i++) var results = context.Results;
var resultsCount = results.Count;
for (var i = 0; i < resultsCount; i++)
{ {
var validatorItem = context.Results[i]; var validatorItem = results[i];
// Don't overwrite anything that was done by a previous provider. // Don't overwrite anything that was done by a previous provider.
if (validatorItem.Validator != null) if (validatorItem.Validator != null)
{ {

View File

@ -27,9 +27,12 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations
typeToValidate == typeof(double) || typeToValidate == typeof(double) ||
typeToValidate == typeof(decimal)) typeToValidate == typeof(decimal))
{ {
for (var i = 0; i < context.Results.Count; i++) var results = context.Results;
// Read interface .Count once rather than per iteration
var resultsCount = results.Count;
for (var i = 0; i < resultsCount; i++)
{ {
var validator = context.Results[i].Validator; var validator = results[i].Validator;
if (validator != null && validator is NumericClientModelValidator) if (validator != null && validator is NumericClientModelValidator)
{ {
// A validator is already present. No need to add one. // A validator is already present. No need to add one.
@ -37,7 +40,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations
} }
} }
context.Results.Add(new ClientValidatorItem results.Add(new ClientValidatorItem
{ {
Validator = new NumericClientModelValidator(), Validator = new NumericClientModelValidator(),
IsReusable = true IsReusable = true

View File

@ -124,10 +124,11 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
// XmlDataContractSerializer does synchronous reads. In order to avoid blocking on the stream, we asynchronously // XmlDataContractSerializer does synchronous reads. In order to avoid blocking on the stream, we asynchronously
// read everything into a buffer, and then seek back to the beginning. // read everything into a buffer, and then seek back to the beginning.
var memoryThreshold = DefaultMemoryThreshold; var memoryThreshold = DefaultMemoryThreshold;
if (request.ContentLength.HasValue && request.ContentLength.Value > 0 && request.ContentLength.Value < memoryThreshold) var contentLength = request.ContentLength.GetValueOrDefault();
if (contentLength > 0 && contentLength < memoryThreshold)
{ {
// If the Content-Length is known and is smaller than the default buffer size, use it. // If the Content-Length is known and is smaller than the default buffer size, use it.
memoryThreshold = (int)request.ContentLength.Value; memoryThreshold = (int)contentLength;
} }
readStream = new FileBufferingReadStream(request.Body, memoryThreshold); readStream = new FileBufferingReadStream(request.Body, memoryThreshold);
@ -163,7 +164,7 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
{ {
if (readStream is FileBufferingReadStream fileBufferingReadStream) if (readStream is FileBufferingReadStream fileBufferingReadStream)
{ {
fileBufferingReadStream.Dispose(); await fileBufferingReadStream.DisposeAsync();
} }
} }
} }

View File

@ -104,10 +104,11 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
// XmlSerializer does synchronous reads. In order to avoid blocking on the stream, we asynchronously // XmlSerializer does synchronous reads. In order to avoid blocking on the stream, we asynchronously
// read everything into a buffer, and then seek back to the beginning. // read everything into a buffer, and then seek back to the beginning.
var memoryThreshold = DefaultMemoryThreshold; var memoryThreshold = DefaultMemoryThreshold;
if (request.ContentLength.HasValue && request.ContentLength.Value > 0 && request.ContentLength.Value < memoryThreshold) var contentLength = request.ContentLength.GetValueOrDefault();
if (contentLength > 0 && contentLength < memoryThreshold)
{ {
// If the Content-Length is known and is smaller than the default buffer size, use it. // If the Content-Length is known and is smaller than the default buffer size, use it.
memoryThreshold = (int)request.ContentLength.Value; memoryThreshold = (int)contentLength;
} }
readStream = new FileBufferingReadStream(request.Body, memoryThreshold); readStream = new FileBufferingReadStream(request.Body, memoryThreshold);
@ -156,7 +157,7 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
{ {
if (readStream is FileBufferingReadStream fileBufferingReadStream) if (readStream is FileBufferingReadStream fileBufferingReadStream)
{ {
fileBufferingReadStream.Dispose(); await fileBufferingReadStream.DisposeAsync();
} }
} }
} }

View File

@ -31,7 +31,7 @@ namespace Microsoft.AspNetCore.Mvc.NewtonsoftJson
private static readonly ConcurrentDictionary<Type, Func<JObject, object>> _dictionaryConverters = private static readonly ConcurrentDictionary<Type, Func<JObject, object>> _dictionaryConverters =
new ConcurrentDictionary<Type, Func<JObject, object>>(); new ConcurrentDictionary<Type, Func<JObject, object>>();
private static readonly Dictionary<JTokenType, Type> _tokenTypeLookup = new Dictionary<JTokenType, Type> private static readonly Dictionary<JTokenType, Type> _tokenTypeLookup = new Dictionary<JTokenType, Type>(8)
{ {
{ JTokenType.String, typeof(string) }, { JTokenType.String, typeof(string) },
{ JTokenType.Integer, typeof(int) }, { JTokenType.Integer, typeof(int) },

View File

@ -134,10 +134,11 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
// JSON.Net does synchronous reads. In order to avoid blocking on the stream, we asynchronously // JSON.Net does synchronous reads. In order to avoid blocking on the stream, we asynchronously
// read everything into a buffer, and then seek back to the beginning. // read everything into a buffer, and then seek back to the beginning.
var memoryThreshold = DefaultMemoryThreshold; var memoryThreshold = DefaultMemoryThreshold;
if (request.ContentLength.HasValue && request.ContentLength.Value > 0 && request.ContentLength.Value < memoryThreshold) var contentLength = request.ContentLength.GetValueOrDefault();
if (contentLength > 0 && contentLength < memoryThreshold)
{ {
// If the Content-Length is known and is smaller than the default buffer size, use it. // If the Content-Length is known and is smaller than the default buffer size, use it.
memoryThreshold = (int)request.ContentLength.Value; memoryThreshold = (int)contentLength;
} }
readStream = new FileBufferingReadStream(request.Body, memoryThreshold); readStream = new FileBufferingReadStream(request.Body, memoryThreshold);
@ -171,7 +172,7 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
if (readStream is FileBufferingReadStream fileBufferingReadStream) if (readStream is FileBufferingReadStream fileBufferingReadStream)
{ {
fileBufferingReadStream.Dispose(); await fileBufferingReadStream.DisposeAsync();
} }
} }
} }