Reducing allocations in value providers

- Don't go async in formdata providers unless we need to
- Remove unnecessary defensive copy in CompositeValueProvider
This commit is contained in:
Ryan Nowak 2015-10-05 21:28:01 -07:00
parent 082f175b48
commit 0dadf56fc8
5 changed files with 45 additions and 24 deletions

View File

@ -0,0 +1,18 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Threading.Tasks;
namespace Microsoft.AspNet.Mvc.Internal
{
public static class TaskCache<T>
{
private static readonly Task<T> _defaultCompletedTask = Task.FromResult(default(T));
/// <summary>
/// Gets a completed <see cref="Task"/> with the value of <c>default(T)</c>.
/// </summary>
public static Task<T> DefaultCompletedTask => _defaultCompletedTask;
}
}

View File

@ -30,8 +30,8 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
/// </summary> /// </summary>
/// <param name="valueProviders">The sequence of <see cref="IValueProvider"/> to add to this instance of /// <param name="valueProviders">The sequence of <see cref="IValueProvider"/> to add to this instance of
/// <see cref="CompositeValueProvider"/>.</param> /// <see cref="CompositeValueProvider"/>.</param>
public CompositeValueProvider(IEnumerable<IValueProvider> valueProviders) protected CompositeValueProvider(IList<IValueProvider> valueProviders)
: base(valueProviders.ToList()) : base(valueProviders)
{ {
} }

View File

@ -5,12 +5,13 @@ using System;
using System.Globalization; using System.Globalization;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNet.Http; using Microsoft.AspNet.Http;
using Microsoft.AspNet.Mvc.Internal;
namespace Microsoft.AspNet.Mvc.ModelBinding namespace Microsoft.AspNet.Mvc.ModelBinding
{ {
public class FormValueProviderFactory : IValueProviderFactory public class FormValueProviderFactory : IValueProviderFactory
{ {
public async Task<IValueProvider> GetValueProviderAsync(ValueProviderFactoryContext context) public Task<IValueProvider> GetValueProviderAsync(ValueProviderFactoryContext context)
{ {
if (context == null) if (context == null)
{ {
@ -18,23 +19,20 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
} }
var request = context.HttpContext.Request; var request = context.HttpContext.Request;
if (request.HasFormContentType) if (request.HasFormContentType)
{ {
var culture = GetCultureInfo(request); return CreateValueProviderAsync(request);
return new ReadableStringCollectionValueProvider(
BindingSource.Form,
await request.ReadFormAsync(),
culture);
} }
return null; return TaskCache<IValueProvider>.DefaultCompletedTask;
} }
private static CultureInfo GetCultureInfo(HttpRequest request) private static async Task<IValueProvider> CreateValueProviderAsync(HttpRequest request)
{ {
return CultureInfo.CurrentCulture; return new ReadableStringCollectionValueProvider(
BindingSource.Form,
await request.ReadFormAsync(),
CultureInfo.CurrentCulture);
} }
} }
} }

View File

@ -8,30 +8,35 @@ using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNet.Http; using Microsoft.AspNet.Http;
using Microsoft.AspNet.Mvc.Core; using Microsoft.AspNet.Mvc.Core;
using Microsoft.AspNet.Mvc.Internal;
using Microsoft.Extensions.Primitives; using Microsoft.Extensions.Primitives;
namespace Microsoft.AspNet.Mvc.ModelBinding namespace Microsoft.AspNet.Mvc.ModelBinding
{ {
public class JQueryFormValueProviderFactory : IValueProviderFactory public class JQueryFormValueProviderFactory : IValueProviderFactory
{ {
public async Task<IValueProvider> GetValueProviderAsync(ValueProviderFactoryContext context) public Task<IValueProvider> GetValueProviderAsync(ValueProviderFactoryContext context)
{ {
if (context == null) if (context == null)
{ {
throw new ArgumentNullException(nameof(context)); throw new ArgumentNullException(nameof(context));
} }
var request = context.HttpContext.Request; var request = context.HttpContext.Request;
if (request.HasFormContentType) if (request.HasFormContentType)
{ {
return new JQueryFormValueProvider( return CreateValueProviderAsync(request);
}
return TaskCache<IValueProvider>.DefaultCompletedTask;
}
private static async Task<IValueProvider> CreateValueProviderAsync(HttpRequest request)
{
return new JQueryFormValueProvider(
BindingSource.Form, BindingSource.Form,
await GetValueCollectionAsync(request), await GetValueCollectionAsync(request),
CultureInfo.CurrentCulture); CultureInfo.CurrentCulture);
}
return null;
} }
private static async Task<IDictionary<string, StringValues>> GetValueCollectionAsync(HttpRequest request) private static async Task<IDictionary<string, StringValues>> GetValueCollectionAsync(HttpRequest request)

View File

@ -26,7 +26,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
new JQueryFormValueProvider(bindingSource, new Dictionary<string, StringValues>(), culture); new JQueryFormValueProvider(bindingSource, new Dictionary<string, StringValues>(), culture);
var valueProvider = new JQueryFormValueProvider(bindingSource, values, culture); var valueProvider = new JQueryFormValueProvider(bindingSource, values, culture);
return new CompositeValueProvider(new[] { emptyValueProvider, valueProvider }); return new CompositeValueProvider() { emptyValueProvider, valueProvider };
} }
#if DNX451 #if DNX451
@ -43,7 +43,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
provider2.Setup(p => p.GetKeysFromPrefix("prefix")) provider2.Setup(p => p.GetKeysFromPrefix("prefix"))
.Returns(dictionary) .Returns(dictionary)
.Verifiable(); .Verifiable();
var provider = new CompositeValueProvider(new[] { provider1, provider2.Object }); var provider = new CompositeValueProvider() { provider1, provider2.Object };
// Act // Act
var values = provider.GetKeysFromPrefix("prefix"); var values = provider.GetKeysFromPrefix("prefix");
@ -61,7 +61,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
// Arrange // Arrange
var provider1 = Mock.Of<IValueProvider>(); var provider1 = Mock.Of<IValueProvider>();
var provider2 = Mock.Of<IValueProvider>(); var provider2 = Mock.Of<IValueProvider>();
var provider = new CompositeValueProvider(new[] { provider1, provider2 }); var provider = new CompositeValueProvider() { provider1, provider2 };
// Act // Act
var values = provider.GetKeysFromPrefix("prefix"); var values = provider.GetKeysFromPrefix("prefix");
@ -89,7 +89,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
var valueProvider1 = GetMockValueProvider("Test"); var valueProvider1 = GetMockValueProvider("Test");
var valueProvider2 = GetMockValueProvider("Unrelated"); var valueProvider2 = GetMockValueProvider("Unrelated");
var provider = new CompositeValueProvider(new List<IValueProvider>() { valueProvider1.Object, valueProvider2.Object }); var provider = new CompositeValueProvider() { valueProvider1.Object, valueProvider2.Object };
// Act // Act
var result = provider.Filter(metadata.BindingSource); var result = provider.Filter(metadata.BindingSource);