Understanding ActionResults in Mvc

Almost all our Action in Mvc returns some kind of an ActionResult ( surprise! ). Or let's say , they return a class derived from ActionResult , such as ; 

· ViewResult

· RedirectResult

· RedirectToRouteResult

· JsonResult

But how does this ActionResults work? It pretty simple actually ;

namespace System.Web.Mvc
{
    public abstract class ActionResult
    {
        public abstract void ExecuteResult(ControllerContext context);
    }
}


Told you! It's just an abstract class with ExecuteResult function.

So let's look into one of the most commonly used ActionResult , ViewResult and ViewResultBase ( Why I can't link directly to a particular file  , *sigh* ).

Abstract base class , **ViewResultBase** first ;

 
namespace System.Web.Mvc
{
    using System;
    using System.Diagnostics.CodeAnalysis;

    public abstract class ViewResultBase : ActionResult
    {
        private TempDataDictionary _tempData;
        private ViewDataDictionary _viewData;
        private ViewEngineCollection _viewEngineCollection;
        private string _viewName;

        //Lots of properties below , the best part , ExecuteResult , is at the bottom

        [SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly",
        Justification = "This entire type is meant to be mutable.")]
        public TempDataDictionary TempData
        {
            get
            {
                if (_tempData == null)
                {
                    _tempData = new TempDataDictionary();
                }
                return _tempData;
            }
            set
            {
                _tempData = value;
            }
        }

        public IView View
        {
            get;
            set;
        }

        [SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly",
        Justification = "This entire type is meant to be mutable.")]
        public ViewDataDictionary ViewData
        {
            get
            {
                if (_viewData == null)
                {
                    _viewData = new ViewDataDictionary();
                }
                return _viewData;
            }
            set
            {
                _viewData = value;
            }
        }

        [SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly",
        Justification = "This entire type is meant to be mutable.")]
        public ViewEngineCollection ViewEngineCollection
        {
            get
            {
                return _viewEngineCollection ?? ViewEngines.Engines;
            }
            set
            {
                _viewEngineCollection = value;
            }
        }

        public string ViewName
        {
            get
            {
                return _viewName ?? String.Empty;
            }
            set
            {
                _viewName = value;
            }
        }

        public override void ExecuteResult(ControllerContext context)
        {
            if (context == null)
            {
                throw new ArgumentNullException("context");
            }
            if (String.IsNullOrEmpty(ViewName))
            {
                ViewName = context.RouteData.GetRequiredString("action");
            }

            ViewEngineResult result = null;

            if (View == null)
            {
                result = FindView(context); //this is the part we care about at the moment
                View = result.View;
            }

            ViewContext viewContext = new ViewContext(context, View, ViewData, TempData);
            View.Render(viewContext, context.HttpContext.Response.Output);

            if (result != null)
            {
                result.ViewEngine.ReleaseView(context, View); //And probably this
            }
        }

        protected abstract ViewEngineResult FindView(ControllerContext context);
    }
}


Wow , that's a lot of code. But actually it's just a few properties for stuff like ViewData and TempData. What we really care about here is ExecuteResult method ( at the bottom ). As you can see , ExecuteResult ( the only thing we really need for a custom ActionResult ) does 2 main things ; go find the view ( FindView ) and send it to ViewEngine ( ReleaseView ).

And the last part of our ActionResult , **ViewResult** ;

 
public class ViewResult : ViewResultBase
{
    private string _masterName;

    public string MasterName
    {
        get
        {
            return _masterName ?? String.Empty;
        }
        set
        {
            _masterName = value;
        }
    }

    protected override ViewEngineResult FindView(ControllerContext context)
    {
        ViewEngineResult result = ViewEngineCollection.FindView(context, ViewName, MasterName);
        //Obviously this gives you a good opportunity to interfere View & MasterPage selections
        if (result.View != null)
        {
            return result;
        }

        //I took out the part about error generation for now
    }
}


So all ViewResult does , is to use our ViewEngineCollection to find the page ( view ) we want. Remember , then it goes back to ViewResultBase and releases the view and we're pretty much done with ActionResult , ViewEngine will take care of the rest.

Let's have a look at how this all executes ;

Client Requests a Page

Controller is Created

-> Controller - OnActionExecuting runs

Action runs

->ViewResult is Created ( at return View(); )

ActionFinishes

-> Controller - OnActionExecuted runs

-> Controller - OnResultExecuting and OnResultExecuted runs

-> ViewResultBase - ExecuteResult runs

-> ViewResult - FindView runs

-> ViewResultBase send the view to ViewEngine ( ReleaseView )

Some magic happens after that which I know nothing of yet

Client gets the html , and hopefully happy now


You can also check this MSDN article about the execution process.

And Codeplex page of Asp.Net for the source code of Mvc


In next post , we'll create a new custom ActionResult and play with it a little...