acton parameters
The asp.net mvc framework matches action parameters in the following order:
- Form values
- Route arguments
- Querystring parameters
The parameters that is passed in the action method is handled by ControllerActionInvoker.The ControllerActionInvoker tries to using reflection and other techniques to extract information for client's request, as the source code shows below.
//ControllerActionInvoker members public bool InvokeAction(ControllerContext controllerContext, string actionName) { //..code skip IDictionary<string, object> parameters = GetParameterValues(controllerContext, actionDescriptor); ActionExecutedContext postActionContext = InvokeActionMethodWithFilters(controllerContext, filterInfo.ActionFilters, actionDescriptor, parameters); InvokeActionResultWithFilters(controllerContext, filterInfo.ResultFilters, postActionContext.Result); //.. } protected virtual IDictionary<string, object> GetParameterValues(ControllerContext controllerContext, ActionDescriptor actionDescriptor) { Dictionary<string, object> parametersDict = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase); ParameterDescriptor[] parameterDescriptors = actionDescriptor.GetParameters(); foreach (ParameterDescriptor parameterDescriptor in parameterDescriptors) { parametersDict[parameterDescriptor.ParameterName] = GetParameterValue(controllerContext, parameterDescriptor); } return parametersDict; } protected virtual object GetParameterValue(ControllerContext controllerContext, ParameterDescriptor parameterDescriptor) { // collect all of the necessary binding properties Type parameterType = parameterDescriptor.ParameterType; //you can register your binder, but default is DefaultModelBinder IModelBinder binder = GetModelBinder(parameterDescriptor); IDictionary<string, ValueProviderResult> valueProvider = controllerContext.Controller.ValueProvider; string parameterName = parameterDescriptor.BindingInfo.Prefix ?? parameterDescriptor.ParameterName; Predicate<string> propertyFilter = GetPropertyFilter(parameterDescriptor); // finally, call into the binder ModelBindingContext bindingContext = new ModelBindingContext() { FallbackToEmptyPrefix = (parameterDescriptor.BindingInfo.Prefix == null), // only fall back if prefix not specified ModelName = parameterName, ModelState = controllerContext.Controller.ViewData.ModelState, ModelType = parameterType, PropertyFilter = propertyFilter, ValueProvider = valueProvider }; object result = binder.BindModel(controllerContext, bindingContext); return result; }
You can also register your ModelBinder to help mvc to build your parameter in the application startup like the code below.
public class ConferenceModelBinder : DefaultModelBinder { private readonly IConferenceRepository _repository; //We can require dependencies in binders just like normal classes public ConferenceModelBinder(IConferenceRepository repository) { _repository = repository; } public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) { //first, we get the value from the request that matches the name ValueProviderResult providerResult = bindingContext.ValueProvider[bindingContext.ModelName]; //next we use our repository to find the Conference by the key Conference conference = _repository.GetByKey(providerResult.AttemptedValue); return conference; } } ModelBinders.Binders.Add(typeof (Conference), new ConferenceModelBinder( new ConferenceRepositoryStub()));