Saturday, August 2, 2014

Tune performance and implement own view engine in ASP.NET MVC

As the title suggest, in this article we will learn bit of performance improvement by attaching right view engine in MVC and then we will learn to extend the existing Razor view engine and the implementation of our own view engine.

So, let’s start with the view engine tuning part at first. We know that ActionResult is the super class of all result type of action and ViewResult is one of them. If we want to return a View we can either explicitly specify the view name or may not mention.  Now, view execution process is handle by view engine which performs in last part of MVC pipeline.

At first it decides that whether the return result is ViewResult type or not and if the result is ViewResult type then it view Engine’s responsibility to invoke the appropriate view. Generally, MVC framework uses two view engines by default, those are Web Form view engine and Razor view engine and MVC framework searches for Web Form view engine at first, if not present then Razor view engine.  So, in theory, whenever MVC framework wants to invoke a view, if search for Web Form view engine at first then search for Razor view engine, even if we use only Razor view engine in application. So, let’s see in action.

Here is the controller which contains Index() action and we are trying to return Test view which is not available in application.

      public class TestController : Controller
      {
        public ActionResult Index()
        {
            return View("Test");
        }
       }
Obviously we should see the below screen, because we don’t have the view at all. If we check the search step closely then we will see that MVC framework is searching .aspx page at first then .cshtml and all, so the Web Form view is performing at first then Razor view.



As we said earlier, this will slow down the view execution process, ok. Let’s solve the problem. We know that MVC is highly customize and configurable. Now, we will detach Web Form view engine from MVC pipeline and we will attach only Razor view Engine then the framework will not search for Web Form view in time of execution.

The process is very simple ,just open Global.asax page of application and modify code accordingly.

public class MvcApplication : System.Web.HttpApplication
    {
        protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();
            RouteConfig.RegisterRoutes(RouteTable.Routes);

            //Remove all view engine
            ViewEngines.Engines.Clear();

            //Add Razor view Engine
            ViewEngines.Engines.Add(new RazorViewEngine());

            Bootstrapper.Initialise();
        }
    }

Here we are clearing all view engines from MVC pipeline and then we are registering only Razor view engine. Now, if we run the same application, we will find below output and we are seeing that now MVC framework is not searching for .aspx page. 




Customize the Razor view Engine
Now, there is another small problem in application. We are seeing that Razor view engine is searching both for vbhtml and cshtml page. Now, it’s very much common in application that you might use single programming language in View , either C# or VB. So, here we can again customize the existing Razor view engine to let it search only for cshtml page or vbhtml page.

So, just create one class and inherit it from RazorViewEngine class. Here is the example implementation.

public class CSharpRazorViewEngine : RazorViewEngine
    {
        public CSharpRazorViewEngine()
        {
            AreaViewLocationFormats = new[]
             {
             "~/Areas/{2}/Views/{1}/{0}.cshtml",
             "~/Areas/{2}/Views/Shared/{0}.cshtml"
             };
                        AreaMasterLocationFormats = new[]
             {
             "~/Areas/{2}/Views/{1}/{0}.cshtml",
             "~/Areas/{2}/Views/Shared/{0}.cshtml"
             };
                        AreaPartialViewLocationFormats = new[]
             {
             "~/Areas/{2}/Views/{1}/{0}.cshtml",
             "~/Areas/{2}/Views/Shared/{0}.cshtml"
             };
                        ViewLocationFormats = new[]
             {
             "~/Views/{1}/{0}.cshtml",
             "~/Views/Shared/{0}.cshtml"
             };
                        MasterLocationFormats = new[]
             {
             "~/Views/{1}/{0}.cshtml",
             "~/Views/Shared/{0}.cshtml"
             };
                        PartialViewLocationFormats = new[]
             {
             "~/Views/{1}/{0}.cshtml",
             "~/Views/Shared/{0}.cshtml"
             };
        }
    }

Now, we have to register the view engine in MVC pipeline.  Just modify the Global.asax file accordingly.

public class MvcApplication : System.Web.HttpApplication
    {
        protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();
            RouteConfig.RegisterRoutes(RouteTable.Routes);

            //Remove all view engine
            ViewEngines.Engines.Clear();

            //Add Custom view Engine Derived from Razor
            ViewEngines.Engines.Add(new CSharpRazorViewEngine());

            Bootstrapper.Initialise();
        }
    }

Now, if we run the application, we will see that the custom view Engine is searching for .cshtml page only.

Fine, we have learned to customize the view engine to improve performance of MVC application. Now, we will build our own view engine and we will use the engine in place of existing Web Form view or Razor view.

Implement custom view engine
To implement our own view engine, we have to implement IViewEngine in our own class. The IViewEngine contains three methods.
FindPartialView: This method will search for partial view if there is any partial view call in main view.
FindView : This method will search for main view.
ReleaseView: After execution of view, we can implement disposing activity of our custom view class.
Here, I have implemented IViewEngine interface in “MyViewEngine” class. Have a look on below code.

 public class MyViewEngine : IViewEngine
    {
        public ViewEngineResult FindPartialView(ControllerContext controllerContext, string partialViewName, bool useCache)
        {
            throw new NotImplementedException();
        }

        public ViewEngineResult FindView(ControllerContext controllerContext,
            string viewName, string masterName, bool useCache)
        {
            if (viewName.Contains("myView"))
            {
                return new ViewEngineResult(new myCustomView(),this);
            }
            else
                return new ViewEngineResult(new string[] {"No View found, please provide correct name"});
        }

        public void ReleaseView(ControllerContext controllerContext, IView view)
        {
           
        }
    }

So, we have built our own view engine, now we have to build our own view. To build our own view, we have to implement IView interface in our own class. In this example I have implemented IView interface in myCustomView class.
The IView contains one and only one method called Render where we can implement the rendering mechanism.  In this example, we are just printing string.

  public class myCustomView : IView
    {
        public void Render(ViewContext viewContext, System.IO.TextWriter writer)
        {
            writer.Write("Data from custom view");
        }
    }

Register the custom view
This is one obvious process need to follow. We have to register the custom view in MVC pipeline. Just modify the Global.asax page accordingly.

protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();
            RouteConfig.RegisterRoutes(RouteTable.Routes);

            //Remove all view engine
            ViewEngines.Engines.Clear();

            //Attach custom view in pipeline
            ViewEngines.Engines.Add(new MyViewEngine());
                       
            Bootstrapper.Initialise();
        }

Now, we will implement controller and we will call our custom view, Here I have specify the action name as myView.

public class TestController : Controller
    {
        public ActionResult myView()
        {
            return View();
        }
       }

And once we run the application and try to hit myView() action, we will see that the custom string has appear in screen.


Border Line
In this example we have learned to configure view engine in MVC application and also learned to implement custom view engine, hope this article will give you better understanding of view execution process in MVC application. 


1 comment: