Consume ModelBinding from Mvc

* Modify ReflectedActionInvoker to model bind parameters
* Introduce IBodyReader for reading request bodies
* Introduce types for per-action-context specific binders, value providers
  and body readers
This commit is contained in:
Pranav K 2014-02-24 19:21:37 -08:00
parent 9dc79df9cf
commit e87f8c372c
20 changed files with 376 additions and 56 deletions

View File

@ -10,6 +10,22 @@ namespace MvcSample
return View("MyView", User()); return View("MyView", User());
} }
/// <summary>
/// Action that exercises query\form based model binding.
/// </summary>
public IActionResult SaveUser(User user)
{
return View("MyView", user);
}
/// <summary>
/// Action that exercises input formatter
/// </summary>
public IActionResult Post([FromBody]User user)
{
return View("MyView", user);
}
public IActionResult Something() public IActionResult Something()
{ {
return new ContentResult return new ContentResult

View File

@ -40,14 +40,16 @@ namespace MvcSample
var endpoint = ActivatorUtilities.CreateInstance<RouteEndpoint>(mvcServices.Services); var endpoint = ActivatorUtilities.CreateInstance<RouteEndpoint>(mvcServices.Services);
router.Add(new TemplateRoute(
endpoint,
"{controller}/{action}",
new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase) { { "controller", "Home" }, { "action", "Index" } }));
router.Add(new TemplateRoute( router.Add(new TemplateRoute(
endpoint, endpoint,
"{controller}", "{controller}",
new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase) { { "controller", "Home" } })); new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase) { { "controller", "Home" } }));
router.Add(new TemplateRoute(
endpoint,
"{controller}/{action}",
new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase) { { "controller", "Home" }, { "action", "Index" } }));
} }
} }
} }

View File

@ -7,13 +7,16 @@ namespace Microsoft.AspNet.Mvc
private readonly IActionResultFactory _actionResultFactory; private readonly IActionResultFactory _actionResultFactory;
private readonly IServiceProvider _serviceProvider; private readonly IServiceProvider _serviceProvider;
private readonly IControllerFactory _controllerFactory; private readonly IControllerFactory _controllerFactory;
private readonly IActionBindingContextProvider _bindingProvider;
public ActionInvokerProvider(IActionResultFactory actionResultFactory, public ActionInvokerProvider(IActionResultFactory actionResultFactory,
IControllerFactory controllerFactory, IControllerFactory controllerFactory,
IActionBindingContextProvider bindingProvider,
IServiceProvider serviceProvider) IServiceProvider serviceProvider)
{ {
_actionResultFactory = actionResultFactory; _actionResultFactory = actionResultFactory;
_controllerFactory = controllerFactory; _controllerFactory = controllerFactory;
_bindingProvider = bindingProvider;
_serviceProvider = serviceProvider; _serviceProvider = serviceProvider;
} }
@ -24,21 +27,20 @@ namespace Microsoft.AspNet.Mvc
public void Invoke(ActionInvokerProviderContext context, Action callNext) public void Invoke(ActionInvokerProviderContext context, Action callNext)
{ {
var ad = context.ActionContext.ActionDescriptor as ReflectedActionDescriptor; var actionDescriptor = context.ActionContext.ActionDescriptor as ReflectedActionDescriptor;
if (ad != null) if (actionDescriptor != null)
{ {
context.Result = new ReflectedActionInvoker( context.Result = new ReflectedActionInvoker(
context.ActionContext, context.ActionContext,
ad, actionDescriptor,
_actionResultFactory, _actionResultFactory,
_controllerFactory, _controllerFactory,
_serviceProvider); _bindingProvider,
_serviceProvider);
} }
callNext(); callNext();
} }
} }
} }

View File

@ -0,0 +1,15 @@
using System;
namespace Microsoft.AspNet.Mvc
{
public class BodyParameterInfo
{
public BodyParameterInfo(Type parameterType)
{
ParameterType = parameterType;
}
public Type ParameterType { get; private set; }
}
}

View File

