Make IValueProvider sync, IValueProviderFactory async

This commit is contained in:
Ryan Nowak 2015-08-19 11:04:08 -07:00
parent 3519c375fc
commit 4a7ada5f64
36 changed files with 302 additions and 356 deletions

View File

@ -15,13 +15,13 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
/// </summary> /// </summary>
/// <param name="prefix">The prefix to search for.</param> /// <param name="prefix">The prefix to search for.</param>
/// <returns>true if the collection contains the specified prefix; otherwise, false.</returns> /// <returns>true if the collection contains the specified prefix; otherwise, false.</returns>
Task<bool> ContainsPrefixAsync(string prefix); bool ContainsPrefix(string prefix);
/// <summary> /// <summary>
/// Retrieves a value object using the specified key. /// Retrieves a value object using the specified key.
/// </summary> /// </summary>
/// <param name="key">The key of the value object to retrieve.</param> /// <param name="key">The key of the value object to retrieve.</param>
/// <returns>The value object for the specified key. If the exact key is not found, null.</returns> /// <returns>The value object for the specified key. If the exact key is not found, null.</returns>
Task<ValueProviderResult> GetValueAsync(string key); ValueProviderResult GetValue(string key);
} }
} }

View File

@ -1,6 +1,7 @@
// Copyright (c) .NET Foundation. All rights reserved. // 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. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Threading.Tasks;
using Microsoft.Framework.Internal; using Microsoft.Framework.Internal;
namespace Microsoft.AspNet.Mvc.ModelBinding namespace Microsoft.AspNet.Mvc.ModelBinding
@ -8,10 +9,12 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
public interface IValueProviderFactory public interface IValueProviderFactory
{ {
/// <summary> /// <summary>
/// Get a value provider with values from the given <paramref name="requestContext"/>. /// Gets a <see cref="IValueProvider"/> with values from the current request.
/// </summary> /// </summary>
/// <param name="context">ValueProviderFactoryContext that value provider will populate from</param> /// <param name="context">The <see cref="ValueProviderFactoryContext"/>.</param>
/// <returns>a value provider instance or null</returns> /// <returns>
IValueProvider GetValueProvider([NotNull] ValueProviderFactoryContext context); /// A <see cref="Task"/> that when completed will yield a <see cref="IValueProvider"/> instance or <c>null</c>.
/// </returns>
Task<IValueProvider> GetValueProviderAsync([NotNull] ValueProviderFactoryContext context);
} }
} }

View File

@ -11,7 +11,7 @@ using Microsoft.Framework.Internal;
namespace Microsoft.AspNet.Mvc.ModelBinding namespace Microsoft.AspNet.Mvc.ModelBinding
{ {
/// <summary> /// <summary>
/// Result of an <see cref="IValueProvider.GetValueAsync(string)"/> operation. /// Result of an <see cref="IValueProvider.GetValue(string)"/> operation.
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// <para> /// <para>

View File

@ -333,7 +333,7 @@ namespace Microsoft.AspNet.Mvc.Core
ActionContext.HttpContext, ActionContext.HttpContext,
ActionContext.RouteData.Values); ActionContext.RouteData.Values);
ActionBindingContext.ValueProvider = CompositeValueProvider.Create( ActionBindingContext.ValueProvider = await CompositeValueProvider.CreateAsync(
_resourceExecutingContext.ValueProviderFactories, _resourceExecutingContext.ValueProviderFactories,
valueProviderFactoryContext); valueProviderFactoryContext);

View File

@ -60,10 +60,10 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
protected BindingSource BindingSource { get; } protected BindingSource BindingSource { get; }
/// <inheritdoc /> /// <inheritdoc />
public abstract Task<bool> ContainsPrefixAsync(string prefix); public abstract bool ContainsPrefix(string prefix);
/// <inheritdoc /> /// <inheritdoc />
public abstract Task<ValueProviderResult> GetValueAsync(string key); public abstract ValueProviderResult GetValue(string key);
/// <inheritdoc /> /// <inheritdoc />
public virtual IValueProvider Filter(BindingSource bindingSource) public virtual IValueProvider Filter(BindingSource bindingSource)

View File

