Change IValueProviderFactory.GetValueProvider to be synchronous.

This commit is contained in:
Pranav K 2014-05-12 15:08:33 -07:00
parent ec8d09dd65
commit d8ba998dec
11 changed files with 93 additions and 75 deletions

View File

@ -29,14 +29,12 @@ namespace Microsoft.AspNet.Mvc
_validatorProviders = validatorProviders;
}
public async Task<ActionBindingContext> GetActionBindingContextAsync(ActionContext actionContext)
public Task<ActionBindingContext> GetActionBindingContextAsync(ActionContext actionContext)
{
var requestContext = new RequestContext(actionContext.HttpContext, actionContext.RouteValues);
var valueProviders = await Task.WhenAll(_valueProviderFactories.Select(factory => factory.GetValueProviderAsync(requestContext)));
valueProviders = valueProviders.Where(vp => vp != null)
.ToArray();
return new ActionBindingContext(
var valueProviders = _valueProviderFactories.Select(factory => factory.GetValueProvider(requestContext))
.Where(vp => vp != null);
var context = new ActionBindingContext(
actionContext,
_modelMetadataProvider,
new CompositeModelBinder(_modelBinders),
@ -44,6 +42,8 @@ namespace Microsoft.AspNet.Mvc
_inputFormatterProvider,
_validatorProviders
);
return Task.FromResult(context);
}
}
}

View File

