Thinking in Asp.Net MVC – (Model Binding)

Model Controller and Descriptor

public interface ICustomAttributeProvider {
object[] GetCustomAttributes(bool inherit);
object[] GetCustomAttributes (Type attributeType, bool inherit);
bool IsDefined (Type attribute, bool inherit);
}

public abstract class ControllerDescriptor : ICustomAttributeProvider{
public virtual object[] GetCustomAttributes(bool inherit);
public virtual object[] GetCustomAttributes(Type attributeType, bool inherit);
public virtual bool IsDefined(Type attributeType, bool inherit);
public virtual IEnumerable GetFilterAttributes(bool useCache);
public abstract ActionDescriptor FindAction(ControllerContext context, string actionName);
public abstract ActionDescriptor[] GetCanonicalActions ();
public virtual string ControllerName {get;}
public abstract Type controllerType {get;}
public virtual string UniqueId {get;}
}

public class ReflectedControllerDescriptor : ControllerDescriptor {
public ReflectedControllerDescriptor (Type controllerType);
public override object[] GetCustomAttributes(bool inherit);
public override object[] GetCustomAttributes(Type attributeType, bool inherit);
public override IsDefined(Type attributeType, bool inherit);
public override IEnumerable GetFilterAttributes(bool useCache);
public override ActionDescriptor FindAction(ControllerContext context, string actionName);
public override ActionDescriptor[] GetCanonicalActions();
public sealed override Type ControllerType {get;}
}

-They are the structure of classes providing controller metadata . which will be used to locate a controller.

abstract class ActionNameSelectorAttribute : Attribute{
abstract bool IsValidName (ControllerContext context, string actionName, MethodInfo, method);
}
sealed class ActionNameAttribute : ActionNameSelectorAttribute{
ActionNameAttribute (string name);
override bool IsValidName (ControllerContext context, string actionName, MethodInfo, method);
}
  • These 2 classes defined where to override the MVC action name(and how to validate ) .
abstract class ActionMethodSelectorAttribute: Attribute {

}
sealed class AcceptVerbsAttribute : ActionMethodSelectorAttribute{
AcceptVerbsAttribute (HttpVerbs verbs);
AcceptVerbsAttribute (params string[] verbs);
overrde bool IsValidForRequest (ControllerContext context, MethodInfo method);
}
[Flags]
enum HttpVerbs{
Get = 1,
Post = 2,
Put = 4,
Delete = 8,
Head = 16
}
  • The usage is straight forward , while binding a model ,reflect each action and check if it is matching current request ‘Http Method’.
  • It is flag enum , we can label multiple on the method .
  • Below are the shortcut versions :
    [HttpGet]
    [HttpPost]
    [HttpPut]
    [HttpDelete]
    [HttpHead]
    [HttpOptions]
    [HttpPatch]
abstract class ActionDescriptor : ICustomAttributeProvider{
virtual object[] GetCustomAttributes(bool inherit);
virtual object[] GetCustomAttributes(Type attribute, bool inherit);
virtual bool IsDefined (Type type, bool inherit);
virtual IEnumerable GetFilterAttributes (bool useCache);
abstract ParameterDescriptor[] GetParameters();
abstract object Execute(ControllerContext context, IDictionary<string, object> parameters);
virtual ICollection GetSelectors();
virtual FilterInfo GetFilters();
abstract string ActionName {get;}
abstract ControllerDescriptor ControllerDescriptor {get;}
virtual string UniqueId {get;}
}
FilterInfo{
IList ActionFilters {get;}
IList AuthorizationFilters {get;}
IList ExceptionFilters {get;}
IList ResultFilters {get;}
}
class ReflectedActionDescriptor : ActionDescriptor {
...
}
  • they are the structure of the Action meta data attribute classes . will be used to locate action inside a controller .
  • there are four kinds of filter in MVC : Action, Authorization, Exception, Result . can be used whenever we want to apply certain filter logic on actions .
  • same with controller meta structure , MVC also provide a ‘default implementation’ called ReflectedActionDescriptor class .
  • here the action meta class has method to get All Parameters Descriptor : ParameterDescriptor[], which is used to provide values for the parameters after action is located .
abstract class ParameterDescriptor :ICustomAttributeProvider{
virtual object[] GetCustomAttributes (bool inherit);
virtual object[] GetCustomAttributes (Type type, bool inherit);
...
abstract ActionDescriptor ActionDescripor {get;}
abstract string ParameterName {get;}
abstract Type ParameterType {get;}
virtual object DefaultValue {get;}
virtual object ParameterBindingInfo BindingInfo{get;}
}