@ -3,21 +3,19 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNet.DependencyInjection; using Microsoft.AspNet.DependencyInjection;
using Microsoft.AspNet.Mvc.ModelBinding;
namespace Microsoft.AspNet.Mvc namespace Microsoft.AspNet.Mvc
{ {
public class DefaultActionSelector : IActionSelector public class DefaultActionSelector : IActionSelector
{ {
private readonly INestedProviderManager<ActionDescriptorProviderContext> _actionDescriptorProvider; private readonly INestedProviderManager<ActionDescriptorProviderContext> _actionDescriptorProvider;
private readonly IEnumerable<IValueProviderFactory> _valueProviderFactory; private readonly IActionBindingContextProvider _bindingProvider;
public DefaultActionSelector( public DefaultActionSelector(INestedProviderManager<ActionDescriptorProviderContext> actionDescriptorProvider,
INestedProviderManager<ActionDescriptorProviderContext> actionDescriptorProvider, IActionBindingContextProvider bindingProvider)
IEnumerable<IValueProviderFactory> valueProviderFactories)
{ {
_actionDescriptorProvider = actionDescriptorProvider; _actionDescriptorProvider = actionDescriptorProvider;
_valueProviderFactory = valueProviderFactories; _bindingProvider = bindingProvider;
} }
public async Task<ActionDescriptor> SelectAsync(RequestContext context) public async Task<ActionDescriptor> SelectAsync(RequestContext context)
@ -61,9 +59,6 @@ namespace Microsoft.AspNet.Mvc
protected virtual async Task<ActionDescriptor> SelectBestCandidate(RequestContext context, List<ActionDescriptor> candidates) protected virtual async Task<ActionDescriptor> SelectBestCandidate(RequestContext context, List<ActionDescriptor> candidates)
{ {
var valueProviders = await Task.WhenAll(_valueProviderFactory.Select(vpf => vpf.GetValueProviderAsync(context)));
valueProviders = valueProviders.Where(vp => vp != null).ToArray();
var applicableCandiates = new List<ActionDescriptorCandidate>(); var applicableCandiates = new List<ActionDescriptorCandidate>();
foreach (var action in candidates) foreach (var action in candidates)
{ {
@ -72,18 +67,20 @@ namespace Microsoft.AspNet.Mvc
{ {
Action = action, Action = action,
}; };
var actionContext = new ActionContext(context.HttpContext, context.RouteValues, action);
var actionBindingContext = await _bindingProvider.GetActionBindingContextAsync(actionContext);
foreach (var parameter in action.Parameters.Where(p => !p.Binding.IsFromBody)) foreach (var parameter in action.Parameters.Where(p => p.ParameterBindingInfo != null))
{ {
if (valueProviders.Any(vp => vp.ContainsPrefix(parameter.Binding.Prefix))) if (actionBindingContext.ValueProvider.ContainsPrefix(parameter.ParameterBindingInfo.Prefix))
{ {
candidate.FoundParameters++; candidate.FoundParameters++;
if (parameter.Binding.IsOptional) if (parameter.IsOptional)
{ {
candidate.FoundOptionalParameters++; candidate.FoundOptionalParameters++;
} }
} }
else if (!parameter.Binding.IsOptional) else if (!parameter.IsOptional)
{ {
isApplicable = false; isApplicable = false;
break; break;

View File

@ -1,5 +1,4 @@
 using System.Reflection;
using System.Reflection;
namespace Microsoft.AspNet.Mvc namespace Microsoft.AspNet.Mvc
{ {
@ -7,24 +6,29 @@ namespace Microsoft.AspNet.Mvc
{ {
public ParameterDescriptor GetDescriptor(ParameterInfo parameter) public ParameterDescriptor GetDescriptor(ParameterInfo parameter)
{ {
var bindingInfo = new ParameterBindingInfo() bool isFromBody = IsFromBody(parameter);
{
IsOptional = parameter.IsOptional,
IsFromBody = IsFromBody(parameter),
Prefix = parameter.Name,
};
return new ParameterDescriptor() return new ParameterDescriptor
{ {
Name = parameter.Name, Name = parameter.Name,
Binding = bindingInfo, IsOptional = parameter.IsOptional,
ParameterBindingInfo = isFromBody ? null : GetParameterBindingInfo(parameter),
BodyParameterInfo = isFromBody ? GetBodyParameterInfo(parameter) : null
}; };
} }
public virtual bool IsFromBody(ParameterInfo parameter) public virtual bool IsFromBody(ParameterInfo parameter)
{ {
// Assume for now everything is read from value providers return parameter.GetCustomAttribute<FromBodyAttribute>() != null;
return false; }
private ParameterBindingInfo GetParameterBindingInfo(ParameterInfo parameter)
{
return new ParameterBindingInfo(parameter.Name, parameter.ParameterType);
}
private BodyParameterInfo GetBodyParameterInfo(ParameterInfo parameter)
{
return new BodyParameterInfo(parameter.ParameterType);
} }
} }
} }

View File

@ -0,0 +1,38 @@
using Microsoft.AspNet.Mvc.ModelBinding;
namespace Microsoft.AspNet.Mvc.Internal
{
public static class ActionBindingContextExtensions
{
public static InputFormatterContext CreateInputFormatterContext(this ActionBindingContext actionBindingContext,
ModelStateDictionary modelState,
ParameterDescriptor parameter)
{
var metadataProvider = actionBindingContext.MetadataProvider;
var parameterType = parameter.BodyParameterInfo.ParameterType;
var modelMetadata = metadataProvider.GetMetadataForType(modelAccessor: null, modelType: parameterType);
return new InputFormatterContext(modelMetadata, modelState);
}
public static ModelBindingContext CreateModelBindingContext(this ActionBindingContext actionBindingContext,
ModelStateDictionary modelState,
ParameterDescriptor parameter)
{
var metadataProvider = actionBindingContext.MetadataProvider;
var parameterType = parameter.ParameterBindingInfo.ParameterType;
var modelMetadata = metadataProvider.GetMetadataForType(modelAccessor: null, modelType: parameterType);
return new ModelBindingContext
{
ModelName = parameter.Name,
ModelState = modelState,
ModelMetadata = modelMetadata,
ModelBinder = actionBindingContext.ModelBinder,
ValueProvider = actionBindingContext.ValueProvider,
MetadataProvider = metadataProvider,
HttpContext = actionBindingContext.ActionContext.HttpContext,
FallbackToEmptyPrefix = true
};
}
}
}

View File

@ -0,0 +1,30 @@
using Microsoft.AspNet.Mvc.ModelBinding;
namespace Microsoft.AspNet.Mvc
{
public class ActionBindingContext
{
public ActionBindingContext(ActionContext context,
IModelMetadataProvider metadataProvider,
IModelBinder modelBinder,
IValueProvider valueProvider,
IInputFormatter inputFormatter)
{
ActionContext = context;
MetadataProvider = metadataProvider;
ModelBinder = modelBinder;
ValueProvider = valueProvider;
InputFormatter = inputFormatter;
}
public ActionContext ActionContext { get; private set; }
public IModelMetadataProvider MetadataProvider { get; private set; }
public IModelBinder ModelBinder { get; private set; }
public IValueProvider ValueProvider { get; private set; }
public IInputFormatter InputFormatter { get; private set; }
}
}

View File

@ -0,0 +1,42 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNet.Mvc.ModelBinding;
namespace Microsoft.AspNet.Mvc
{
public class DefaultActionBindingContextProvider : IActionBindingContextProvider
{
private readonly IModelMetadataProvider _modelMetadataProvider;
private readonly IEnumerable<IModelBinder> _modelBinders;
private readonly IEnumerable<IValueProviderFactory> _valueProviderFactories;
private readonly IEnumerable<IInputFormatter> _bodyReaders;
public DefaultActionBindingContextProvider(IModelMetadataProvider modelMetadataProvider,
IEnumerable<IModelBinder> modelBinders,
IEnumerable<IValueProviderFactory> valueProviderFactories,
IEnumerable<IInputFormatter> bodyReaders)
{
_modelMetadataProvider = modelMetadataProvider;
_modelBinders = modelBinders.OrderBy(binder => binder.GetType() == typeof(ComplexModelDtoModelBinder) ? 1 : 0);
_valueProviderFactories = valueProviderFactories;
_bodyReaders = bodyReaders;
}
public async Task<ActionBindingContext> GetActionBindingContextAsync(ActionContext actionContext)
{
var requestContext = new RequestContext(actionContext.HttpContext, actionContext.RouteValues);
var valueProviders = await Task.WhenAll(_valueProviderFactories.Select(factory => factory.GetValueProviderAsync(requestContext)));
valueProviders = valueProviders.Where(vp => vp != null)
.ToArray();
return new ActionBindingContext(
actionContext,
_modelMetadataProvider,
new CompositeModelBinder(_modelBinders),
new CompositeValueProvider(valueProviders),
new CompositeInputFormatter(_bodyReaders)
);
}
}
}

View File

@ -0,0 +1,9 @@
using System;
namespace Microsoft.AspNet.Mvc
{
[AttributeUsage(AttributeTargets.Parameter)]
public sealed class FromBodyAttribute : Attribute
{
}
}

View File

@ -0,0 +1,10 @@
using System.Threading.Tasks;
using Microsoft.AspNet.Mvc.ModelBinding;
namespace Microsoft.AspNet.Mvc
{
public interface IActionBindingContextProvider
{
Task<ActionBindingContext> GetActionBindingContextAsync(ActionContext actionContext);
}
}

View File

@ -1,13 +1,17 @@
 using System;
namespace Microsoft.AspNet.Mvc namespace Microsoft.AspNet.Mvc
{ {
// This is a placeholder and is missing things that we'll need for real model binding
public class ParameterBindingInfo public class ParameterBindingInfo
{ {
public bool IsOptional { get; set; } public ParameterBindingInfo(string prefix, Type parameterType)
{
Prefix = prefix;
ParameterType = parameterType;
}
public bool IsFromBody { get; set; } public string Prefix { get; private set; }
public string Prefix { get; set; } public Type ParameterType { get; private set; }
} }
} }

View File

@ -1,4 +1,4 @@
 using System;
namespace Microsoft.AspNet.Mvc namespace Microsoft.AspNet.Mvc
{ {
@ -6,6 +6,10 @@ namespace Microsoft.AspNet.Mvc
{ {
public string Name { get; set; } public string Name { get; set; }
public ParameterBindingInfo Binding { get; set; } public bool IsOptional { get; set; }
public ParameterBindingInfo ParameterBindingInfo { get; set; }
public BodyParameterInfo BodyParameterInfo { get; set; }
} }
} }

View File

@ -1,8 +1,11 @@
using System; using System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNet.Abstractions; using Microsoft.AspNet.Abstractions;
using Microsoft.AspNet.Mvc.Internal;
using Microsoft.AspNet.Mvc.ModelBinding;
namespace Microsoft.AspNet.Mvc namespace Microsoft.AspNet.Mvc
{ {
@ -13,21 +16,24 @@ namespace Microsoft.AspNet.Mvc
private readonly IActionResultFactory _actionResultFactory; private readonly IActionResultFactory _actionResultFactory;
private readonly IServiceProvider _serviceProvider; private readonly IServiceProvider _serviceProvider;
private readonly IControllerFactory _controllerFactory; private readonly IControllerFactory _controllerFactory;
private readonly IActionBindingContextProvider _bindingProvider;
public ReflectedActionInvoker(ActionContext actionContext, public ReflectedActionInvoker(ActionContext actionContext,
ReflectedActionDescriptor descriptor, ReflectedActionDescriptor descriptor,
IActionResultFactory actionResultFactory, IActionResultFactory actionResultFactory,
IControllerFactory controllerFactory, IControllerFactory controllerFactory,
IActionBindingContextProvider bindingContextProvider,
IServiceProvider serviceProvider) IServiceProvider serviceProvider)
{ {
_actionContext = actionContext; _actionContext = actionContext;
_descriptor = descriptor; _descriptor = descriptor;
_actionResultFactory = actionResultFactory; _actionResultFactory = actionResultFactory;
_controllerFactory = controllerFactory; _controllerFactory = controllerFactory;
_bindingProvider = bindingContextProvider;
_serviceProvider = serviceProvider; _serviceProvider = serviceProvider;
} }
public Task InvokeActionAsync() public async Task InvokeActionAsync()
{ {
IActionResult actionResult = null; IActionResult actionResult = null;
@ -39,7 +45,8 @@ namespace Microsoft.AspNet.Mvc
} }
else else
{ {
Initialize(controller); var modelState = new ModelStateDictionary();
InitializeController(controller, modelState);
var method = _descriptor.MethodInfo; var method = _descriptor.MethodInfo;
@ -49,17 +56,19 @@ namespace Microsoft.AspNet.Mvc
} }
else else
{ {
object actionReturnValue = method.Invoke(controller, null); var parameterValues = await GetParameterValues(modelState);
object actionReturnValue = method.Invoke(controller, GetArgumentValues(parameterValues));
actionResult = _actionResultFactory.CreateActionResult(method.ReturnType, actionReturnValue, _actionContext); actionResult = _actionResultFactory.CreateActionResult(method.ReturnType, actionReturnValue, _actionContext);
} }
} }
// TODO: This will probably move out once we got filters // TODO: This will probably move out once we got filters
return actionResult.ExecuteResultAsync(_actionContext); await actionResult.ExecuteResultAsync(_actionContext);
} }
private void Initialize(object controller) private void InitializeController(object controller, ModelStateDictionary modelState)
{ {
var controllerType = controller.GetType(); var controllerType = controller.GetType();
@ -72,6 +81,10 @@ namespace Microsoft.AspNet.Mvc
prop.SetValue(controller, _actionContext.HttpContext); prop.SetValue(controller, _actionContext.HttpContext);
} }
} }
else if (prop.Name == "ModelState" && prop.PropertyType == typeof(ModelStateDictionary))
{
prop.SetValue(controller, modelState);
}
} }
var method = controllerType.GetRuntimeMethods().FirstOrDefault(m => m.Name.Equals("Initialize", StringComparison.OrdinalIgnoreCase)); var method = controllerType.GetRuntimeMethods().FirstOrDefault(m => m.Name.Equals("Initialize", StringComparison.OrdinalIgnoreCase));
@ -86,5 +99,57 @@ namespace Microsoft.AspNet.Mvc
method.Invoke(controller, args); method.Invoke(controller, args);
} }
private async Task<IDictionary<string, object>> GetParameterValues(ModelStateDictionary modelState)
{
var actionBindingContext = await _bindingProvider.GetActionBindingContextAsync(_actionContext);
var parameters = _descriptor.Parameters;
var parameterValues = new Dictionary<string, object>(parameters.Count, StringComparer.Ordinal);
for (int i = 0; i < parameters.Count; i++)
{
var parameter = parameters[i];
if (parameter.BodyParameterInfo != null)
{
var inputFormatterContext = actionBindingContext.CreateInputFormatterContext(
modelState,
parameter);
await actionBindingContext.InputFormatter.ReadAsync(inputFormatterContext);
parameterValues[parameter.Name] = inputFormatterContext.Model;
}
else
{
var modelBindingContext = actionBindingContext.CreateModelBindingContext(
modelState,
parameter);
actionBindingContext.ModelBinder.BindModel(modelBindingContext);
parameterValues[parameter.Name] = modelBindingContext.Model;
}
}
return parameterValues;
}
private object[] GetArgumentValues(IDictionary<string, object> parameterValues)
{
var parameters = _descriptor.MethodInfo.GetParameters();
var arguments = new object[parameters.Length];
for (int i = 0; i < arguments.Length; i++)
{
object value;
if (parameterValues.TryGetValue(parameters[i].Name, out value))
{
arguments[i] = value;
}
else
{
arguments[i] = parameters[i].DefaultValue;
}
}
return arguments;
}
} }
} }

