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:
parent
4f67ff9216
commit
cc96e988f4
|
|
@ -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]
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -71,7 +71,7 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
||||||
{
|
{
|
||||||
if (inputStream is TranscodingReadStream transcoding)
|
if (inputStream is TranscodingReadStream transcoding)
|
||||||
{
|
{
|
||||||
transcoding.Dispose();
|
await transcoding.DisposeAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -66,7 +66,7 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
||||||
{
|
{
|
||||||
if (writeStream is TranscodingWriteStream transcoding)
|
if (writeStream is TranscodingWriteStream transcoding)
|
||||||
{
|
{
|
||||||
transcoding.Dispose();
|
await transcoding.DisposeAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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))
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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());
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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) },
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue