Sunday, September 18, 2011

ASP.NET MVC Internals 3: Controller structure

Controllers implement the IController interface:
public interface IController 
{
    void Execute(RequestContext requestContext);
}
ControllerBase abstract class implements the IController interface:
public abstract class ControllerBase : MarshalByRefObject, IController 
{

    private TempDataDictionary _tempDataDictionary;
    private bool _validateRequest = true;
    private IDictionary<string, ValueProviderResult> _valueProvider;
    private ViewDataDictionary _viewDataDictionary;

    public ControllerContext ControllerContext {
        get;
        set;
    }

    public TempDataDictionary TempData {
        get {
            if (_tempDataDictionary == null) {
                _tempDataDictionary = new TempDataDictionary();
            }
            return _tempDataDictionary;
        }
        set {
            _tempDataDictionary = value;
        }
    }

    public bool ValidateRequest {
        get {
            return _validateRequest;
        }
        set {
            _validateRequest = value;
        }
    }

    public IDictionary<string, ValueProviderResult> ValueProvider {
        get {
            if (_valueProvider == null) {
                _valueProvider = new ValueProviderDictionary(ControllerContext);
            }
            return _valueProvider;
        }
        set {
            _valueProvider = value;
        }
    }

    public ViewDataDictionary ViewData {
        get {
            if (_viewDataDictionary == null) {
                _viewDataDictionary = new ViewDataDictionary();
            }
            return _viewDataDictionary;
        }
        set {
            _viewDataDictionary = value;
        }
    }

    protected virtual void Execute(RequestContext requestContext) {
        if (requestContext == null) {
            throw new ArgumentNullException("requestContext");
        }

        Initialize(requestContext);
        ExecuteCore();
    }

    protected abstract void ExecuteCore();

    protected virtual void Initialize(RequestContext requestContext) {
        ControllerContext = new ControllerContext(requestContext, this);
    }
    
    [...]
}

The Controller class derives from ControllerBase abstract class:
public abstract class Controller : ControllerBase, IActionFilter, IAuthorizationFilter, IDisposable, IExceptionFilter, IResultFilter {

    private IActionInvoker _actionInvoker;
    private ModelBinderDictionary _binders;
    private RouteCollection _routeCollection;
    private ITempDataProvider _tempDataProvider;

    public IActionInvoker ActionInvoker {
        get {
            if (_actionInvoker == null) {
                _actionInvoker = new ControllerActionInvoker();
            }
            return _actionInvoker;
        }
        set {
            _actionInvoker = value;
        }
    }

    protected internal ModelBinderDictionary Binders {
        get {
            if (_binders == null) {
                _binders = ModelBinders.Binders;
            }
            return _binders;
        }
        set {
            _binders = value;
        }
    }

    public HttpContextBase HttpContext {
        get {
            return ControllerContext == null ? null : ControllerContext.HttpContext;
        }
    }

    public ModelStateDictionary ModelState {
        get {
            return ViewData.ModelState;
        }
    }

    public HttpRequestBase Request {
        get {
            return HttpContext == null ? null : HttpContext.Request;
        }
    }

    public HttpResponseBase Response {
        get {
            return HttpContext == null ? null : HttpContext.Response;
        }
    }

    internal RouteCollection RouteCollection {
        get {
            if (_routeCollection == null) {
                _routeCollection = RouteTable.Routes;
            }
            return _routeCollection;
        }
        set {
            _routeCollection = value;
        }
    }

    public RouteData RouteData {
        get {
            return ControllerContext == null ? null : ControllerContext.RouteData;
        }
    }

    public HttpServerUtilityBase Server {
        get {
            return HttpContext == null ? null : HttpContext.Server;
        }
    }

    public HttpSessionStateBase Session {
        get {
            return HttpContext == null ? null : HttpContext.Session;
        }
    }

    public ITempDataProvider TempDataProvider {
        get {
            if (_tempDataProvider == null) {
                _tempDataProvider = new SessionStateTempDataProvider();
            }
            return _tempDataProvider;
        }
        set {
            _tempDataProvider = value;
        }
    }

    public UrlHelper Url {
        get;
        set;
    }

    public IPrincipal User {
        get {
            return HttpContext == null ? null : HttpContext.User;
        }
    }

    [...]

    protected internal virtual ContentResult Content(string content, string contentType, Encoding contentEncoding) {
        return new ContentResult {
            Content = content,
            ContentType = contentType,
            ContentEncoding = contentEncoding
        };
    }

    [...]

    protected override void ExecuteCore() {
        TempData.Load(ControllerContext, TempDataProvider);

        try {
            string actionName = RouteData.GetRequiredString("action");
            if (!ActionInvoker.InvokeAction(ControllerContext, actionName)) {
                HandleUnknownAction(actionName);
            }
        }
        finally {
            TempData.Save(ControllerContext, TempDataProvider);
        }
    }

    [...]

    protected internal virtual FilePathResult File(string fileName, string contentType, string fileDownloadName) {
        return new FilePathResult(fileName, contentType) { FileDownloadName = fileDownloadName };
    }

    protected virtual void HandleUnknownAction(string actionName) {
        throw new HttpException(404, String.Format(CultureInfo.CurrentUICulture,
            MvcResources.Controller_UnknownAction, actionName, GetType().FullName));
    }

    protected internal virtual JavaScriptResult JavaScript(string script) {
        return new JavaScriptResult { Script = script };
    }

    [...]

    protected internal virtual JsonResult Json(object data, string contentType, Encoding contentEncoding) {
        return new JsonResult {
            Data = data,
            ContentType = contentType,
            ContentEncoding = contentEncoding
        };
    }

    protected override void Initialize(RequestContext requestContext) {
        base.Initialize(requestContext);
        Url = new UrlHelper(requestContext);
    }

    protected virtual void OnActionExecuting(ActionExecutingContext filterContext) {
    }

    protected virtual void OnActionExecuted(ActionExecutedContext filterContext) {
    }

    protected virtual void OnAuthorization(AuthorizationContext filterContext) {
    }

    protected virtual void OnException(ExceptionContext filterContext) {
    }

    protected virtual void OnResultExecuted(ResultExecutedContext filterContext) {
    }

    protected virtual void OnResultExecuting(ResultExecutingContext filterContext) {
    }

    [...]

    protected internal PartialViewResult PartialView(string viewName) {
        return PartialView(viewName, null /* model */);
    }

    protected internal virtual PartialViewResult PartialView(string viewName, object model) {
        if (model != null) {
            ViewData.Model = model;
        }

        return new PartialViewResult {
            ViewName = viewName,
            ViewData = ViewData,
            TempData = TempData
        };
    }

    protected internal virtual RedirectResult Redirect(string url) {
        [...]
        return new RedirectResult(url);
    }

    protected internal RedirectToRouteResult RedirectToAction(string actionName) {
        return RedirectToAction(actionName, (RouteValueDictionary)null);
    }

    [...]

    protected internal virtual RedirectToRouteResult RedirectToAction(string actionName, string controllerName, RouteValueDictionary routeValues) {
        RouteValueDictionary mergedRouteValues;

        if (RouteData == null) {
            mergedRouteValues = RouteValuesHelpers.MergeRouteValues(actionName, controllerName, null, routeValues, true /* includeImplicitMvcValues */);
        }
        else {
            mergedRouteValues = RouteValuesHelpers.MergeRouteValues(actionName, controllerName, RouteData.Values, routeValues, true /* includeImplicitMvcValues */);
        }

        return new RedirectToRouteResult(mergedRouteValues);
    }

    [...]

    protected internal RedirectToRouteResult RedirectToRoute(string routeName) {
        return RedirectToRoute(routeName, (RouteValueDictionary)null);
    }

    [...]

    protected internal bool TryUpdateModel<TModel>(TModel model) where TModel : class {
        return TryUpdateModel(model, null, null, null, ValueProvider);
    }

    [...]