View File

@ -0,0 +1,29 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Microsoft.AspNet.Mvc.ModelBinding
{
public class CompositeInputFormatter : IInputFormatter
{
private IInputFormatter[] _bodyReaders;
public CompositeInputFormatter(IEnumerable<IInputFormatter> bodyReaders)
{
_bodyReaders = bodyReaders.ToArray();
}
public async Task<bool> ReadAsync(InputFormatterContext context)
{
for(int i = 0; i < _bodyReaders.Length; i++)
{
if (await _bodyReaders[i].ReadAsync(context))
{
return true;
}
}
return false;
}
}
}

View File

@ -0,0 +1,9 @@
using System.Threading.Tasks;
namespace Microsoft.AspNet.Mvc.ModelBinding
{
public interface IInputFormatter
{
Task<bool> ReadAsync(InputFormatterContext context);
}
}

View File

@ -0,0 +1,19 @@
using System;
namespace Microsoft.AspNet.Mvc.ModelBinding
{
public class InputFormatterContext
{
public InputFormatterContext(ModelMetadata metadata, ModelStateDictionary modelState)
{
Metadata = metadata;
ModelState = modelState;
}
public ModelMetadata Metadata { get; private set; }
public ModelStateDictionary ModelState { get; private set; }
public object Model { get; set; }
}
}

