Introduction

One of my pet peeves is code organization when it comes to putting together an application architecture. I have a current application that is built in ASP.NET MVC 2, using IOC, dependency injection, mutliple layers of abstraction from the business layer back.

But, I have found that after it was all said and done...there were .js files a plenty for performing all of the UI validation, presentation, AJAX calls so and so forth. Most of the scripts were in seperate files, but there was also a whole bunch within the views. Debugging and handing this off the the next developer became a challenge.

The goal was to refactor the organiztion and allow to slowly migrate to a better understanding if what executed where and what part of the script file was responsible for what view. The solution - ScriptViewPage.cs

The ScriptViewPage

The ScriptViewPage class inherits System.Web.Mvc.ViewPage and when instantiated, adds an event handler for the PreLoad event.  So when the view renders, or pre-renders, the event is fired and the ScriptViewPage now handles and determines what View is executing and tries to locate the script file that is associated with it.

namespace System.Web.Mvc {     
	public class ScriptViewPage : ViewPage     
	{         
		public ScriptViewPage() 
			: base()         
		{ 
	            this.PreLoad += new EventHandler(_PreLoad);         
		} 

Within the handler, we dig into the ViewContext.RouteData to determine the Controller and View and format the .js script file name.

 string viewScriptPath = string.Format("~/ViewScripts/{0}/{1}.js", ViewContext.RouteData.Values["Controller"], 
ViewContext.RouteData.Values["Action"]); 

Test to see if the file exists and using the TagBuilder; create the script tag and set the text of the Literal control on the Master template/View if the control exists.

if (System.IO.File.Exists(MapPath(viewScriptPath)))
{
   var scr = new TagBuilder("script");
   scr.Attributes.Add("type", "text/javascript");
   scr.Attributes.Add("src", ResolveClientUrl(viewScriptPath));

   Control ctrl = Page.Master.FindControl("viewScript");
   if (ctrl != null)
     ((Literal)ctrl).Text = scr.ToString(TagRenderMode.Normal);

}

Here is the full class code:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

namespace System.Web.Mvc
{
    public class ScriptViewPage : ViewPage
    {
        public ScriptViewPage()
            : base()
        {
            this.PreLoad += new EventHandler(_PreLoad);
        }

        void _PreLoad(object sender, EventArgs e)
        {
            string viewScriptPath = string.Format("~/ViewScripts/{0}/{1}.js", ViewContext.RouteData.Values["Controller"], ViewContext.RouteData.Values["Action"]);

            if (System.IO.File.Exists(MapPath(viewScriptPath)))
            {
                var scr = new TagBuilder("script");
                scr.Attributes.Add("type", "text/javascript");
                scr.Attributes.Add("src", ResolveClientUrl(viewScriptPath));


                Control ctrl = Page.Master.FindControl("viewScript");
                if (ctrl != null)
                    ((Literal)ctrl).Text = scr.ToString(TagRenderMode.Normal);

            }
        }

    }
}


All you need to make sure of is that the .js files are named the same as your views.

Implementation

I needed a way to slowly refactor the existing code, but also allow for the new script organiztion to be used for all new view/scripts added to the application. Here is the direction I took.

  • Add New Folder to the root of the Web Application called "ViewScripts"
  • Replicate the Folder structure that exists within your "Views" folder.
  • Edit Site.Master and add Literal control to bottom of the template above the closing body tag and set the ID="viewScript"
  • In your Views, instead of Inheriting from System.Web.Mvc.ViewPage; Change it to System.Web.Mvc.ScriptViewPage.
  • Points of Interest

    There may be other ways to accomplish the same task, but this was the easiest method to take in order to not have to restructure the current application and be able to organize go forward and have a structure to refactor to for existing scripts. Enjoy.

    
    
    
    
    
    
    						
    推荐.NET配套的通用数据层ORM框架:CYQ.Data 通用数据层框架