    protected internal bool TryUpdateModel<TModel>(TModel model, string prefix, string[] includeProperties, string[] excludeProperties, IDictionary<string, ValueProviderResult> valueProvider) where TModel : class {
        [...]

        Predicate<string> propertyFilter = propertyName => BindAttribute.IsPropertyAllowed(propertyName, includeProperties, excludeProperties);
        IModelBinder binder = Binders.GetBinder(typeof(TModel));

        ModelBindingContext bindingContext = new ModelBindingContext() {
            Model = model,
            ModelName = prefix,
            ModelState = ModelState,
            ModelType = typeof(TModel),
            PropertyFilter = propertyFilter,
            ValueProvider = valueProvider
        };
        binder.BindModel(ControllerContext, bindingContext);
        return ModelState.IsValid;
    }

    protected internal void UpdateModel<TModel>(TModel model) where TModel : class {
        UpdateModel(model, null, null, null, ValueProvider);
    }

    [...]

    protected internal void UpdateModel<TModel>(TModel model, string prefix, string[] includeProperties, string[] excludeProperties, IDictionary<string, ValueProviderResult> valueProvider) where TModel : class {
        bool success = TryUpdateModel(model, prefix, includeProperties, excludeProperties, valueProvider);
        if (!success) {
            string message = String.Format(CultureInfo.CurrentUICulture, MvcResources.Controller_UpdateModel_UpdateUnsuccessful,
                typeof(TModel).FullName);
            throw new InvalidOperationException(message);
        }
    }

    protected internal ViewResult View() {
        return View(null /* viewName */, null /* masterName */, null /* model */);
    }

    protected internal ViewResult View(object model) {
        return View(null /* viewName */, null /* masterName */, model);
    }

    protected internal ViewResult View(string viewName) {
        return View(viewName, null /* masterName */, null /* model */);
    }

    protected internal ViewResult View(string viewName, string masterName) {
        return View(viewName, masterName, null /* model */);
    }

    protected internal ViewResult View(string viewName, object model) {
        return View(viewName, null /* masterName */, model);
    }

    protected internal virtual ViewResult View(string viewName, string masterName, object model) {
        if (model != null) {
            ViewData.Model = model;
        }

        return new ViewResult {
            ViewName = viewName,
            MasterName = masterName,
            ViewData = ViewData,
            TempData = TempData
        };
    }

    protected internal ViewResult View(IView view) {
        return View(view, null /* model */);
    }

    protected internal virtual ViewResult View(IView view, object model) {
        if (model != null) {
            ViewData.Model = model;
        }

        return new ViewResult {
            View = view,
            ViewData = ViewData,
            TempData = TempData
        };
    }

    #region IActionFilter Members
    void IActionFilter.OnActionExecuting(ActionExecutingContext filterContext) {
        OnActionExecuting(filterContext);
    }

    void IActionFilter.OnActionExecuted(ActionExecutedContext filterContext) {
        OnActionExecuted(filterContext);
    }
    #endregion

    #region IAuthorizationFilter Members
    void IAuthorizationFilter.OnAuthorization(AuthorizationContext filterContext) {
        OnAuthorization(filterContext);
    }
    #endregion

    #region IExceptionFilter Members
    void IExceptionFilter.OnException(ExceptionContext filterContext) {
        OnException(filterContext);
    }
    #endregion

    #region IResultFilter Members
    void IResultFilter.OnResultExecuting(ResultExecutingContext filterContext) {
        OnResultExecuting(filterContext);
    }

    void IResultFilter.OnResultExecuted(ResultExecutedContext filterContext) {
        OnResultExecuted(filterContext);
    }
    #endregion
}
The Controller class implements the ExecuteCore() function. It sets the TempData to make it available during the action method execution. The method reads the action name from the RouteData and calls the action method, identified by the recently read name, using the ActionInvoker.InvokeAction() method.
All the usually used helper methods such as View(), UpdateModel(), File(), Json() are defined by the Controller class.
ASP.NET MCV: Controller inheritance
Link: ASP.NET MVC – Controller inheritance