@ -25,7 +25,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
public virtual async Task<bool> ContainsPrefixAsync(string prefix)
{
for (int i = 0; i < Count; i++)
for (var i = 0; i < Count; i++)
{
if (await this[i].ContainsPrefixAsync(prefix))
{
@ -39,11 +39,11 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
{
// Performance-sensitive
// Caching the count is faster for IList<T>
int itemCount = Items.Count;
for (int i = 0; i < itemCount; i++)
var itemCount = Items.Count;
for (var i = 0; i < itemCount; i++)
{
IValueProvider vp = Items[i];
ValueProviderResult result = await vp.GetValueAsync(key);
var valueProvider = Items[i];
var result = await valueProvider.GetValueAsync(key);
if (result != null)
{
return result;
@ -52,11 +52,11 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
return null;
}
public virtual IDictionary<string, string> GetKeysFromPrefix(string prefix)
public virtual async Task<IDictionary<string, string>> GetKeysFromPrefixAsync(string prefix)
{
foreach (IValueProvider vp in this)
foreach (var valueProvider in this)
{
IDictionary<string, string> result = GetKeysFromPrefixFromProvider(vp, prefix);
var result = await GetKeysFromPrefixFromProvider(valueProvider, prefix);
if (result != null && result.Count > 0)
{
return result;
@ -65,10 +65,11 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
return new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
}
internal static IDictionary<string, string> GetKeysFromPrefixFromProvider(IValueProvider provider, string prefix)
private static Task<IDictionary<string, string>> GetKeysFromPrefixFromProvider(IValueProvider provider,
string prefix)
{
IEnumerableValueProvider enumeratedProvider = provider as IEnumerableValueProvider;
return (enumeratedProvider != null) ? enumeratedProvider.GetKeysFromPrefix(prefix) : null;
var enumeratedProvider = provider as IEnumerableValueProvider;
return (enumeratedProvider != null) ? enumeratedProvider.GetKeysFromPrefixAsync(prefix) : null;
}
protected override void InsertItem(int index, [NotNull] IValueProvider item)

View File

@ -12,15 +12,14 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
{
private const string FormEncodedContentType = "application/x-www-form-urlencoded";
public async Task<IValueProvider> GetValueProviderAsync(RequestContext requestContext)
public IValueProvider GetValueProvider(RequestContext requestContext)
{
var request = requestContext.HttpContext.Request;
if (IsSupportedContentType(request))
{
var queryCollection = await request.GetFormAsync();
var culture = GetCultureInfo(request);
return new ReadableStringCollectionValueProvider(queryCollection, culture);
return new ReadableStringCollectionValueProvider(request.GetFormAsync, culture);
}
return null;

View File

@ -2,11 +2,12 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Microsoft.AspNet.Mvc.ModelBinding
{
public interface IEnumerableValueProvider : IValueProvider
{
IDictionary<string, string> GetKeysFromPrefix(string prefix);
Task<IDictionary<string, string>> GetKeysFromPrefixAsync(string prefix);
}
}

View File

@ -12,6 +12,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
/// </summary>
/// <param name="requestContext">RequestContext that value provider will populate from</param>
/// <returns>a value provider instance or null</returns>
Task<IValueProvider> GetValueProviderAsync(RequestContext requestContext);
IValueProvider GetValueProvider(RequestContext requestContext);
}
}

View File

@ -2,8 +2,6 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Globalization;
using System.Threading.Tasks;
using Microsoft.AspNet.Mvc.ModelBinding.Internal;
namespace Microsoft.AspNet.Mvc.ModelBinding
{
@ -11,7 +9,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
{
private static readonly object _cacheKey = new object();
public Task<IValueProvider> GetValueProviderAsync([NotNull] RequestContext requestContext)
public IValueProvider GetValueProvider([NotNull] RequestContext requestContext)
{
// Process the query collection once-per request.
var storage = requestContext.HttpContext.Items;
@ -27,7 +25,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
{
provider = (ReadableStringCollectionValueProvider)value;
}
return Task.FromResult(provider);
return provider;
}
}
}

View File

@ -1,7 +1,9 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Globalization;
using System.Linq;
using System.Threading.Tasks;
@ -14,7 +16,8 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
{
private readonly CultureInfo _culture;
private PrefixContainer _prefixContainer;
private readonly IReadableStringCollection _values;
private readonly Func<Task<IReadableStringCollection>> _valuesFactory;
private IReadableStringCollection _values;
/// <summary>
/// Creates a NameValuePairsProvider wrapping an existing set of key value pairs.
@ -27,6 +30,13 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
_culture = culture;
}
public ReadableStringCollectionValueProvider([NotNull] Func<Task<IReadableStringCollection>> valuesFactory,
CultureInfo culture)
{
_valuesFactory = valuesFactory;
_culture = culture;
}
public CultureInfo Culture
{
get
@ -35,35 +45,24 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
}
}
private PrefixContainer PrefixContainer
public virtual async Task<bool> ContainsPrefixAsync(string prefix)
{
get
{
if (_prefixContainer == null)
{
// Initialization race is OK providing data remains read-only and object identity is not significant
// TODO: Figure out if we can have IReadableStringCollection expose Keys, Count etc
_prefixContainer = new PrefixContainer(_values.Select(v => v.Key).ToArray());
}
return _prefixContainer;
}
var prefixContainer = await GetPrefixContainerAsync();
return prefixContainer.ContainsPrefix(prefix);
}
public virtual Task<bool> ContainsPrefixAsync(string prefix)
public virtual async Task<IDictionary<string, string>> GetKeysFromPrefixAsync([NotNull] string prefix)
{
return Task.FromResult(PrefixContainer.ContainsPrefix(prefix));
var prefixContainer = await GetPrefixContainerAsync();
return prefixContainer.GetKeysFromPrefix(prefix);
}
public virtual IDictionary<string, string> GetKeysFromPrefix([NotNull] string prefix)
public virtual async Task<ValueProviderResult> GetValueAsync([NotNull] string key)
{
return PrefixContainer.GetKeysFromPrefix(prefix);
}
var collection = await GetValueCollectionAsync();
var values = collection.GetValues(key);
public virtual Task<ValueProviderResult> GetValueAsync([NotNull] string key)
{
ValueProviderResult result;
var values = _values.GetValues(key);
if (values == null)
{
result = null;
@ -78,7 +77,31 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
result = new ValueProviderResult(values, _values.Get(key), _culture);
}
return Task.FromResult(result);
return result;
}
private async Task<IReadableStringCollection> GetValueCollectionAsync()
{
if (_values == null)
{
Contract.Assert(_valuesFactory != null);
_values = await _valuesFactory();
}
return _values;
}
private async Task<PrefixContainer> GetPrefixContainerAsync()
{
if (_prefixContainer == null)
{
// Initialization race is OK providing data remains read-only and object identity is not significant
// TODO: Fix this once https://github.com/aspnet/HttpAbstractions/issues/3 is sorted out.
var collection = await GetValueCollectionAsync();
_prefixContainer = new PrefixContainer(collection.Select(v => v.Key).ToArray());
}
return _prefixContainer;
}
}
}

View File

@ -1,16 +1,13 @@
// Copyright (c) Microsoft Open Technologies, Inc. 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.ModelBinding
{
public class RouteValueValueProviderFactory : IValueProviderFactory
{
public Task<IValueProvider> GetValueProviderAsync(RequestContext requestContext)
public IValueProvider GetValueProvider(RequestContext requestContext)
{
var valueProvider = new DictionaryBasedValueProvider(requestContext.RouteValues);
return Task.FromResult<IValueProvider>(valueProvider);
return new DictionaryBasedValueProvider(requestContext.RouteValues);
}
}
}

View File

@ -14,14 +14,14 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
public class FormValueProviderFactoryTests
{
[Fact]
public async Task GetValueProvider_ReturnsNull_WhenContentTypeIsNotFormUrlEncoded()
public void GetValueProvider_ReturnsNull_WhenContentTypeIsNotFormUrlEncoded()
{
// Arrange
var requestContext = CreateRequestContext("some-content-type");
var factory = new FormValueProviderFactory();
// Act
var result = await factory.GetValueProviderAsync(requestContext);
var result = factory.GetValueProvider(requestContext);
// Assert
Assert.Null(result);
@ -30,14 +30,14 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
[Theory]
[InlineData("application/x-www-form-urlencoded")]
[InlineData("application/x-www-form-urlencoded;charset=utf-8")]
public async Task GetValueProvider_ReturnsValueProviderInstaceWithInvariantCulture(string contentType)
public void GetValueProvider_ReturnsValueProviderInstaceWithInvariantCulture(string contentType)
{
// Arrange
var requestContext = CreateRequestContext(contentType);
var factory = new FormValueProviderFactory();
// Act
var result = await factory.GetValueProviderAsync(requestContext);
var result = factory.GetValueProvider(requestContext);
// Assert
var valueProvider = Assert.IsType<ReadableStringCollectionValueProvider>(result);

View File

@ -3,7 +3,6 @@
using System.Collections.Generic;
using System.Globalization;
using System.Threading.Tasks;
using Microsoft.AspNet.Http;
#if NET45
using Moq;
@ -18,7 +17,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
#if NET45
[Fact]
public async Task GetValueProvider_ReturnsQueryStringValueProviderInstaceWithInvariantCulture()
public void GetValueProvider_ReturnsQueryStringValueProviderInstaceWithInvariantCulture()
{
// Arrange
var request = new Mock<HttpRequest>();
@ -29,7 +28,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
var requestContext = new RequestContext(context.Object, new Dictionary<string, object>());
// Act
var result = await _factory.GetValueProviderAsync(requestContext);
var result = _factory.GetValueProvider(requestContext);
// Assert
var valueProvider = Assert.IsType<ReadableStringCollectionValueProvider>(result);

View File

@ -24,7 +24,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
[Fact]
public async Task ContainsPrefix_WithEmptyCollection_ReturnsFalseForEmptyPrefix()
public async Task ContainsPrefixAsync_WithEmptyCollection_ReturnsFalseForEmptyPrefix()
{
// Arrange
var backingStore = new ReadableStringCollection(new Dictionary<string, string[]>());
@ -38,7 +38,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
}
[Fact]
public async Task ContainsPrefix_WithNonEmptyCollection_ReturnsTrueForEmptyPrefix()
public async Task ContainsPrefixAsync_WithNonEmptyCollection_ReturnsTrueForEmptyPrefix()
{
// Arrange
var valueProvider = new ReadableStringCollectionValueProvider(_backingStore, null);
@ -51,7 +51,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
}
[Fact]
public async Task ContainsPrefix_WithNonEmptyCollection_ReturnsTrueForKnownPrefixes()
public async Task ContainsPrefixAsync_WithNonEmptyCollection_ReturnsTrueForKnownPrefixes()
{
// Arrange
var valueProvider = new ReadableStringCollectionValueProvider(_backingStore, null);
@ -63,7 +63,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
}
[Fact]
public async Task ContainsPrefix_WithNonEmptyCollection_ReturnsFalseForUnknownPrefix()
public async Task ContainsPrefixAsync_WithNonEmptyCollection_ReturnsFalseForUnknownPrefix()
{
// Arrange
var valueProvider = new ReadableStringCollectionValueProvider(_backingStore, null);
@ -76,13 +76,13 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
}
[Fact]
public void GetKeysFromPrefix_EmptyPrefix_ReturnsAllPrefixes()
public async Task GetKeysFromPrefixAsync_EmptyPrefix_ReturnsAllPrefixes()
{
// Arrange
var valueProvider = new ReadableStringCollectionValueProvider(_backingStore, null);
// Act
IDictionary<string, string> result = valueProvider.GetKeysFromPrefix("");
var result = await valueProvider.GetKeysFromPrefixAsync("");
// Assert
Assert.Equal<KeyValuePair<string, string>>(
@ -91,35 +91,35 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
}
[Fact]
public void GetKeysFromPrefix_UnknownPrefix_ReturnsEmptyDictionary()
public async Task GetKeysFromPrefixAsync_UnknownPrefix_ReturnsEmptyDictionary()
{
// Arrange
var valueProvider = new ReadableStringCollectionValueProvider(_backingStore, null);
// Act
IDictionary<string, string> result = valueProvider.GetKeysFromPrefix("abc");
var result = await valueProvider.GetKeysFromPrefixAsync("abc");
// Assert
Assert.Empty(result);
}
[Fact]
public void GetKeysFromPrefix_KnownPrefix_ReturnsMatchingItems()
public async Task GetKeysFromPrefixAsync_KnownPrefix_ReturnsMatchingItems()
{
// Arrange
var valueProvider = new ReadableStringCollectionValueProvider(_backingStore, null);
// Act
IDictionary<string, string> result = valueProvider.GetKeysFromPrefix("bar");
var result = await valueProvider.GetKeysFromPrefixAsync("bar");
// Assert
KeyValuePair<string, string> kvp = Assert.Single(result);
var kvp = Assert.Single(result);
Assert.Equal("baz", kvp.Key);
Assert.Equal("bar.baz", kvp.Value);
}
[Fact]
public async Task GetValue_SingleValue()
public async Task GetValueAsync_SingleValue()
{
// Arrange
var culture = new CultureInfo("fr-FR");
@ -136,7 +136,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
}
[Fact]
public async Task GetValue_MultiValue()
public async Task GetValueAsync_MultiValue()
{
// Arrange
var culture = new CultureInfo("fr-FR");
@ -174,7 +174,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
//}
[Fact]
public async Task GetValue_NullMultipleValue()
public async Task GetValueAsync_NullMultipleValue()
{
// Arrange
var backingStore = new ReadableStringCollection(
@ -194,7 +194,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
}
[Fact]
public async Task GetValue_ReturnsNullIfKeyNotFound()
public async Task GetValueAsync_ReturnsNullIfKeyNotFound()
{
// Arrange
var valueProvider = new ReadableStringCollectionValueProvider(_backingStore, null);