@ -13,7 +13,12 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
public class ByteArrayModelBinder : IModelBinder public class ByteArrayModelBinder : IModelBinder
{ {
/// <inheritdoc /> /// <inheritdoc />
public async Task<ModelBindingResult> BindModelAsync([NotNull] ModelBindingContext bindingContext) public Task<ModelBindingResult> BindModelAsync([NotNull] ModelBindingContext bindingContext)
{
return Task.FromResult(BindModel(bindingContext));
}
private ModelBindingResult BindModel(ModelBindingContext bindingContext)
{ {
// Check if this binder applies. // Check if this binder applies.
if (bindingContext.ModelType != typeof(byte[])) if (bindingContext.ModelType != typeof(byte[]))
@ -22,7 +27,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
} }
// Check for missing data case 1: There was no <input ... /> element containing this data. // Check for missing data case 1: There was no <input ... /> element containing this data.
var valueProviderResult = await bindingContext.ValueProvider.GetValueAsync(bindingContext.ModelName); var valueProviderResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
if (valueProviderResult == ValueProviderResult.None) if (valueProviderResult == ValueProviderResult.None)
{ {
return new ModelBindingResult(model: null, key: bindingContext.ModelName, isModelSet: false); return new ModelBindingResult(model: null, key: bindingContext.ModelName, isModelSet: false);

View File

@ -27,7 +27,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
ModelBindingHelper.ValidateBindingContext(bindingContext); ModelBindingHelper.ValidateBindingContext(bindingContext);
var model = bindingContext.Model; var model = bindingContext.Model;
if (!await bindingContext.ValueProvider.ContainsPrefixAsync(bindingContext.ModelName)) if (!bindingContext.ValueProvider.ContainsPrefix(bindingContext.ModelName))
{ {
// If this is the fallback case and we failed to find data for a top-level model, then generate a // If this is the fallback case and we failed to find data for a top-level model, then generate a
// default 'empty' model (or use existing Model) and return it. // default 'empty' model (or use existing Model) and return it.
@ -53,7 +53,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
return null; return null;
} }
var valueProviderResult = await bindingContext.ValueProvider.GetValueAsync(bindingContext.ModelName); var valueProviderResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
CollectionResult result; CollectionResult result;
if (valueProviderResult == ValueProviderResult.None) if (valueProviderResult == ValueProviderResult.None)
@ -188,7 +188,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
private async Task<CollectionResult> BindComplexCollection(ModelBindingContext bindingContext) private async Task<CollectionResult> BindComplexCollection(ModelBindingContext bindingContext)
{ {
var indexPropertyName = ModelNames.CreatePropertyModelName(bindingContext.ModelName, "index"); var indexPropertyName = ModelNames.CreatePropertyModelName(bindingContext.ModelName, "index");
var valueProviderResultIndex = await bindingContext.ValueProvider.GetValueAsync(indexPropertyName); var valueProviderResultIndex = bindingContext.ValueProvider.GetValue(indexPropertyName);
var indexNames = GetIndexNamesFromValueProviderResult(valueProviderResultIndex); var indexNames = GetIndexNamesFromValueProviderResult(valueProviderResultIndex);
return await BindComplexCollectionFromIndexes(bindingContext, indexNames); return await BindComplexCollectionFromIndexes(bindingContext, indexNames);

View File

@ -46,14 +46,14 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
/// A <see cref="CompositeValueProvider"/> containing all <see cref="IValueProvider"/> instances /// A <see cref="CompositeValueProvider"/> containing all <see cref="IValueProvider"/> instances
/// created. /// created.
/// </returns> /// </returns>
public static CompositeValueProvider Create( public static async Task<CompositeValueProvider> CreateAsync(
[NotNull] IEnumerable<IValueProviderFactory> factories, [NotNull] IEnumerable<IValueProviderFactory> factories,
[NotNull] ValueProviderFactoryContext context) [NotNull] ValueProviderFactoryContext context)
{ {
var composite = new CompositeValueProvider(); var composite = new CompositeValueProvider();
foreach (var valueProvidersFactory in factories) foreach (var valueProvidersFactory in factories)
{ {
var valueProvider = valueProvidersFactory.GetValueProvider(context); var valueProvider = await valueProvidersFactory.GetValueProviderAsync(context);
if (valueProvider != null) if (valueProvider != null)
{ {
composite.Add(valueProvider); composite.Add(valueProvider);
@ -64,11 +64,11 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
} }
/// <inheritdoc /> /// <inheritdoc />
public virtual async Task<bool> ContainsPrefixAsync(string prefix) public virtual bool ContainsPrefix(string prefix)
{ {
for (var i = 0; i < Count; i++) for (var i = 0; i < Count; i++)
{ {
if (await this[i].ContainsPrefixAsync(prefix)) if (this[i].ContainsPrefix(prefix))
{ {
return true; return true;
} }
@ -77,7 +77,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
} }
/// <inheritdoc /> /// <inheritdoc />
public virtual async Task<ValueProviderResult> GetValueAsync(string key) public virtual ValueProviderResult GetValue(string key)
{ {
// Performance-sensitive // Performance-sensitive
// Caching the count is faster for IList<T> // Caching the count is faster for IList<T>
@ -85,7 +85,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
for (var i = 0; i < itemCount; i++) for (var i = 0; i < itemCount; i++)
{ {
var valueProvider = Items[i]; var valueProvider = Items[i];
var result = await valueProvider.GetValueAsync(key); var result = valueProvider.GetValue(key);
if (result != ValueProviderResult.None) if (result != ValueProviderResult.None)
{ {
return result; return result;
@ -96,14 +96,14 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
} }
/// <inheritdoc /> /// <inheritdoc />
public virtual async Task<IDictionary<string, string>> GetKeysFromPrefixAsync(string prefix) public virtual IDictionary<string, string> GetKeysFromPrefix(string prefix)
{ {
foreach (var valueProvider in this) foreach (var valueProvider in this)
{ {
var enumeratedProvider = valueProvider as IEnumerableValueProvider; var enumeratedProvider = valueProvider as IEnumerableValueProvider;
if (enumeratedProvider != null) if (enumeratedProvider != null)
{ {
var result = await enumeratedProvider.GetKeysFromPrefixAsync(prefix); var result = enumeratedProvider.GetKeysFromPrefix(prefix);
if (result != null && result.Count > 0) if (result != null && result.Count > 0)
{ {
return result; return result;

View File

@ -44,27 +44,24 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
} }
/// <inheritdoc /> /// <inheritdoc />
public override Task<bool> ContainsPrefixAsync(string key) public override bool ContainsPrefix(string key)
{ {
return Task.FromResult(PrefixContainer.ContainsPrefix(key)); return PrefixContainer.ContainsPrefix(key);
} }
/// <inheritdoc /> /// <inheritdoc />
public override Task<ValueProviderResult> GetValueAsync([NotNull] string key) public override ValueProviderResult GetValue([NotNull] string key)
{ {
object value; object value;
ValueProviderResult result;
if (_values.TryGetValue(key, out value)) if (_values.TryGetValue(key, out value))
{ {
var stringValue = value as string ?? value?.ToString() ?? string.Empty; var stringValue = value as string ?? value?.ToString() ?? string.Empty;
result = new ValueProviderResult(stringValue, CultureInfo.InvariantCulture); return new ValueProviderResult(stringValue, CultureInfo.InvariantCulture);
} }
else else
{ {
result = ValueProviderResult.None; return ValueProviderResult.None;
} }
return Task.FromResult(result);
} }
} }
} }

View File

@ -47,7 +47,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
} }
// Attempt to bind dictionary from a set of prefix[key]=value entries. Get the short and long keys first. // Attempt to bind dictionary from a set of prefix[key]=value entries. Get the short and long keys first.
var keys = await enumerableValueProvider.GetKeysFromPrefixAsync(bindingContext.ModelName); var keys = enumerableValueProvider.GetKeysFromPrefix(bindingContext.ModelName);
if (!keys.Any()) if (!keys.Any())
{ {
// No entries with the expected keys. // No entries with the expected keys.

View File

@ -3,7 +3,6 @@
using System; using System;
using System.Globalization; using System.Globalization;
using System.Threading.Tasks;
namespace Microsoft.AspNet.Mvc.ModelBinding namespace Microsoft.AspNet.Mvc.ModelBinding
{ {
@ -22,20 +21,20 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
public string Value { get; } public string Value { get; }
public Task<bool> ContainsPrefixAsync(string prefix) public bool ContainsPrefix(string prefix)
{ {
return Task.FromResult(PrefixContainer.IsPrefixMatch(prefix, Key)); return PrefixContainer.IsPrefixMatch(prefix, Key);
} }
public Task<ValueProviderResult> GetValueAsync(string key) public ValueProviderResult GetValue(string key)
{ {
if (string.Equals(key, Key, StringComparison.OrdinalIgnoreCase)) if (string.Equals(key, Key, StringComparison.OrdinalIgnoreCase))
{ {
return Task.FromResult(new ValueProviderResult(Value, Culture)); return new ValueProviderResult(Value, Culture);
} }
else else
{ {
return Task.FromResult(ValueProviderResult.None); return ValueProviderResult.None;
} }
} }
} }

View File

@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Globalization; using System.Globalization;
using System.Threading.Tasks;
using Microsoft.AspNet.Http; using Microsoft.AspNet.Http;
using Microsoft.Framework.Internal; using Microsoft.Framework.Internal;
@ -9,7 +10,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
{ {
public class FormValueProviderFactory : IValueProviderFactory public class FormValueProviderFactory : IValueProviderFactory
{ {
public IValueProvider GetValueProvider([NotNull] ValueProviderFactoryContext context) public async Task<IValueProvider> GetValueProviderAsync([NotNull] ValueProviderFactoryContext context)
{ {
var request = context.HttpContext.Request; var request = context.HttpContext.Request;
@ -19,7 +20,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
return new ReadableStringCollectionValueProvider( return new ReadableStringCollectionValueProvider(
BindingSource.Form, BindingSource.Form,
async () => await request.ReadFormAsync(), await request.ReadFormAsync(),
culture); culture);
} }

View File

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

View File

@ -1,7 +1,6 @@
// Copyright (c) .NET Foundation. All rights reserved. // 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. // 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.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Globalization; using System.Globalization;
@ -11,15 +10,12 @@ using Microsoft.Framework.Internal;
namespace Microsoft.AspNet.Mvc.ModelBinding namespace Microsoft.AspNet.Mvc.ModelBinding
{ {
/// <summary> /// <summary>
/// An <see cref="IValueProvider"/> for form data stored in an <see cref="IDictionary{string, string[]}"/> and /// An <see cref="IValueProvider"/> for form data stored in an <see cref="IDictionary{string, string[]}"/>.
/// generally accessed asynchronously.
/// </summary> /// </summary>
public class JQueryFormValueProvider : BindingSourceValueProvider, IEnumerableValueProvider public class JQueryFormValueProvider : BindingSourceValueProvider, IEnumerableValueProvider
{ {
private readonly Func<Task<IDictionary<string, string[]>>> _valuesFactory;
private PrefixContainer _prefixContainer; private PrefixContainer _prefixContainer;
private IDictionary<string, string[]> _values; private readonly IDictionary<string, string[]> _values;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="DictionaryBasedValueProvider"/> class. /// Initializes a new instance of the <see cref="DictionaryBasedValueProvider"/> class.
@ -28,17 +24,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
/// <param name="valuesFactory">A delegate which provides the values to wrap.</param> /// <param name="valuesFactory">A delegate which provides the values to wrap.</param>
/// <param name="culture">The culture to return with ValueProviderResult instances.</param> /// <param name="culture">The culture to return with ValueProviderResult instances.</param>
public JQueryFormValueProvider( public JQueryFormValueProvider(
[NotNull] BindingSource bindingSource,
[NotNull] Func<Task<IDictionary<string, string[]>>> valuesFactory,
CultureInfo culture)
: base(bindingSource)
{
_valuesFactory = valuesFactory;
Culture = culture;
}
// Internal for testing.
internal JQueryFormValueProvider(
[NotNull] BindingSource bindingSource, [NotNull] BindingSource bindingSource,
[NotNull] IDictionary<string, string[]> values, [NotNull] IDictionary<string, string[]> values,
CultureInfo culture) CultureInfo culture)
@ -51,58 +36,41 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
// Internal for testing // Internal for testing
internal CultureInfo Culture { get; } internal CultureInfo Culture { get; }
/// <inheritdoc /> protected PrefixContainer PrefixContainer
public override async Task<bool> ContainsPrefixAsync(string prefix)
{ {
var prefixContainer = await GetPrefixContainerAsync(); get
return prefixContainer.ContainsPrefix(prefix); {
if (_prefixContainer == null)
{
_prefixContainer = new PrefixContainer(_values.Keys);
}
return _prefixContainer;
}
} }
/// <inheritdoc /> /// <inheritdoc />
public async Task<IDictionary<string, string>> GetKeysFromPrefixAsync(string prefix) public override bool ContainsPrefix(string prefix)
{ {
var prefixContainer = await GetPrefixContainerAsync(); return PrefixContainer.ContainsPrefix(prefix);
return prefixContainer.GetKeysFromPrefix(prefix);
} }
/// <inheritdoc /> /// <inheritdoc />
public override async Task<ValueProviderResult> GetValueAsync(string key) public IDictionary<string, string> GetKeysFromPrefix(string prefix)
{ {
var dictionary = await GetDictionary(); return PrefixContainer.GetKeysFromPrefix(prefix);
}
/// <inheritdoc />
public override ValueProviderResult GetValue(string key)
{
string[] values; string[] values;
if (dictionary.TryGetValue(key, out values) && values != null && values.Length > 0) if (_values.TryGetValue(key, out values) && values != null && values.Length > 0)
{ {
return new ValueProviderResult(values, Culture); return new ValueProviderResult(values, Culture);
} }
return ValueProviderResult.None; return ValueProviderResult.None;
} }
private async Task<IDictionary<string, string[]>> GetDictionary()
{
if (_values == null)
{
Debug.Assert(_valuesFactory != null);
// Initialization race is OK providing data remains read-only.
_values = await _valuesFactory();
}
return _values;
}
private async Task<PrefixContainer> GetPrefixContainerAsync()
{
if (_prefixContainer == null)
{
var dictionary = await GetDictionary();
// Initialization race is OK providing data remains read-only and object identity is not significant.
_prefixContainer = new PrefixContainer(dictionary.Keys);
}
return _prefixContainer;
}
} }
} }

View File

@ -14,7 +14,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
{ {
public class JQueryFormValueProviderFactory : IValueProviderFactory public class JQueryFormValueProviderFactory : IValueProviderFactory
{ {
public IValueProvider GetValueProvider([NotNull] ValueProviderFactoryContext context) public async Task<IValueProvider> GetValueProviderAsync([NotNull] ValueProviderFactoryContext context)
{ {
var request = context.HttpContext.Request; var request = context.HttpContext.Request;
@ -22,7 +22,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
{ {
return new JQueryFormValueProvider( return new JQueryFormValueProvider(
BindingSource.Form, BindingSource.Form,
() => GetValueCollectionAsync(request), await GetValueCollectionAsync(request),
CultureInfo.CurrentCulture); CultureInfo.CurrentCulture);
} }

View File

@ -35,7 +35,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
PropertyMetadata = GetMetadataForProperties(bindingContext).ToArray(), PropertyMetadata = GetMetadataForProperties(bindingContext).ToArray(),
}; };
if (!(await CanCreateModel(mutableObjectBinderContext))) if (!(CanCreateModel(mutableObjectBinderContext)))
{ {
return null; return null;
} }
@ -72,7 +72,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
return CanUpdatePropertyInternal(propertyMetadata); return CanUpdatePropertyInternal(propertyMetadata);
} }
internal async Task<bool> CanCreateModel(MutableObjectBinderContext context) internal bool CanCreateModel(MutableObjectBinderContext context)
{ {
var bindingContext = context.ModelBindingContext; var bindingContext = context.ModelBindingContext;
var isTopLevelObject = bindingContext.IsTopLevelObject; var isTopLevelObject = bindingContext.IsTopLevelObject;
@ -109,7 +109,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
} }
// 3. Any of the model properties can be bound using a value provider. // 3. Any of the model properties can be bound using a value provider.
if (await CanValueBindAnyModelProperties(context)) if (CanValueBindAnyModelProperties(context))
{ {
return true; return true;
} }
@ -117,7 +117,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
return false; return false;
} }
private async Task<bool> CanValueBindAnyModelProperties(MutableObjectBinderContext context) private bool CanValueBindAnyModelProperties(MutableObjectBinderContext context)
{ {
// If there are no properties on the model, there is nothing to bind. We are here means this is not a top // If there are no properties on the model, there is nothing to bind. We are here means this is not a top
// level object. So we return false. // level object. So we return false.
@ -170,7 +170,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
propertyMetadata); propertyMetadata);
// If any property can return a true value. // If any property can return a true value.
if (await CanBindValue(propertyModelBindingContext)) if (CanBindValue(propertyModelBindingContext))
{ {
return true; return true;
} }
@ -189,7 +189,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
return false; return false;
} }
private async Task<bool> CanBindValue(ModelBindingContext bindingContext) private bool CanBindValue(ModelBindingContext bindingContext)
{ {
var valueProvider = bindingContext.ValueProvider; var valueProvider = bindingContext.ValueProvider;
@ -209,7 +209,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
} }
} }
if (await valueProvider.ContainsPrefixAsync(bindingContext.ModelName)) if (valueProvider.ContainsPrefix(bindingContext.ModelName))
{ {
return true; return true;
} }

View File

@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Globalization; using System.Globalization;
using System.Threading.Tasks;
using Microsoft.Framework.Internal; using Microsoft.Framework.Internal;
namespace Microsoft.AspNet.Mvc.ModelBinding namespace Microsoft.AspNet.Mvc.ModelBinding
@ -13,12 +14,12 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
public class QueryStringValueProviderFactory : IValueProviderFactory public class QueryStringValueProviderFactory : IValueProviderFactory
{ {
/// <inheritdoc /> /// <inheritdoc />
public IValueProvider GetValueProvider([NotNull] ValueProviderFactoryContext context) public Task<IValueProvider> GetValueProviderAsync([NotNull] ValueProviderFactoryContext context)
{ {
return new ReadableStringCollectionValueProvider( return Task.FromResult<IValueProvider>(new ReadableStringCollectionValueProvider(
BindingSource.Query, BindingSource.Query,
context.HttpContext.Request.Query, context.HttpContext.Request.Query,
CultureInfo.InvariantCulture); CultureInfo.InvariantCulture));
} }
} }
} }

View File

@ -18,7 +18,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
public class ReadableStringCollectionValueProvider : BindingSourceValueProvider, IEnumerableValueProvider public class ReadableStringCollectionValueProvider : BindingSourceValueProvider, IEnumerableValueProvider
{ {
private readonly CultureInfo _culture; private readonly CultureInfo _culture;
private readonly Func<Task<IReadableStringCollection>> _valuesFactory;
private PrefixContainer _prefixContainer; private PrefixContainer _prefixContainer;
private IReadableStringCollection _values; private IReadableStringCollection _values;
@ -38,23 +37,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
_culture = culture; _culture = culture;
} }
/// <summary>
/// Creates a provider for <see cref="IReadableStringCollection"/> wrapping an
/// existing set of key value pairs provided by the delegate.
/// </summary>
/// <param name="bindingSource">The <see cref="BindingSource"/> for the data.</param>
/// <param name="values">The delegate that provides the key value pairs to wrap.</param>
/// <param name="culture">The culture to return with ValueProviderResult instances.</param>
public ReadableStringCollectionValueProvider(
[NotNull] BindingSource bindingSource,
[NotNull] Func<Task<IReadableStringCollection>> valuesFactory,
CultureInfo culture)
: base(bindingSource)
{
_valuesFactory = valuesFactory;
_culture = culture;
}
public CultureInfo Culture public CultureInfo Culture
{ {
get get
@ -63,59 +45,43 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
} }
} }
/// <inheritdoc /> protected PrefixContainer PrefixContainer
public override async Task<bool> ContainsPrefixAsync(string prefix)
{ {
var prefixContainer = await GetPrefixContainerAsync(); get
return prefixContainer.ContainsPrefix(prefix); {
if (_prefixContainer == null)
{
_prefixContainer = new PrefixContainer(_values.Keys);
}
return _prefixContainer;
}
} }
/// <inheritdoc /> /// <inheritdoc />
public virtual async Task<IDictionary<string, string>> GetKeysFromPrefixAsync([NotNull] string prefix) public override bool ContainsPrefix(string prefix)
{ {
var prefixContainer = await GetPrefixContainerAsync(); return PrefixContainer.ContainsPrefix(prefix);
return prefixContainer.GetKeysFromPrefix(prefix);
} }
/// <inheritdoc /> /// <inheritdoc />
public override async Task<ValueProviderResult> GetValueAsync([NotNull] string key) public virtual IDictionary<string, string> GetKeysFromPrefix([NotNull] string prefix)
{ {
var collection = await GetValueCollectionAsync(); return PrefixContainer.GetKeysFromPrefix(prefix);
var values = collection.GetValues(key); }
ValueProviderResult result; /// <inheritdoc />
public override ValueProviderResult GetValue([NotNull] string key)
{
var values = _values.GetValues(key);
if (values == null) if (values == null)
{ {
result = ValueProviderResult.None; return ValueProviderResult.None;
} }
else else
{ {
result = new ValueProviderResult(values.ToArray(), _culture); return new ValueProviderResult(values.ToArray(), _culture);
} }
return result;
}
private async Task<IReadableStringCollection> GetValueCollectionAsync()
{
if (_values == null)
{
Debug.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
var collection = await GetValueCollectionAsync();
_prefixContainer = new PrefixContainer(collection.Keys);
}
return _prefixContainer;
} }
} }
} }

View File

@ -1,15 +1,18 @@
// Copyright (c) .NET Foundation. All rights reserved. // 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. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Threading.Tasks;
using Microsoft.Framework.Internal; using Microsoft.Framework.Internal;
namespace Microsoft.AspNet.Mvc.ModelBinding namespace Microsoft.AspNet.Mvc.ModelBinding
{ {
public class RouteValueValueProviderFactory : IValueProviderFactory public class RouteValueValueProviderFactory : IValueProviderFactory
{ {
public IValueProvider GetValueProvider([NotNull] ValueProviderFactoryContext context) public Task<IValueProvider> GetValueProviderAsync([NotNull] ValueProviderFactoryContext context)
{ {
return new DictionaryBasedValueProvider(BindingSource.Path, context.RouteValues); return Task.FromResult<IValueProvider>(new DictionaryBasedValueProvider(
BindingSource.Path,
context.RouteValues));
} }
} }
} }

View File

@ -10,7 +10,12 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
{ {
public class SimpleTypeModelBinder : IModelBinder public class SimpleTypeModelBinder : IModelBinder
{ {
public async Task<ModelBindingResult> BindModelAsync(ModelBindingContext bindingContext) public Task<ModelBindingResult> BindModelAsync(ModelBindingContext bindingContext)
{
return Task.FromResult(BindModel(bindingContext));
}
public ModelBindingResult BindModel(ModelBindingContext bindingContext)
{ {
if (bindingContext.ModelMetadata.IsComplexType) if (bindingContext.ModelMetadata.IsComplexType)
{ {
@ -18,7 +23,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
return null; return null;
} }
var valueProviderResult = await bindingContext.ValueProvider.GetValueAsync(bindingContext.ModelName); var valueProviderResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
if (valueProviderResult == ValueProviderResult.None) if (valueProviderResult == ValueProviderResult.None)
{ {
// no entry // no entry
@ -60,7 +65,11 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
new ModelValidationNode(bindingContext.ModelName, bindingContext.ModelMetadata, model) : new ModelValidationNode(bindingContext.ModelName, bindingContext.ModelMetadata, model) :
null; null;
return new ModelBindingResult(model, bindingContext.ModelName, isModelSet, validationNode); return new ModelBindingResult(
model,
bindingContext.ModelName,
isModelSet,
validationNode);
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -69,7 +78,10 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
// Were able to find a converter for the type but conversion failed. // Were able to find a converter for the type but conversion failed.
// Tell the model binding system to skip other model binders i.e. return non-null. // Tell the model binding system to skip other model binders i.e. return non-null.
return new ModelBindingResult(model: null, key: bindingContext.ModelName, isModelSet: false); return new ModelBindingResult(
model: null,
key: bindingContext.ModelName,
isModelSet: false);
} }
private static bool AllowsNullValue(Type type) private static bool AllowsNullValue(Type type)

View File

@ -193,15 +193,15 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
var mockIntBinder = new Mock<IModelBinder>(); var mockIntBinder = new Mock<IModelBinder>();
mockIntBinder mockIntBinder
.Setup(o => o.BindModelAsync(It.IsAny<ModelBindingContext>())) .Setup(o => o.BindModelAsync(It.IsAny<ModelBindingContext>()))
.Returns(async (ModelBindingContext mbc) => .Returns((ModelBindingContext mbc) =>
{ {
var value = await mbc.ValueProvider.GetValueAsync(mbc.ModelName); var value = mbc.ValueProvider.GetValue(mbc.ModelName);
if (value != ValueProviderResult.None) if (value != ValueProviderResult.None)
{ {
var model = value.ConvertTo(mbc.ModelType); var model = value.ConvertTo(mbc.ModelType);
return new ModelBindingResult(model, key: null, isModelSet: true); return Task.FromResult(new ModelBindingResult(model, key: null, isModelSet: true));
} }
return null; return Task.FromResult<ModelBindingResult>(null);
}); });
return mockIntBinder.Object; return mockIntBinder.Object;
} }

View File

@ -83,12 +83,12 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
{ {
} }
public override Task<bool> ContainsPrefixAsync(string prefix) public override bool ContainsPrefix(string prefix)
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }
public override Task<ValueProviderResult> GetValueAsync(string key) public override ValueProviderResult GetValue(string key)
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }

View File

@ -435,17 +435,21 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
Mock<IModelBinder> mockIntBinder = new Mock<IModelBinder>(); Mock<IModelBinder> mockIntBinder = new Mock<IModelBinder>();
mockIntBinder mockIntBinder
.Setup(o => o.BindModelAsync(It.IsAny<ModelBindingContext>())) .Setup(o => o.BindModelAsync(It.IsAny<ModelBindingContext>()))
.Returns(async (ModelBindingContext mbc) => .Returns((ModelBindingContext mbc) =>
{ {
var value = await mbc.ValueProvider.GetValueAsync(mbc.ModelName); var value = mbc.ValueProvider.GetValue(mbc.ModelName);
if (value != ValueProviderResult.None) if (value != ValueProviderResult.None)
{ {
var model = value.ConvertTo(mbc.ModelType); var model = value.ConvertTo(mbc.ModelType);
var modelValidationNode = new ModelValidationNode(mbc.ModelName, mbc.ModelMetadata, model); var modelValidationNode = new ModelValidationNode(mbc.ModelName, mbc.ModelMetadata, model);
return new ModelBindingResult(model, mbc.ModelName, model != null, modelValidationNode); return Task.FromResult(new ModelBindingResult(
model,
mbc.ModelName,
model != null,
modelValidationNode));
} }
return null; return Task.FromResult<ModelBindingResult>(null);
}); });
return mockIntBinder.Object; return mockIntBinder.Object;
} }

View File

@ -30,7 +30,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
#if DNX451 #if DNX451
[Fact] [Fact]
public async Task GetKeysFromPrefixAsync_ReturnsResultFromFirstValueProviderThatReturnsValues() public void GetKeysFromPrefixAsync_ReturnsResultFromFirstValueProviderThatReturnsValues()
{ {
// Arrange // Arrange
var provider1 = Mock.Of<IValueProvider>(); var provider1 = Mock.Of<IValueProvider>();
@ -39,13 +39,13 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
{ "prefix-test", "some-value" }, { "prefix-test", "some-value" },
}; };
var provider2 = new Mock<IEnumerableValueProvider>(); var provider2 = new Mock<IEnumerableValueProvider>();
provider2.Setup(p => p.GetKeysFromPrefixAsync("prefix")) provider2.Setup(p => p.GetKeysFromPrefix("prefix"))
.Returns(Task.FromResult<IDictionary<string, string>>(dictionary)) .Returns(dictionary)
.Verifiable(); .Verifiable();
var provider = new CompositeValueProvider(new[] { provider1, provider2.Object }); var provider = new CompositeValueProvider(new[] { provider1, provider2.Object });
// Act // Act
var values = await provider.GetKeysFromPrefixAsync("prefix"); var values = provider.GetKeysFromPrefix("prefix");
// Assert // Assert
var result = Assert.Single(values); var result = Assert.Single(values);
@ -55,7 +55,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
} }
[Fact] [Fact]
public async Task GetKeysFromPrefixAsync_ReturnsEmptyDictionaryIfNoValueProviderReturnsValues() public void GetKeysFromPrefixAsync_ReturnsEmptyDictionaryIfNoValueProviderReturnsValues()
{ {
// Arrange // Arrange
var provider1 = Mock.Of<IValueProvider>(); var provider1 = Mock.Of<IValueProvider>();
@ -63,7 +63,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
var provider = new CompositeValueProvider(new[] { provider1, provider2 }); var provider = new CompositeValueProvider(new[] { provider1, provider2 });
// Act // Act
var values = await provider.GetKeysFromPrefixAsync("prefix"); var values = provider.GetKeysFromPrefix("prefix");
// Assert // Assert
Assert.Empty(values); Assert.Empty(values);

View File

@ -11,7 +11,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
public class DictionaryBasedValueProviderTests public class DictionaryBasedValueProviderTests
{ {
[Fact] [Fact]
public async Task GetValueProvider_ReturnsNull_WhenKeyIsNotFound() public void GetValueProvider_ReturnsNull_WhenKeyIsNotFound()
{ {
// Arrange // Arrange
var values = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase) var values = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase)
@ -21,14 +21,14 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
var provider = new DictionaryBasedValueProvider(BindingSource.Query, values); var provider = new DictionaryBasedValueProvider(BindingSource.Query, values);
// Act // Act
var result = await provider.GetValueAsync("not-test-key"); var result = provider.GetValue("not-test-key");
// Assert // Assert
Assert.Equal(ValueProviderResult.None, result); Assert.Equal(ValueProviderResult.None, result);
} }
[Fact] [Fact]
public async Task GetValueProvider_ReturnsValue_IfKeyIsPresent() public void GetValueProvider_ReturnsValue_IfKeyIsPresent()
{ {
// Arrange // Arrange
var values = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase) var values = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase)
@ -38,14 +38,14 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
var provider = new DictionaryBasedValueProvider(BindingSource.Query, values); var provider = new DictionaryBasedValueProvider(BindingSource.Query, values);
// Act // Act
var result = await provider.GetValueAsync("test-key"); var result = provider.GetValue("test-key");
// Assert // Assert
Assert.Equal("test-value", (string)result); Assert.Equal("test-value", (string)result);
} }
[Fact] [Fact]
public async Task ContainsPrefixAsync_ReturnsNullValue_IfKeyIsPresent() public void ContainsPrefix_ReturnsNullValue_IfKeyIsPresent()
{ {
// Arrange // Arrange
var values = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase) var values = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase)
@ -55,7 +55,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
var provider = new DictionaryBasedValueProvider(BindingSource.Query, values); var provider = new DictionaryBasedValueProvider(BindingSource.Query, values);
// Act // Act
var result = await provider.GetValueAsync("test-key"); var result = provider.GetValue("test-key");
// Assert // Assert
Assert.Equal(string.Empty, (string)result); Assert.Equal(string.Empty, (string)result);
@ -65,7 +65,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
[InlineData("foo")] [InlineData("foo")]
[InlineData("bar")] [InlineData("bar")]
[InlineData("bar.baz")] [InlineData("bar.baz")]
public async Task ContainsPrefixAsync_ReturnsTrue_ForKnownPrefixes(string prefix) public void ContainsPrefix_ReturnsTrue_ForKnownPrefixes(string prefix)
{ {
// Arrange // Arrange
var values = new Dictionary<string, object> var values = new Dictionary<string, object>
@ -77,7 +77,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
var valueProvider = new DictionaryBasedValueProvider(BindingSource.Query, values); var valueProvider = new DictionaryBasedValueProvider(BindingSource.Query, values);
// Act // Act
var result = await valueProvider.ContainsPrefixAsync(prefix); var result = valueProvider.ContainsPrefix(prefix);
// Assert // Assert
Assert.True(result); Assert.True(result);
@ -86,7 +86,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
[Theory] [Theory]
[InlineData("bar", "1")] [InlineData("bar", "1")]
[InlineData("bar.baz", "2")] [InlineData("bar.baz", "2")]
public async Task GetValueAsync_ReturnsCorrectValue_ForKnownKeys(string prefix, string expectedValue) public void GetValue_ReturnsCorrectValue_ForKnownKeys(string prefix, string expectedValue)
{ {
// Arrange // Arrange
var values = new Dictionary<string, object> var values = new Dictionary<string, object>
@ -98,14 +98,14 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
var valueProvider = new DictionaryBasedValueProvider(BindingSource.Query, values); var valueProvider = new DictionaryBasedValueProvider(BindingSource.Query, values);
// Act // Act
var result = await valueProvider.GetValueAsync(prefix); var result = valueProvider.GetValue(prefix);
// Assert // Assert
Assert.Equal(expectedValue, (string)result); Assert.Equal(expectedValue, (string)result);
} }
[Fact] [Fact]
public async Task GetValueAsync_DoesNotReturnAValue_ForAKeyPrefix() public void GetValue_DoesNotReturnAValue_ForAKeyPrefix()
{ {
// Arrange // Arrange
var values = new Dictionary<string, object> var values = new Dictionary<string, object>
@ -116,14 +116,14 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
var valueProvider = new DictionaryBasedValueProvider(BindingSource.Query, values); var valueProvider = new DictionaryBasedValueProvider(BindingSource.Query, values);
// Act // Act
var result = await valueProvider.GetValueAsync("bar"); var result = valueProvider.GetValue("bar");
// Assert // Assert
Assert.Equal(ValueProviderResult.None, result); Assert.Equal(ValueProviderResult.None, result);
} }
[Fact] [Fact]
public async Task ContainsPrefixAsync_ReturnsFalse_IfKeyIsNotPresent() public void ContainsPrefix_ReturnsFalse_IfKeyIsNotPresent()
{ {
// Arrange // Arrange
var values = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase) var values = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase)
@ -133,14 +133,14 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
var provider = new DictionaryBasedValueProvider(BindingSource.Query, values); var provider = new DictionaryBasedValueProvider(BindingSource.Query, values);
// Act // Act
var result = await provider.ContainsPrefixAsync("not-test-key"); var result = provider.ContainsPrefix("not-test-key");
// Assert // Assert
Assert.False(result); Assert.False(result);
} }
[Fact] [Fact]
public async Task ContainsPrefixAsync_ReturnsTrue_IfKeyIsPresent() public void ContainsPrefix_ReturnsTrue_IfKeyIsPresent()
{ {
// Arrange // Arrange
var values = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase) var values = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase)
@ -150,7 +150,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
var provider = new DictionaryBasedValueProvider(BindingSource.Query, values); var provider = new DictionaryBasedValueProvider(BindingSource.Query, values);
// Act // Act
var result = await provider.ContainsPrefixAsync("test-key"); var result = provider.ContainsPrefix("test-key");
// Assert // Assert
Assert.True(result); Assert.True(result);

View File

@ -14,8 +14,9 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
[InlineData("MyProperty", "MyProperty")] [InlineData("MyProperty", "MyProperty")]
[InlineData("MyProperty.SubProperty", "MyProperty")] [InlineData("MyProperty.SubProperty", "MyProperty")]
[InlineData("MyProperty[0]", "MyProperty")] [InlineData("MyProperty[0]", "MyProperty")]
public async Task ContainsPrefixAsync_ReturnsTrue_IfElementNameStartsWithPrefix(string elementName, public void ContainsPrefix_ReturnsTrue_IfElementNameStartsWithPrefix(
string prefix) string elementName,
string prefix)
{ {
// Arrange // Arrange
var culture = new CultureInfo("en-US"); var culture = new CultureInfo("en-US");
@ -25,7 +26,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
culture); culture);
// Act // Act
var containsPrefix = await elementalValueProvider.ContainsPrefixAsync(prefix); var containsPrefix = elementalValueProvider.ContainsPrefix(prefix);
// Assert // Assert
Assert.True(containsPrefix); Assert.True(containsPrefix);
@ -35,8 +36,9 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
[InlineData("MyProperty", "MyProperty1")] [InlineData("MyProperty", "MyProperty1")]
[InlineData("MyPropertyTest", "MyProperty")] [InlineData("MyPropertyTest", "MyProperty")]
[InlineData("Random", "MyProperty")] [InlineData("Random", "MyProperty")]
public async Task ContainsPrefixAsync_ReturnsFalse_IfElementCannotSpecifyValuesForPrefix(string elementName, public void ContainsPrefix_ReturnsFalse_IfElementCannotSpecifyValuesForPrefix(
string prefix) string elementName,
string prefix)
{ {
// Arrange // Arrange
var culture = new CultureInfo("en-US"); var culture = new CultureInfo("en-US");
@ -46,21 +48,21 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
culture); culture);
// Act // Act
var containsPrefix = await elementalValueProvider.ContainsPrefixAsync(prefix); var containsPrefix = elementalValueProvider.ContainsPrefix(prefix);
// Assert // Assert
Assert.False(containsPrefix); Assert.False(containsPrefix);
} }
[Fact] [Fact]
public async Task GetValueAsync_NameDoesNotMatch_ReturnsEmptyResult() public void GetValue_NameDoesNotMatch_ReturnsEmptyResult()
{ {
// Arrange // Arrange
var culture = new CultureInfo("fr-FR"); var culture = new CultureInfo("fr-FR");
var valueProvider = new ElementalValueProvider("foo", "hi", culture); var valueProvider = new ElementalValueProvider("foo", "hi", culture);
// Act // Act
var result = await valueProvider.GetValueAsync("bar"); var result = valueProvider.GetValue("bar");
// Assert // Assert
Assert.Equal(ValueProviderResult.None, result); Assert.Equal(ValueProviderResult.None, result);
@ -70,14 +72,14 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
[InlineData("foo")] [InlineData("foo")]
[InlineData("FOO")] [InlineData("FOO")]
[InlineData("FoO")] [InlineData("FoO")]
public async Task GetValueAsync_NameMatches_ReturnsValueProviderResult(string name) public void GetValue_NameMatches_ReturnsValueProviderResult(string name)
{ {
// Arrange // Arrange
var culture = new CultureInfo("fr-FR"); var culture = new CultureInfo("fr-FR");
var valueProvider = new ElementalValueProvider("foo", "hi", culture); var valueProvider = new ElementalValueProvider("foo", "hi", culture);
// Act // Act
var result = await valueProvider.GetValueAsync(name); var result = valueProvider.GetValue(name);
// Assert // Assert
Assert.NotNull(result); Assert.NotNull(result);

View File

@ -29,61 +29,62 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
}; };
[Fact] [Fact]
public async Task ContainsPrefixAsync_WithEmptyCollection_ReturnsFalseForEmptyPrefix() public void ContainsPrefix_WithEmptyCollection_ReturnsFalseForEmptyPrefix()
{ {
// Arrange // Arrange
var backingStore = new Dictionary<string, string[]>(); var backingStore = new Dictionary<string, string[]>();
var valueProvider = GetEnumerableValueProvider(BindingSource.Query, backingStore, culture: null); var valueProvider = GetEnumerableValueProvider(BindingSource.Query, backingStore, culture: null);
// Act // Act
var result = await valueProvider.ContainsPrefixAsync(string.Empty); var result = valueProvider.ContainsPrefix(string.Empty);
// Assert // Assert
Assert.False(result); Assert.False(result);
} }
[Fact] [Fact]
public async Task ContainsPrefixAsync_WithNonEmptyCollection_ReturnsTrueForEmptyPrefix() public void ContainsPrefix_WithNonEmptyCollection_ReturnsTrueForEmptyPrefix()
{ {
// Arrange // Arrange
var valueProvider = GetEnumerableValueProvider(BindingSource.Query, _backingStore, culture: null); var valueProvider = GetEnumerableValueProvider(BindingSource.Query, _backingStore, culture: null);
// Act // Act
var result = await valueProvider.ContainsPrefixAsync(string.Empty); var result = valueProvider.ContainsPrefix(string.Empty);
// Assert // Assert
Assert.True(result); Assert.True(result);
} }
[Fact] [Theory]
public async Task ContainsPrefixAsync_WithNonEmptyCollection_ReturnsTrueForKnownPrefixes() [InlineData("some")]
[InlineData("prefix")]
[InlineData("prefix.name")]
[InlineData("[index]")]
[InlineData("prefix[index1]")]
public void ContainsPrefix_WithNonEmptyCollection_ReturnsTrueForKnownPrefixes(string prefix)
{ {
// Arrange // Arrange
var valueProvider = GetEnumerableValueProvider(BindingSource.Query, _backingStore, culture: null); var valueProvider = GetEnumerableValueProvider(BindingSource.Query, _backingStore, culture: null);
// Act & Assert // Act & Assert
Assert.True(await valueProvider.ContainsPrefixAsync("some")); Assert.True(valueProvider.ContainsPrefix(prefix));
Assert.True(await valueProvider.ContainsPrefixAsync("prefix"));
Assert.True(await valueProvider.ContainsPrefixAsync("prefix.name"));
Assert.True(await valueProvider.ContainsPrefixAsync("[index]"));
Assert.True(await valueProvider.ContainsPrefixAsync("prefix[index1]"));
} }
[Fact] [Fact]
public async Task ContainsPrefixAsync_WithNonEmptyCollection_ReturnsFalseForUnknownPrefix() public void ContainsPrefix_WithNonEmptyCollection_ReturnsFalseForUnknownPrefix()
{ {
// Arrange // Arrange
var valueProvider = GetEnumerableValueProvider(BindingSource.Query, _backingStore, culture: null); var valueProvider = GetEnumerableValueProvider(BindingSource.Query, _backingStore, culture: null);
// Act // Act
var result = await valueProvider.ContainsPrefixAsync("biff"); var result = valueProvider.ContainsPrefix("biff");
// Assert // Assert
Assert.False(result); Assert.False(result);
} }
[Fact] [Fact]
public async Task GetKeysFromPrefixAsync_EmptyPrefix_ReturnsAllPrefixes() public void GetKeysFromPrefix_EmptyPrefix_ReturnsAllPrefixes()
{ {
// Arrange // Arrange
var expected = new Dictionary<string, string> var expected = new Dictionary<string, string>
@ -96,27 +97,27 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
var valueProvider = GetEnumerableValueProvider(BindingSource.Query, _backingStore, culture: null); var valueProvider = GetEnumerableValueProvider(BindingSource.Query, _backingStore, culture: null);
// Act // Act
var result = await valueProvider.GetKeysFromPrefixAsync(string.Empty); var result = valueProvider.GetKeysFromPrefix(string.Empty);
// Assert // Assert
Assert.Equal(expected, result.OrderBy(kvp => kvp.Key)); Assert.Equal(expected, result.OrderBy(kvp => kvp.Key));
} }
[Fact] [Fact]
public async Task GetKeysFromPrefixAsync_UnknownPrefix_ReturnsEmptyDictionary() public void GetKeysFromPrefix_UnknownPrefix_ReturnsEmptyDictionary()
{ {
// Arrange // Arrange
var valueProvider = GetEnumerableValueProvider(BindingSource.Query, _backingStore, culture: null); var valueProvider = GetEnumerableValueProvider(BindingSource.Query, _backingStore, culture: null);
// Act // Act
var result = await valueProvider.GetKeysFromPrefixAsync("abc"); var result = valueProvider.GetKeysFromPrefix("abc");
// Assert // Assert
Assert.Empty(result); Assert.Empty(result);
} }
[Fact] [Fact]
public async Task GetKeysFromPrefixAsync_KnownPrefix_ReturnsMatchingItems() public void GetKeysFromPrefix_KnownPrefix_ReturnsMatchingItems()
{ {
// Arrange // Arrange
var expected = new Dictionary<string, string> var expected = new Dictionary<string, string>
@ -131,14 +132,14 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
var valueProvider = GetEnumerableValueProvider(BindingSource.Query, _backingStore, culture: null); var valueProvider = GetEnumerableValueProvider(BindingSource.Query, _backingStore, culture: null);
// Act // Act
var result = await valueProvider.GetKeysFromPrefixAsync("prefix"); var result = valueProvider.GetKeysFromPrefix("prefix");
// Assert // Assert
Assert.Equal(expected, result); Assert.Equal(expected, result);
} }
[Fact] [Fact]
public async Task GetKeysFromPrefixAsync_IndexPrefix_ReturnsMatchingItems() public void GetKeysFromPrefix_IndexPrefix_ReturnsMatchingItems()
{ {
// Arrange // Arrange
var expected = new Dictionary<string, string> var expected = new Dictionary<string, string>
@ -149,21 +150,21 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
var valueProvider = GetEnumerableValueProvider(BindingSource.Query, _backingStore, culture: null); var valueProvider = GetEnumerableValueProvider(BindingSource.Query, _backingStore, culture: null);
// Act // Act
var result = await valueProvider.GetKeysFromPrefixAsync("[index]"); var result = valueProvider.GetKeysFromPrefix("[index]");
// Assert // Assert
Assert.Equal(expected, result); Assert.Equal(expected, result);
} }
[Fact] [Fact]
public async Task GetValueAsync_SingleValue() public void GetValue_SingleValue()
{ {
// Arrange // Arrange
var culture = new CultureInfo("fr-FR"); var culture = new CultureInfo("fr-FR");
var valueProvider = GetEnumerableValueProvider(BindingSource.Query, _backingStore, culture); var valueProvider = GetEnumerableValueProvider(BindingSource.Query, _backingStore, culture);
// Act // Act
var result = await valueProvider.GetValueAsync("prefix.name"); var result = valueProvider.GetValue("prefix.name");
// Assert // Assert
Assert.NotNull(result); Assert.NotNull(result);
@ -172,14 +173,14 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
} }
[Fact] [Fact]
public async Task GetValueAsync_MultiValue() public void GetValue_MultiValue()
{ {
// Arrange // Arrange
var culture = new CultureInfo("fr-FR"); var culture = new CultureInfo("fr-FR");
var valueProvider = GetEnumerableValueProvider(BindingSource.Query, _backingStore, culture); var valueProvider = GetEnumerableValueProvider(BindingSource.Query, _backingStore, culture);
// Act // Act
var result = await valueProvider.GetValueAsync("some"); var result = valueProvider.GetValue("some");
// Assert // Assert
Assert.NotNull(result); Assert.NotNull(result);
@ -191,21 +192,21 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
[Theory] [Theory]
[InlineData("null_value")] [InlineData("null_value")]
[InlineData("prefix.null_value")] [InlineData("prefix.null_value")]
public async Task GetValue_NullValue(string key) public void GetValue_NullValue(string key)
{ {
// Arrange // Arrange
var culture = new CultureInfo("fr-FR"); var culture = new CultureInfo("fr-FR");
var valueProvider = GetEnumerableValueProvider(BindingSource.Query, _backingStore, culture); var valueProvider = GetEnumerableValueProvider(BindingSource.Query, _backingStore, culture);
// Act // Act
var result = await valueProvider.GetValueAsync(key); var result = valueProvider.GetValue(key);
// Assert // Assert
Assert.Equal(ValueProviderResult.None, result); Assert.Equal(ValueProviderResult.None, result);
} }
[Fact] [Fact]
public async Task GetValueAsync_NullMultipleValue() public void GetValue_NullMultipleValue()
{ {
// Arrange // Arrange
var backingStore = new Dictionary<string, string[]> var backingStore = new Dictionary<string, string[]>
@ -216,7 +217,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
var valueProvider = GetEnumerableValueProvider(BindingSource.Query, backingStore, culture); var valueProvider = GetEnumerableValueProvider(BindingSource.Query, backingStore, culture);
// Act // Act
var result = await valueProvider.GetValueAsync("key"); var result = valueProvider.GetValue("key");
// Assert // Assert
Assert.Equal(new[] { null, null, "value" }, result.Values); Assert.Equal(new[] { null, null, "value" }, result.Values);
@ -224,13 +225,13 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
} }
[Fact] [Fact]
public async Task GetValueAsync_ReturnsNullIfKeyNotFound() public void GetValue_ReturnsNullIfKeyNotFound()
{ {
// Arrange // Arrange
var valueProvider = GetEnumerableValueProvider(BindingSource.Query, _backingStore, culture: null); var valueProvider = GetEnumerableValueProvider(BindingSource.Query, _backingStore, culture: null);
// Act // Act
var result = await valueProvider.GetValueAsync("prefix"); var result = valueProvider.GetValue("prefix");
// Assert // Assert
Assert.Equal(ValueProviderResult.None, result); Assert.Equal(ValueProviderResult.None, result);

View File

@ -4,6 +4,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using System.Threading.Tasks;
using Microsoft.AspNet.Http.Internal; using Microsoft.AspNet.Http.Internal;
using Xunit; using Xunit;
@ -12,14 +13,14 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
public class FormValueProviderFactoryTest public class FormValueProviderFactoryTest
{ {
[Fact] [Fact]
public void GetValueProvider_ReturnsNull_WhenContentTypeIsNotFormUrlEncoded() public async Task GetValueProviderAsync_ReturnsNull_WhenContentTypeIsNotFormUrlEncoded()
{ {
// Arrange // Arrange
var context = CreateContext("some-content-type"); var context = CreateContext("some-content-type");
var factory = new FormValueProviderFactory(); var factory = new FormValueProviderFactory();
// Act // Act
var result = factory.GetValueProvider(context); var result = await factory.GetValueProviderAsync(context);
// Assert // Assert
Assert.Null(result); Assert.Null(result);
@ -30,14 +31,14 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
[InlineData("application/x-www-form-urlencoded;charset=utf-8")] [InlineData("application/x-www-form-urlencoded;charset=utf-8")]
[InlineData("multipart/form-data; boundary=----WebKitFormBoundarymx2fSWqWSd0OxQqq")] [InlineData("multipart/form-data; boundary=----WebKitFormBoundarymx2fSWqWSd0OxQqq")]
[InlineData("multipart/form-data; boundary=----WebKitFormBoundarymx2fSWqWSd0OxQqq; charset=utf-8")] [InlineData("multipart/form-data; boundary=----WebKitFormBoundarymx2fSWqWSd0OxQqq; charset=utf-8")]
public void GetValueProvider_ReturnsValueProviderInstanceWithCurrentCulture(string contentType) public async Task GetValueProviderAsync_ReturnsValueProvider_WithCurrentCulture(string contentType)
{ {
// Arrange // Arrange
var context = CreateContext(contentType); var context = CreateContext(contentType);
var factory = new FormValueProviderFactory(); var factory = new FormValueProviderFactory();
// Act // Act
var result = factory.GetValueProvider(context); var result = await factory.GetValueProviderAsync(context);
// Assert // Assert
var valueProvider = Assert.IsType<ReadableStringCollectionValueProvider>(result); var valueProvider = Assert.IsType<ReadableStringCollectionValueProvider>(result);
@ -49,6 +50,11 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
var context = new DefaultHttpContext(); var context = new DefaultHttpContext();
context.Request.ContentType = contentType; context.Request.ContentType = contentType;
if (context.Request.HasFormContentType)
{
context.Request.Form = new FormCollection(new Dictionary<string, string[]>());
}
return new ValueProviderFactoryContext( return new ValueProviderFactoryContext(
context, context,
new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase)); new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase));

View File

@ -37,14 +37,14 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
}; };
[Fact] [Fact]
public void GetValueProvider_ReturnsNull_WhenContentTypeIsNotFormUrlEncoded() public async Task GetValueProvider_ReturnsNull_WhenContentTypeIsNotFormUrlEncoded()
{ {
// Arrange // Arrange
var context = CreateContext("some-content-type", formValues: null); var context = CreateContext("some-content-type", formValues: null);
var factory = new JQueryFormValueProviderFactory(); var factory = new JQueryFormValueProviderFactory();
// Act // Act
var result = factory.GetValueProvider(context); var result = await factory.GetValueProviderAsync(context);
// Assert // Assert
Assert.Null(result); Assert.Null(result);
@ -55,14 +55,14 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
[InlineData("application/x-www-form-urlencoded;charset=utf-8")] [InlineData("application/x-www-form-urlencoded;charset=utf-8")]
[InlineData("multipart/form-data; boundary=----WebKitFormBoundarymx2fSWqWSd0OxQqq")] [InlineData("multipart/form-data; boundary=----WebKitFormBoundarymx2fSWqWSd0OxQqq")]
[InlineData("multipart/form-data; boundary=----WebKitFormBoundarymx2fSWqWSd0OxQqq; charset=utf-8")] [InlineData("multipart/form-data; boundary=----WebKitFormBoundarymx2fSWqWSd0OxQqq; charset=utf-8")]
public void GetValueProvider_ReturnsExpectedValueProviderInstanceWithCurrentCulture(string contentType) public async Task GetValueProviderAsync_ReturnsValueProvider_WithCurrentCulture(string contentType)
{ {
// Arrange // Arrange
var context = CreateContext(contentType, formValues: null); var context = CreateContext(contentType, formValues: null);
var factory = new JQueryFormValueProviderFactory(); var factory = new JQueryFormValueProviderFactory();
// Act // Act
var result = factory.GetValueProvider(context); var result = await factory.GetValueProviderAsync(context);
// Assert // Assert
var valueProvider = Assert.IsType<JQueryFormValueProvider>(result); var valueProvider = Assert.IsType<JQueryFormValueProvider>(result);
@ -106,8 +106,8 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
var factory = new JQueryFormValueProviderFactory(); var factory = new JQueryFormValueProviderFactory();
// Act // Act
var valueProvider = factory.GetValueProvider(context); var valueProvider = await factory.GetValueProviderAsync(context);
var result = await valueProvider.GetValueAsync(key); var result = valueProvider.GetValue(key);
// Assert // Assert
Assert.Equal("found", (string)result); Assert.Equal("found", (string)result);
@ -119,9 +119,10 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
{ {
var context = new DefaultHttpContext(); var context = new DefaultHttpContext();
context.Request.ContentType = contentType; context.Request.ContentType = contentType;
if (formValues != null)
if (context.Request.HasFormContentType)
{ {
context.Request.Form = new FormCollection(formValues); context.Request.Form = new FormCollection(formValues ?? new Dictionary<string, string[]>());
} }
return new ValueProviderFactoryContext( return new ValueProviderFactoryContext(

View File

@ -36,7 +36,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
[InlineData(true, false, "prefix", "dummyModelName", true)] [InlineData(true, false, "prefix", "dummyModelName", true)]
[InlineData(false, true, "prefix", "dummyModelName", false)] [InlineData(false, true, "prefix", "dummyModelName", false)]
[InlineData(false, false, "prefix", "dummyModelName", false)] [InlineData(false, false, "prefix", "dummyModelName", false)]
public async Task CanCreateModel_ReturnsTrue_IfIsTopLevelObjectAndNotIsFirstChanceBinding( public void CanCreateModel_ReturnsTrue_IfIsTopLevelObjectAndNotIsFirstChanceBinding(
bool isTopLevelObject, bool isTopLevelObject,
bool isFirstChanceBinding, bool isFirstChanceBinding,
string binderModelName, string binderModelName,
@ -45,8 +45,8 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
{ {
var mockValueProvider = new Mock<IValueProvider>(); var mockValueProvider = new Mock<IValueProvider>();
mockValueProvider mockValueProvider
.Setup(o => o.ContainsPrefixAsync(It.IsAny<string>())) .Setup(o => o.ContainsPrefix(It.IsAny<string>()))
.Returns(Task.FromResult(false)); .Returns(false);
var metadataProvider = new TestModelMetadataProvider(); var metadataProvider = new TestModelMetadataProvider();
var bindingContext = new MutableObjectBinderContext var bindingContext = new MutableObjectBinderContext
@ -77,14 +77,14 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
mutableBinder.GetMetadataForProperties(bindingContext.ModelBindingContext).ToArray(); mutableBinder.GetMetadataForProperties(bindingContext.ModelBindingContext).ToArray();
// Act // Act
var canCreate = await mutableBinder.CanCreateModel(bindingContext); var canCreate = mutableBinder.CanCreateModel(bindingContext);
// Assert // Assert
Assert.Equal(expectedCanCreate, canCreate); Assert.Equal(expectedCanCreate, canCreate);
} }
[Fact] [Fact]
public async Task CanCreateModel_ReturnsFalse_IfNotIsTopLevelObjectAndModelIsMarkedWithBinderMetadata() public void CanCreateModel_ReturnsFalse_IfNotIsTopLevelObjectAndModelIsMarkedWithBinderMetadata()
{ {
// Get the property metadata so that it is not a top level object. // Get the property metadata so that it is not a top level object.
var modelMetadata = GetMetadataForType(typeof(Document)) var modelMetadata = GetMetadataForType(typeof(Document))
@ -107,14 +107,14 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
var mutableBinder = new MutableObjectModelBinder(); var mutableBinder = new MutableObjectModelBinder();
// Act // Act
var canCreate = await mutableBinder.CanCreateModel(bindingContext); var canCreate = mutableBinder.CanCreateModel(bindingContext);
// Assert // Assert
Assert.False(canCreate); Assert.False(canCreate);
} }
[Fact] [Fact]
public async Task CanCreateModel_ReturnsTrue_IfIsTopLevelObjectAndModelIsMarkedWithBinderMetadata() public void CanCreateModel_ReturnsTrue_IfIsTopLevelObjectAndModelIsMarkedWithBinderMetadata()
{ {
var bindingContext = new MutableObjectBinderContext var bindingContext = new MutableObjectBinderContext
{ {
@ -134,18 +134,19 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
var mutableBinder = new MutableObjectModelBinder(); var mutableBinder = new MutableObjectModelBinder();
// Act // Act
var canCreate = await mutableBinder.CanCreateModel(bindingContext); var canCreate = mutableBinder.CanCreateModel(bindingContext);
// Assert // Assert
Assert.True(canCreate); Assert.True(canCreate);
} }
[Fact] [Fact]
public async Task CanCreateModel_CreatesModel_IfTheModelIsBinderPoco() public void CanCreateModel_CreatesModel_IfTheModelIsBinderPoco()
{ {
var mockValueProvider = new Mock<IValueProvider>(); var mockValueProvider = new Mock<IValueProvider>();
mockValueProvider.Setup(o => o.ContainsPrefixAsync(It.IsAny<string>())) mockValueProvider
.Returns(Task.FromResult(false)); .Setup(o => o.ContainsPrefix(It.IsAny<string>()))
.Returns(false);
var bindingContext = new MutableObjectBinderContext var bindingContext = new MutableObjectBinderContext
{ {
@ -170,23 +171,23 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
mutableBinder.GetMetadataForProperties(bindingContext.ModelBindingContext).ToArray(); mutableBinder.GetMetadataForProperties(bindingContext.ModelBindingContext).ToArray();
// Act // Act
var retModel = await mutableBinder.CanCreateModel(bindingContext); var canCreate = mutableBinder.CanCreateModel(bindingContext);
// Assert // Assert
Assert.True(retModel); Assert.True(canCreate);
} }
[Theory] [Theory]
[InlineData(true)] [InlineData(true)]
[InlineData(false)] [InlineData(false)]
public async Task CanCreateModel_ReturnsTrue_IfNotIsTopLevelObject_BasedOnValueAvailability( public void CanCreateModel_ReturnsTrue_IfNotIsTopLevelObject_BasedOnValueAvailability(
bool valueAvailable) bool valueAvailable)
{ {
// Arrange // Arrange
var mockValueProvider = new Mock<IValueProvider>(MockBehavior.Strict); var mockValueProvider = new Mock<IValueProvider>(MockBehavior.Strict);
mockValueProvider mockValueProvider
.Setup(provider => provider.ContainsPrefixAsync("SimpleContainer.Simple.Name")) .Setup(provider => provider.ContainsPrefix("SimpleContainer.Simple.Name"))
.Returns(Task.FromResult(valueAvailable)); .Returns(valueAvailable);
var typeMetadata = GetMetadataForType(typeof(SimpleContainer)); var typeMetadata = GetMetadataForType(typeof(SimpleContainer));
var modelMetadata = typeMetadata.Properties[nameof(SimpleContainer.Simple)]; var modelMetadata = typeMetadata.Properties[nameof(SimpleContainer.Simple)];
@ -210,15 +211,15 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
var mutableBinder = new MutableObjectModelBinder(); var mutableBinder = new MutableObjectModelBinder();
// Act // Act
var result = await mutableBinder.CanCreateModel(bindingContext); var canCreate = mutableBinder.CanCreateModel(bindingContext);
// Assert // Assert
// Result matches whether first Simple property can bind. // Result matches whether first Simple property can bind.
Assert.Equal(valueAvailable, result); Assert.Equal(valueAvailable, canCreate);
} }
[Fact] [Fact]
public async Task CanCreateModel_ReturnsFalse_IfNotIsTopLevelObjectAndModelHasNoProperties() public void CanCreateModel_ReturnsFalse_IfNotIsTopLevelObjectAndModelHasNoProperties()
{ {
// Arrange // Arrange
var bindingContext = new MutableObjectBinderContext var bindingContext = new MutableObjectBinderContext
@ -236,14 +237,14 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
mutableBinder.GetMetadataForProperties(bindingContext.ModelBindingContext).ToArray(); mutableBinder.GetMetadataForProperties(bindingContext.ModelBindingContext).ToArray();
// Act // Act
var canCreate = await mutableBinder.CanCreateModel(bindingContext); var canCreate = mutableBinder.CanCreateModel(bindingContext);
// Assert // Assert
Assert.False(canCreate); Assert.False(canCreate);
} }
[Fact] [Fact]
public async Task CanCreateModel_ReturnsTrue_IfIsTopLevelObjectAndModelHasNoProperties() public void CanCreateModel_ReturnsTrue_IfIsTopLevelObjectAndModelHasNoProperties()
{ {
// Arrange // Arrange
var bindingContext = new MutableObjectBinderContext var bindingContext = new MutableObjectBinderContext
@ -260,10 +261,10 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
mutableBinder.GetMetadataForProperties(bindingContext.ModelBindingContext).ToArray(); mutableBinder.GetMetadataForProperties(bindingContext.ModelBindingContext).ToArray();
// Act // Act
var retModel = await mutableBinder.CanCreateModel(bindingContext); var canCreate = mutableBinder.CanCreateModel(bindingContext);
// Assert // Assert
Assert.True(retModel); Assert.True(canCreate);
} }
[Theory] [Theory]
@ -273,13 +274,13 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
[InlineData(typeof(TypeWithAtLeastOnePropertyMarkedUsingValueBinderMetadata), true)] [InlineData(typeof(TypeWithAtLeastOnePropertyMarkedUsingValueBinderMetadata), true)]
[InlineData(typeof(TypeWithUnmarkedAndBinderMetadataMarkedProperties), false)] [InlineData(typeof(TypeWithUnmarkedAndBinderMetadataMarkedProperties), false)]
[InlineData(typeof(TypeWithUnmarkedAndBinderMetadataMarkedProperties), true)] [InlineData(typeof(TypeWithUnmarkedAndBinderMetadataMarkedProperties), true)]
public async Task public void CanCreateModel_CreatesModelForValueProviderBasedBinderMetadatas_IfAValueProviderProvidesValue(
CanCreateModel_CreatesModelForValueProviderBasedBinderMetadatas_IfAValueProviderProvidesValue Type modelType,
(Type modelType, bool valueProviderProvidesValue) bool valueProviderProvidesValue)
{ {
var mockValueProvider = new Mock<IValueProvider>(); var mockValueProvider = new Mock<IValueProvider>();
mockValueProvider.Setup(o => o.ContainsPrefixAsync(It.IsAny<string>())) mockValueProvider.Setup(o => o.ContainsPrefix(It.IsAny<string>()))
.Returns(Task.FromResult(valueProviderProvidesValue)); .Returns(valueProviderProvidesValue);
var bindingContext = new MutableObjectBinderContext var bindingContext = new MutableObjectBinderContext
{ {
@ -303,27 +304,28 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
mutableBinder.GetMetadataForProperties(bindingContext.ModelBindingContext).ToArray(); mutableBinder.GetMetadataForProperties(bindingContext.ModelBindingContext).ToArray();
// Act // Act
var retModel = await mutableBinder.CanCreateModel(bindingContext); var canCreate = mutableBinder.CanCreateModel(bindingContext);
// Assert // Assert
Assert.Equal(valueProviderProvidesValue, retModel); Assert.Equal(valueProviderProvidesValue, canCreate);
} }
[Theory] [Theory]
[InlineData(typeof(TypeWithAtLeastOnePropertyMarkedUsingValueBinderMetadata), false)] [InlineData(typeof(TypeWithAtLeastOnePropertyMarkedUsingValueBinderMetadata), false)]
[InlineData(typeof(TypeWithAtLeastOnePropertyMarkedUsingValueBinderMetadata), true)] [InlineData(typeof(TypeWithAtLeastOnePropertyMarkedUsingValueBinderMetadata), true)]
public async Task CanCreateModel_ForExplicitValueProviderMetadata_UsesOriginalValueProvider( public void CanCreateModel_ForExplicitValueProviderMetadata_UsesOriginalValueProvider(
Type modelType, Type modelType,
bool originalValueProviderProvidesValue) bool originalValueProviderProvidesValue)
{ {
var mockValueProvider = new Mock<IValueProvider>(); var mockValueProvider = new Mock<IValueProvider>();
mockValueProvider.Setup(o => o.ContainsPrefixAsync(It.IsAny<string>())) mockValueProvider
.Returns(Task.FromResult(false)); .Setup(o => o.ContainsPrefix(It.IsAny<string>()))
.Returns(false);
var mockOriginalValueProvider = new Mock<IBindingSourceValueProvider>(); var mockOriginalValueProvider = new Mock<IBindingSourceValueProvider>();
mockOriginalValueProvider mockOriginalValueProvider
.Setup(o => o.ContainsPrefixAsync(It.IsAny<string>())) .Setup(o => o.ContainsPrefix(It.IsAny<string>()))
.Returns(Task.FromResult(originalValueProviderProvidesValue)); .Returns(originalValueProviderProvidesValue);
mockOriginalValueProvider mockOriginalValueProvider
.Setup(o => o.Filter(It.IsAny<BindingSource>())) .Setup(o => o.Filter(It.IsAny<BindingSource>()))
@ -363,10 +365,10 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
mutableBinder.GetMetadataForProperties(bindingContext.ModelBindingContext).ToArray(); mutableBinder.GetMetadataForProperties(bindingContext.ModelBindingContext).ToArray();
// Act // Act
var retModel = await mutableBinder.CanCreateModel(bindingContext); var canCreate = mutableBinder.CanCreateModel(bindingContext);
// Assert // Assert
Assert.Equal(originalValueProviderProvidesValue, retModel); Assert.Equal(originalValueProviderProvidesValue, canCreate);
} }
[Theory] [Theory]
@ -374,17 +376,17 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
[InlineData(typeof(TypeWithUnmarkedAndBinderMetadataMarkedProperties), true)] [InlineData(typeof(TypeWithUnmarkedAndBinderMetadataMarkedProperties), true)]
[InlineData(typeof(TypeWithNoBinderMetadata), false)] [InlineData(typeof(TypeWithNoBinderMetadata), false)]
[InlineData(typeof(TypeWithNoBinderMetadata), true)] [InlineData(typeof(TypeWithNoBinderMetadata), true)]
public async Task CanCreateModel_UnmarkedProperties_UsesCurrentValueProvider( public void CanCreateModel_UnmarkedProperties_UsesCurrentValueProvider(
Type modelType, Type modelType,
bool valueProviderProvidesValue) bool valueProviderProvidesValue)
{ {
var mockValueProvider = new Mock<IValueProvider>(); var mockValueProvider = new Mock<IValueProvider>();
mockValueProvider.Setup(o => o.ContainsPrefixAsync(It.IsAny<string>())) mockValueProvider.Setup(o => o.ContainsPrefix(It.IsAny<string>()))
.Returns(Task.FromResult(valueProviderProvidesValue)); .Returns(valueProviderProvidesValue);
var mockOriginalValueProvider = new Mock<IValueProvider>(); var mockOriginalValueProvider = new Mock<IValueProvider>();
mockOriginalValueProvider.Setup(o => o.ContainsPrefixAsync(It.IsAny<string>())) mockOriginalValueProvider.Setup(o => o.ContainsPrefix(It.IsAny<string>()))
.Returns(Task.FromResult(false)); .Returns(false);
var bindingContext = new MutableObjectBinderContext var bindingContext = new MutableObjectBinderContext
{ {
@ -408,10 +410,10 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
mutableBinder.GetMetadataForProperties(bindingContext.ModelBindingContext).ToArray(); mutableBinder.GetMetadataForProperties(bindingContext.ModelBindingContext).ToArray();
// Act // Act
var retModel = await mutableBinder.CanCreateModel(bindingContext); var canCreate = mutableBinder.CanCreateModel(bindingContext);
// Assert // Assert
Assert.Equal(valueProviderProvidesValue, retModel); Assert.Equal(valueProviderProvidesValue, canCreate);
} }
[Fact] [Fact]
@ -420,8 +422,8 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
// Arrange // Arrange
var mockValueProvider = new Mock<IValueProvider>(); var mockValueProvider = new Mock<IValueProvider>();
mockValueProvider mockValueProvider
.Setup(o => o.ContainsPrefixAsync(It.IsAny<string>())) .Setup(o => o.ContainsPrefix(It.IsAny<string>()))
.Returns(Task.FromResult(true)); .Returns(true);
// Mock binder fails to bind all properties. // Mock binder fails to bind all properties.
var mockBinder = new Mock<IModelBinder>(); var mockBinder = new Mock<IModelBinder>();
@ -470,8 +472,8 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
// Arrange // Arrange
var mockValueProvider = new Mock<IValueProvider>(); var mockValueProvider = new Mock<IValueProvider>();
mockValueProvider mockValueProvider
.Setup(o => o.ContainsPrefixAsync(It.IsAny<string>())) .Setup(o => o.ContainsPrefix(It.IsAny<string>()))
.Returns(Task.FromResult(false)); .Returns(false);
// Mock binder fails to bind all properties. // Mock binder fails to bind all properties.
var mockBinder = new Mock<IModelBinder>(); var mockBinder = new Mock<IModelBinder>();
@ -567,10 +569,10 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
var testableBinder = new TestableMutableObjectModelBinder(); var testableBinder = new TestableMutableObjectModelBinder();
// Act // Act
var retModel = testableBinder.CreateModelPublic(bindingContext); var model = testableBinder.CreateModelPublic(bindingContext);
// Assert // Assert
Assert.IsType<Person>(retModel); Assert.IsType<Person>(model);
} }
[Fact] [Fact]

View File

@ -5,6 +5,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using System.Threading.Tasks;
using Microsoft.AspNet.Http; using Microsoft.AspNet.Http;
using Moq; using Moq;
using Xunit; using Xunit;
@ -18,7 +19,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
#if DNX451 #if DNX451
[Fact] [Fact]
public void GetValueProvider_ReturnsQueryStringValueProviderInstanceWithInvariantCulture() public async Task GetValueProvider_ReturnsQueryStringValueProviderInstanceWithInvariantCulture()
{ {
// Arrange // Arrange
var request = new Mock<HttpRequest>(); var request = new Mock<HttpRequest>();
@ -31,7 +32,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase)); new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase));
// Act // Act
var result = _factory.GetValueProvider(factoryContext); var result = await _factory.GetValueProviderAsync(factoryContext);
// Assert // Assert
var valueProvider = Assert.IsType<ReadableStringCollectionValueProvider>(result); var valueProvider = Assert.IsType<ReadableStringCollectionValueProvider>(result);

View File

@ -23,44 +23,21 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
_culture = culture ?? CultureInfo.InvariantCulture; _culture = culture ?? CultureInfo.InvariantCulture;
} }
// copied from ValueProviderUtil public bool ContainsPrefix(string prefix)
public Task<bool> ContainsPrefixAsync(string prefix)
{ {
foreach (string key in Keys) foreach (string key in Keys)
{ {
if (key != null) if (PrefixContainer.IsPrefixMatch(prefix, key))
{ {
if (prefix.Length == 0) return true;
{
return Task.FromResult(true); // shortcut - non-null key matches empty prefix
}
if (key.StartsWith(prefix, StringComparison.OrdinalIgnoreCase))
{
if (key.Length == prefix.Length)
{
return Task.FromResult(true); // exact match
}
else
{
switch (key[prefix.Length])
{
case '.': // known separator characters
case '[':
return Task.FromResult(true);
}
}
}
} }
} }
return Task.FromResult(false); // nothing found return false;
} }
public Task<ValueProviderResult> GetValueAsync(string key) public ValueProviderResult GetValue(string key)
{ {
ValueProviderResult result = ValueProviderResult.None;
object rawValue; object rawValue;
if (TryGetValue(key, out rawValue)) if (TryGetValue(key, out rawValue))
{ {
@ -74,16 +51,16 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
stringValues[i] = array.GetValue(i) as string ?? Convert.ToString(array.GetValue(i), _culture); stringValues[i] = array.GetValue(i) as string ?? Convert.ToString(array.GetValue(i), _culture);
} }
result = new ValueProviderResult(stringValues, _culture); return new ValueProviderResult(stringValues, _culture);
} }
else else
{ {
var stringValue = rawValue as string ?? Convert.ToString(rawValue, _culture) ?? string.Empty; var stringValue = rawValue as string ?? Convert.ToString(rawValue, _culture) ?? string.Empty;
result = new ValueProviderResult(stringValue, _culture); return new ValueProviderResult(stringValue, _culture);
} }
} }
return Task.FromResult(result); return ValueProviderResult.None;
} }
} }
} }

View File

@ -97,9 +97,9 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
actionContext.HttpContext, actionContext.HttpContext,
actionContext.RouteData.Values); actionContext.RouteData.Values);
var valueProvider = CompositeValueProvider.Create( var valueProvider = CompositeValueProvider.CreateAsync(
options.ValueProviderFactories, options.ValueProviderFactories,
valueProviderFactoryContext); valueProviderFactoryContext).Result;
return new ActionBindingContext() return new ActionBindingContext()
{ {

View File

@ -85,7 +85,7 @@ namespace ModelBindingWebSite.Controllers
private class ProductModelBinder : IModelBinder private class ProductModelBinder : IModelBinder
{ {
public async Task<ModelBindingResult> BindModelAsync(ModelBindingContext bindingContext) public Task<ModelBindingResult> BindModelAsync(ModelBindingContext bindingContext)
{ {
if (typeof(Product).IsAssignableFrom(bindingContext.ModelType)) if (typeof(Product).IsAssignableFrom(bindingContext.ModelType))
{ {
@ -97,16 +97,16 @@ namespace ModelBindingWebSite.Controllers
string.IsNullOrEmpty(bindingContext.ModelName) ? string.IsNullOrEmpty(bindingContext.ModelName) ?
"productId" : "productId" :
bindingContext.ModelName + "." + "productId"; bindingContext.ModelName + "." + "productId";
var value = await bindingContext.ValueProvider.GetValueAsync(key); var value = bindingContext.ValueProvider.GetValue(key);
model.ProductId = value.ConvertTo<int>(); model.ProductId = value.ConvertTo<int>();
var validationNode = var validationNode =
new ModelValidationNode(bindingContext.ModelName, bindingContext.ModelMetadata, value); new ModelValidationNode(bindingContext.ModelName, bindingContext.ModelMetadata, value);
return new ModelBindingResult(model, key, true, validationNode); return Task.FromResult(new ModelBindingResult(model, key, true, validationNode));
} }
return null; return Task.FromResult<ModelBindingResult>(null);
} }
} }
} }

View File

@ -211,14 +211,14 @@ namespace ModelBindingWebSite.Controllers
public class CustomValueProvider : IValueProvider public class CustomValueProvider : IValueProvider
{ {
public Task<bool> ContainsPrefixAsync(string prefix) public bool ContainsPrefix(string prefix)
{ {
return Task.FromResult(false); return false;
} }
public Task<ValueProviderResult> GetValueAsync(string key) public ValueProviderResult GetValue(string key)
{ {
return Task.FromResult<ValueProviderResult>(ValueProviderResult.None); return ValueProviderResult.None;
} }
} }
} }

View File

@ -10,29 +10,27 @@ namespace ValueProvidersWebSite
{ {
public class CustomValueProviderFactory : IValueProviderFactory public class CustomValueProviderFactory : IValueProviderFactory
{ {
public IValueProvider GetValueProvider(ValueProviderFactoryContext context) public Task<IValueProvider> GetValueProviderAsync(ValueProviderFactoryContext context)
{ {
if (context.HttpContext.Request.Path.Value.Contains("TestValueProvider")) if (context.HttpContext.Request.Path.Value.Contains("TestValueProvider"))
{ {
return new CustomValueProvider(); return Task.FromResult<IValueProvider>(new CustomValueProvider());
} }
return null; return Task.FromResult<IValueProvider>(null);
} }
private class CustomValueProvider : IValueProvider private class CustomValueProvider : IValueProvider
{ {
public Task<bool> ContainsPrefixAsync(string prefix) public bool ContainsPrefix(string prefix)
{ {
var result = string.Equals(prefix, "test", StringComparison.OrdinalIgnoreCase); return string.Equals(prefix, "test", StringComparison.OrdinalIgnoreCase);
return Task.FromResult(result);
} }
public Task<ValueProviderResult> GetValueAsync(string key) public ValueProviderResult GetValue(string key)
{ {
var value = "custom-value-provider-value"; var value = "custom-value-provider-value";
var result = new ValueProviderResult(value, CultureInfo.CurrentCulture); return new ValueProviderResult(value, CultureInfo.CurrentCulture);
return Task.FromResult(result);
} }
} }
} }