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 string TempFileName { get { throw null; } }
protected override void Dispose(bool disposing) { }
[System.Diagnostics.DebuggerStepThroughAttribute]
public override System.Threading.Tasks.ValueTask DisposeAsync() { throw null; }
public override void Flush() { }
public override int Read(byte[] buffer, int offset, int count) { throw null; }
[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()
{
if (_disposed)

View File

@ -569,9 +569,11 @@ namespace Microsoft.AspNetCore.Mvc.ApiExplorer
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 bindingInfo = BindingInfo.GetBindingInfo(Enumerable.Empty<object>(), propertyMetadata);

View File

@ -71,7 +71,7 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
{
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)
{
transcoding.Dispose();
await transcoding.DisposeAsync();
}
}
}

View File

@ -97,15 +97,22 @@ namespace Microsoft.AspNetCore.Mvc.Cors
private static void ConfigureCorsActionConstraint(ActionModel actionModel)
{
for (var i = 0; i < actionModel.Selectors.Count; i++)
{
var selectorModel = actionModel.Selectors[i];
var selectors = actionModel.Selectors;
// Read interface .Count once rather than per iteration
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)
{
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) &&
!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];
if (string.Equals(supportedMethod, accessControlRequestMethod, StringComparison.OrdinalIgnoreCase))

View File

@ -67,9 +67,12 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations
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)
{
// 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)
{
displayMetadata.DataTypeName = DataType.Html.ToString();
displayMetadata.DataTypeName = nameof(DataType.Html);
}
var containerType = context.Key.ContainerType ?? context.Key.ModelType;
@ -322,11 +322,14 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations
throw new ArgumentNullException(nameof(context));
}
var attributes = new List<object>(context.Attributes.Count);
for (var i = 0; i < context.Attributes.Count; i++)
// Read interface .Count once rather than per iteration
var contextAttributes = context.Attributes;
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)
{
attributes.AddRange(validationProviderAttribute.GetValidationAttributes());

View File

@ -59,9 +59,12 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations
_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)
{
continue;
@ -106,7 +109,9 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations
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)
{

View File

@ -24,9 +24,11 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations
}
// 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.
if (validatorItem.Validator != null)
{

View File

@ -27,9 +27,12 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations
typeToValidate == typeof(double) ||
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)
{
// 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(),
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
// read everything into a buffer, and then seek back to the beginning.
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.
memoryThreshold = (int)request.ContentLength.Value;
memoryThreshold = (int)contentLength;
}
readStream = new FileBufferingReadStream(request.Body, memoryThreshold);
@ -163,7 +164,7 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
{
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
// read everything into a buffer, and then seek back to the beginning.
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.
memoryThreshold = (int)request.ContentLength.Value;
memoryThreshold = (int)contentLength;
}
readStream = new FileBufferingReadStream(request.Body, memoryThreshold);
@ -156,7 +157,7 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
{
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 =
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.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
// read everything into a buffer, and then seek back to the beginning.
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.
memoryThreshold = (int)request.ContentLength.Value;
memoryThreshold = (int)contentLength;
}
readStream = new FileBufferingReadStream(request.Body, memoryThreshold);
@ -171,7 +172,7 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
if (readStream is FileBufferingReadStream fileBufferingReadStream)
{
fileBufferingReadStream.Dispose();
await fileBufferingReadStream.DisposeAsync();
}
}
}