View File

@ -0,0 +1,13 @@
using System;
using System.Threading.Tasks;
namespace Microsoft.AspNet.Mvc.ModelBinding
{
public class JsonInputFormatter : IInputFormatter
{
public Task<bool> ReadAsync(InputFormatterContext bindingContext)
{
return Task.FromResult(false);
}
}
}

View File

@ -2,6 +2,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Microsoft.AspNet.Mvc.ModelBinding.Internal; using Microsoft.AspNet.Mvc.ModelBinding.Internal;
namespace Microsoft.AspNet.Mvc.ModelBinding namespace Microsoft.AspNet.Mvc.ModelBinding
@ -14,8 +15,8 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
{ {
} }
public CompositeValueProvider(IList<IValueProvider> list) public CompositeValueProvider(IEnumerable<IValueProvider> valueProviders)
: base(list) : base(valueProviders.ToList())
{ {
} }

View File

@ -4,7 +4,6 @@ using Microsoft.AspNet.DependencyInjection.NestedProviders;
using Microsoft.AspNet.FileSystems; using Microsoft.AspNet.FileSystems;
using Microsoft.AspNet.Mvc.ModelBinding; using Microsoft.AspNet.Mvc.ModelBinding;
using Microsoft.AspNet.Mvc.Razor; using Microsoft.AspNet.Mvc.Razor;
using Microsoft.AspNet.Mvc.Razor.Compilation;
namespace Microsoft.AspNet.Mvc namespace Microsoft.AspNet.Mvc
{ {
@ -28,8 +27,6 @@ namespace Microsoft.AspNet.Mvc
Add<IActionResultHelper, ActionResultHelper>(); Add<IActionResultHelper, ActionResultHelper>();
Add<IActionResultFactory, ActionResultFactory>(); Add<IActionResultFactory, ActionResultFactory>();
Add<IParameterDescriptorFactory, DefaultParameterDescriptorFactory>(); Add<IParameterDescriptorFactory, DefaultParameterDescriptorFactory>();
Add<IValueProviderFactory, RouteValueValueProviderFactory>();
Add<IValueProviderFactory, QueryStringValueProviderFactory>();
Add<IControllerAssemblyProvider, AppDomainControllerAssemblyProvider>(); Add<IControllerAssemblyProvider, AppDomainControllerAssemblyProvider>();
Add<IActionDiscoveryConventions, DefaultActionDiscoveryConventions>(); Add<IActionDiscoveryConventions, DefaultActionDiscoveryConventions>();
AddInstance<IFileSystem>(new PhysicalFileSystem(appRoot)); AddInstance<IFileSystem>(new PhysicalFileSystem(appRoot));
@ -48,11 +45,25 @@ namespace Microsoft.AspNet.Mvc
Add<IVirtualPathViewFactory, VirtualPathViewFactory>(); Add<IVirtualPathViewFactory, VirtualPathViewFactory>();
Add<IViewEngine, RazorViewEngine>(); Add<IViewEngine, RazorViewEngine>();
Add<IModelMetadataProvider, DataAnnotationsModelMetadataProvider>();
Add<IActionBindingContextProvider, DefaultActionBindingContextProvider>();
// This is temporary until DI has some magic for it // This is temporary until DI has some magic for it
Add<INestedProviderManager<ActionDescriptorProviderContext>, NestedProviderManager<ActionDescriptorProviderContext>>(); Add<INestedProviderManager<ActionDescriptorProviderContext>, NestedProviderManager<ActionDescriptorProviderContext>>();
Add<INestedProviderManager<ActionInvokerProviderContext>, NestedProviderManager<ActionInvokerProviderContext>>(); Add<INestedProviderManager<ActionInvokerProviderContext>, NestedProviderManager<ActionInvokerProviderContext>>();
Add<INestedProvider<ActionDescriptorProviderContext>, ReflectedActionDescriptorProvider>(); Add<INestedProvider<ActionDescriptorProviderContext>, ReflectedActionDescriptorProvider>();
Add<INestedProvider<ActionInvokerProviderContext>, ActionInvokerProvider>(); Add<INestedProvider<ActionInvokerProviderContext>, ActionInvokerProvider>();
Add<IValueProviderFactory, RouteValueValueProviderFactory>();
Add<IValueProviderFactory, QueryStringValueProviderFactory>();
Add<IModelBinder, TypeConverterModelBinder>();
Add<IModelBinder, TypeMatchModelBinder>();
Add<IModelBinder, GenericModelBinder>();
Add<IModelBinder, MutableObjectModelBinder>();
Add<IModelBinder, ComplexModelDtoModelBinder>();
Add<IInputFormatter, JsonInputFormatter>();
} }
private void Add<T, TU>() where TU : T private void Add<T, TU>() where TU : T