Adding a context class for ValueProviderFactories

This allows model binding to once again be independent of routing. Sending
RouteContext into model binding was an odd choice from a layering
point-of-view.
This commit is contained in:
Ryan Nowak 2014-06-11 17:15:33 -07:00
parent 44ed23f65e
commit 7f34c94de7
10 changed files with 48 additions and 30 deletions

View File

@ -33,9 +33,11 @@ namespace Microsoft.AspNet.Mvc
public Task<ActionBindingContext> GetActionBindingContextAsync(ActionContext actionContext)
{
var routeContext = new RouteContext(actionContext.HttpContext);
routeContext.RouteData = actionContext.RouteData;
var valueProviders = _valueProviderFactories.Select(factory => factory.GetValueProvider(routeContext))
var factoryContext = new ValueProviderFactoryContext(
actionContext.HttpContext,
actionContext.RouteData.Values);
var valueProviders = _valueProviderFactories.Select(factory => factory.GetValueProvider(factoryContext))
.Where(vp => vp != null);
var context = new ActionBindingContext(
actionContext,

View File

@ -115,6 +115,7 @@
<Compile Include="ValueProviders\QueryStringValueProviderFactory.cs" />
<Compile Include="ValueProviders\ReadableStringCollectionValueProvider.cs" />
<Compile Include="ValueProviders\RouteValueValueProviderFactory.cs" />
<Compile Include="ValueProviders\ValueProviderFactoryContext.cs" />
<Compile Include="ValueProviders\ValueProviderResult.cs" />
</ItemGroup>
<Import Project="$(VSToolsPath)\AspNet\Microsoft.Web.AspNet.targets" Condition="'$(VSToolsPath)' != ''" />

View File

@ -4,7 +4,6 @@
using System;
using System.Globalization;
using Microsoft.AspNet.Http;
using Microsoft.AspNet.Routing;
namespace Microsoft.AspNet.Mvc.ModelBinding
{
@ -12,9 +11,9 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
{
private const string FormEncodedContentType = "application/x-www-form-urlencoded";
public IValueProvider GetValueProvider([NotNull] RouteContext routeContext)
public IValueProvider GetValueProvider([NotNull] ValueProviderFactoryContext context)
{
var request = routeContext.HttpContext.Request;
var request = context.HttpContext.Request;
if (IsSupportedContentType(request))
{

View File

@ -1,8 +1,6 @@
// 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 Microsoft.AspNet.Routing;
namespace Microsoft.AspNet.Mvc.ModelBinding
{
public interface IValueProviderFactory
@ -10,8 +8,8 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
/// <summary>
/// Get a value provider with values from the given <paramref name="requestContext"/>.
/// </summary>
/// <param name="routeContext">RouteContext that value provider will populate from</param>
/// <param name="context">ValueProviderFactoryContext that value provider will populate from</param>
/// <returns>a value provider instance or null</returns>
IValueProvider GetValueProvider([NotNull] RouteContext routeContext);
IValueProvider GetValueProvider([NotNull] ValueProviderFactoryContext context);
}
}

View File

@ -2,7 +2,6 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Globalization;
using Microsoft.AspNet.Routing;
namespace Microsoft.AspNet.Mvc.ModelBinding
{
@ -10,15 +9,15 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
{
private static readonly object _cacheKey = new object();
public IValueProvider GetValueProvider([NotNull] RouteContext routeContext)
public IValueProvider GetValueProvider([NotNull] ValueProviderFactoryContext context)
{
// Process the query collection once-per request.
var storage = routeContext.HttpContext.Items;
var storage = context.HttpContext.Items;
object value;
IValueProvider provider;
if (!storage.TryGetValue(_cacheKey, out value))
{
var queryCollection = routeContext.HttpContext.Request.Query;
var queryCollection = context.HttpContext.Request.Query;
provider = new ReadableStringCollectionValueProvider(queryCollection, CultureInfo.InvariantCulture);
storage[_cacheKey] = provider;
}

View File

@ -1,15 +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 Microsoft.AspNet.Routing;
namespace Microsoft.AspNet.Mvc.ModelBinding
{
public class RouteValueValueProviderFactory : IValueProviderFactory
{
public IValueProvider GetValueProvider([NotNull] RouteContext routeContext)
public IValueProvider GetValueProvider([NotNull] ValueProviderFactoryContext context)
{
return new DictionaryBasedValueProvider(routeContext.RouteData.Values);
return new DictionaryBasedValueProvider(context.RouteValues);
}
}
}

View File

@ -0,0 +1,23 @@
// 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.Collections.Generic;
using Microsoft.AspNet.Http;
namespace Microsoft.AspNet.Mvc.ModelBinding
{
public class ValueProviderFactoryContext
{
public ValueProviderFactoryContext(
[NotNull] HttpContext httpContext,
[NotNull] IDictionary<string, object> routeValues)
{
HttpContext = httpContext;
RouteValues = routeValues;
}
public HttpContext HttpContext { get; private set; }
public IDictionary<string, object> RouteValues { get; private set; }
}
}

View File

@ -6,7 +6,6 @@
"dependencies": {
"Microsoft.AspNet.Http": "0.1-alpha-*",
"Microsoft.AspNet.Mvc.Common": "",
"Microsoft.AspNet.Routing": "0.1-alpha-*",
"Microsoft.DataAnnotations": "0.1-alpha-*",
"Microsoft.Framework.DependencyInjection": "0.1-alpha-*",
"Newtonsoft.Json": "5.0.8"

View File

@ -19,11 +19,11 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
public void GetValueProvider_ReturnsNull_WhenContentTypeIsNotFormUrlEncoded()
{
// Arrange
var requestContext = CreateRequestContext("some-content-type");
var context = CreateContext("some-content-type");
var factory = new FormValueProviderFactory();
// Act
var result = factory.GetValueProvider(requestContext);
var result = factory.GetValueProvider(context);
// Assert
Assert.Null(result);
@ -35,18 +35,18 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
public void GetValueProvider_ReturnsValueProviderInstaceWithInvariantCulture(string contentType)
{
// Arrange
var requestContext = CreateRequestContext(contentType);
var context = CreateContext(contentType);
var factory = new FormValueProviderFactory();
// Act
var result = factory.GetValueProvider(requestContext);
var result = factory.GetValueProvider(context);
// Assert
var valueProvider = Assert.IsType<ReadableStringCollectionValueProvider>(result);
Assert.Equal(CultureInfo.CurrentCulture, valueProvider.Culture);
}
private static RouteContext CreateRequestContext(string contentType)
private static ValueProviderFactoryContext CreateContext(string contentType)
{
var collection = Mock.Of<IReadableStringCollection>();
var request = new Mock<HttpRequest>();
@ -59,9 +59,9 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
var context = new Mock<HttpContext>();
context.SetupGet(c => c.Request).Returns(request.Object);
var routeContext = new RouteContext(context.Object);
routeContext.RouteData.Values = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
return routeContext;
return new ValueProviderFactoryContext(
context.Object,
new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase));
}
}
}

View File

@ -27,11 +27,10 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
var context = new Mock<HttpContext>();
context.SetupGet(c => c.Items).Returns(new Dictionary<object, object>());
context.SetupGet(c => c.Request).Returns(request.Object);
var routeContext = new RouteContext(context.Object);
routeContext.RouteData.Values = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
var factoryContext = new ValueProviderFactoryContext(context.Object, new Dictionary<String, object>(StringComparer.OrdinalIgnoreCase));
// Act
var result = _factory.GetValueProvider(routeContext);
var result = _factory.GetValueProvider(factoryContext);
// Assert
var valueProvider = Assert.IsType<ReadableStringCollectionValueProvider>(result);