abstract class ParameterBindingInfo {
virtual IModelBinder Binder {get;}
virtual ICollection Include {get;}
virtual ICollection Exclude {get;}
virtual string Prefix {get;}
}

class ReflectedParameterDescriptor : ParameterDescriptor {
...
}
// (request) value providers
IValueProvider {
ValueProviderResult GetValue (string key);
}

class NameValueCollectionValueProvider : IValueProvider {
...
}
// form provider
class FormValueProvider : NameValueCollectionValueProvider{
...
}
// query string provider
class NameValueCollection : NameValueCollectionValueProvider{
...
}

class DictionaryValueProvider : IValueProvider{
DictionaryValueProvider (IDictionary<string, TType> dictionary, CultureInfo culture);
vitual bool ContainsPrefix (string prefix);
virtual IDictionary<string, string> GetKeysFromPrefix (string prefix);
virtual ValueProviderResult GetValue (string key);
}
class RouteDataValueProvider : DictionaryValueProvider {...}// file data providerabstract class HttpPostedFileBase {...virtual string FileName {get;}virtual Stram UnputStream {get;}}sealed class HttpFileCollectionValueProvider :DictionaryValueProvider  {....}// child action value providersealed class ChildActionValueProvider : DictionaryValueProvider {...}// provider factory classesabstract class ValueProviderFactory {IValueProvider GetValueProvider (ControllerContext context);}...

  • the meta data classes of Mvc Action Parameter including the basic description of parameter (name, type, defaultValue …).
  • after found the method (action) for the Http Request , need to convert the data from ValueProviders (Route, Form, QueryString) into the parameter model using ModelBinder.
  • There are different value providers . Form, Querystring, route are used to get data from Request ; FileProvider is used to get requesting files ; ChildActionValueProvider will be used when calleing a child action (e.g. @Html.Action(“someChildAction”, new {…}));
  • MVC also providing Factory classes using abstract factory pattern .There are QuerystringValueProviderFactory,FormValueProviderFactory,RouteDataValueProviderFactory,JSONValueProviderFactory,FileCollectionValueProviderFactory, ChildActionValueProviderFactory.
  • since got all the parameter values from the providers , the last job is Model binder .

  • Model binder structure classes

public interface IModelBinder{
object BindModel(ControllerContext controllerContext, ModleBindingContext context);
}
abstract class CustomModelBinderAttribute : Attribute{
abstract IModelBinder GetBinder();
}
sealed class ModelBinderAttribute : CustomModelBinderAttribute{
public ModelBinderAttribute (Type type);
override IModelBinder GetBinder();
Type BinderType {[CompilerGenerated] get;}
}
public static class ModelBinders {
public static ModelBinderDictionary binders {get;}
}
public class ModelBinderDictionary
:IDictionary<Type, IModelBinder>,
ICollection<KeyValuePair<Type, IModelBinder>>,
IEnumerable<KeyValuePair<Type, IModelBinder>>,
IEnumerable{
public IModelBinder GetBinder(Type type);
public virtual IModelBinder GetBinder (Type type, bool fallbackToDefault);
class XXXModelBinder : IModelBinder{
public object BindModel (ControllerContext context, ModelBindingContext bindingContext){
...
}
}
  • above struture provided a way of defining customized ModelBinder . In Application_Start,Add binders :
protected void Application_Start(){
...
ModelBinders.Binders.Add(typeof(XXX), new XXXModelBinder());
...
}

Another of implementing your own ModelBinder is using ModelBinderProvider .

interface IModelBinderProvider {
IModelBinder GetBinder (Type modelType);
}
public static class ModelBinderProviders {
static ModelBinderProviderCollection BinderProviders {get;}
}
sealed class ModelBinderProviderCollection : Collection<imodelbinderprovider>{
...
}

then define your own model binder provider :

class XModelBinderProvider : IModelBinderProvider{
IModelBinder GetBinder(Type modelType){
// switch type
// case type1 : return modelBinder1
// case type2 : return modelBinder2
...
}
}
protected void Application_Start(){
ModelBinderProviders.BinderProviders.Add(new XModelBinderProvider);
}

Above is another way of implementing own modelBinder and inject it in Application_Start .

Author: lanliang

Programmer.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s