Testing code by precompiling view pages in asp.net mvc#

I am loving to use ASP.NET MVC at work and have several projects that we have live right now.  We are also migrating our web forms app very slowly by adding more mvc like views in our web forms so they can be ported easier down the road.  The one issue I am having so far is testing the code that is in the pages for the mvc apps and the code in the web form views.  Runtime errors are so annoying and when on a decent size team, it is almost impossible to test every page at runtime, especially when you don't always know what another developer added or changed. 

So I wrote a test that precompiles the entire app and all the code in the aspx page.  It is more of a sanity check than anything and has been very helpful.  It has caught several simple errors that would have ended up in production and am very thankful for having this sanity test.  Here is the sample test code that you can add to your test project:

        const string ASPNET_COMPILER_PATH = "C:/Windows/Microsoft.NET/Framework/v2.0.50727/aspnet_compiler -p {0} -v {1}";
        const string TERMINAL_APP_NAME = "cmd.exe";

        [TestMethod]
        public void Precompile_all_pages()
        {
            string sitePath = @"D:\websites\mywebsite.com\preview";
            string siteNode = "preview.mywebsite.com";

            string ret = compileProject(sitePath, siteNode);

            string msg = "Utility to precompile an ASP.NET application\r\nCopyright (C) Microsoft Corporation. All rights reserved.\r\n\r\n";
            Assert.AreEqual(msg, ret);


        }

        public string compileProject(string compileDir, string iisApplicationPath)
        {
            string compileString = string.Format(ASPNET_COMPILER_PATH, compileDir, iisApplicationPath);

            Process proc = new Process();
            proc.StartInfo.UseShellExecute = false;
            proc.StartInfo.RedirectStandardOutput = true;
            proc.StartInfo.FileName = TERMINAL_APP_NAME; //File name to execute
            proc.StartInfo.Arguments = "/c " + compileString;
            proc.StartInfo.RedirectStandardError = true;
            proc.StartInfo.RedirectStandardInput = true;
            proc.Start(); // Execute process
            proc.WaitForExit();

            string output = proc.StandardOutput.ReadToEnd();

            return output;
        }
Make sure to reference System.Diagnostics.  You can also add the aspnet_compiler to your post build in your cproj file, although this will slow down your building of your app locally, but is a good practice I think for working with mvc.
Saturday, August 30, 2008 8:12:47 AM UTC #    Comments [0]  |  Trackback

 

Ajax Control Toolkit with ASP.NET MVC#

I have been playing with ASP.NET MVC for about 4 months now and have a couple sites, one decent sized, in production right now.  I am very comfortable with it and the extra control over the html is very nice.  Since there is no postback and viewstate in MVC, the Ajax Control Toolkit does not work as designed and needs a few changes to make it work within the MVC framework.  It has forced me to look at jQuery more, and I am really enjoying the simplicity of jQuery's design and the power of using it.  I was just wishing I could use some of the controls in the toolkit and of course i wanted to see if i could get it to work for the sake of making it work.  Well I did get it to work for 2 of the controls so far, some that i use in a normal project.  They are the Calendar and TextBoxWatermark. 

To make this easier, and from a lesson I learned with jQuery, it is best to select your DOM elements by a css class when you can.  This allows for your JavaScript to be more unobtrusive, and ultimately cleaner and easier to maintain.  So i took a couple of methods from the Ajax Futures from last July 2007 and put it in a helper class along with a simple function of mine to allow selection of DOM elements by css very trivial.

To give you an idea of what i am talking about using css selectors for the toolkit, look at this sample:

var elements = $select('TextboxWatermark');
    for (i=0;i<elements.length;i++)
    {
        var elem = elements[i];
        $create(AjaxControlToolkit.TextBoxWatermarkBehavior, {"WatermarkText":elem.title}, null, null, $get(elem.id));
    }

 

I created a helper function $select(className) which basically wraps Microsoft Ajax Futures getElementsByClassName like such:

function $select(className)
{
    var elements = Sys.UI.DomElement.getElementsByClassName(className);
    return elements;
}

So basically if you put the class name TextboxWatermark on any textbox input with the title you like to show as the watermark like this:

<input type="text" class"TextboxWatermark" name="username" id="username" title="Must be at least 6 chars" />

or

<%= Html.TextBox("username", new { @class = "TextboxWatermark", @title = "Must be at least 6 chars" }) %>

What is nice about the second option is that you get the added benefit of getting the View Engine to fill out the value of the textbox if there is an item in ViewData of the ViewData.Model that has a var named 'username'.

So I find this much easier than having to add the control and on each page and for each DOM element i want to attach this behavior to. 

When you want to do the same thing with other controls, you need to know how to call it in JavaScript.  This is the part that is kind of hidden when you are using controls like in WebForms.  If you go to the AjaxControlToolkit Sample Site, and view page source on one of the controls example pages, you will see what JavaScript is generated.  Go to the TextboxWatermark example and then view source from the browser.  You will see 5 of these at the bottom for each control

Sys.Application.initialize(); Sys.Application.add_init(function() { $create(AjaxControlToolkit.TextBoxWatermarkBehavior, {"ClientStateFieldID":"ctl00_SampleContent_TextBoxWatermarkExtender1_ClientState","WatermarkCssClass":"watermarked","WatermarkText":"Type First Name Here","id":"ctl00_SampleContent_TextBoxWatermarkExtender1"}, null, null, $get("ctl00_SampleContent_TextBox1")); });

So this is how you can see how to invoke that control with the arguments, and then of course looking at the source files help you see what else they are doing.  I am already feeling cleaner with the JavaScript since i have just one function that now does it for every Dom element with the class name, "TextboxWatermark".  Here is the function that gets called on pageLoad.  This will attach the behavior for each Dom element.

function AddAllTextboxWatermarks()
{
    var elements = $select('TextboxWatermark');
    for (i=0;i<elements.length;i++)
    {
        AddTextBoxWatermark(elements[i]);
    }
}

function AddTextBoxWatermark(elem)
{
    $create(AjaxControlToolkit.TextBoxWatermarkBehavior, {"WatermarkText":elem.title}, null, null, $get(elem.id));
}

Now the Calendar was just as easy to put in the form.  Here is how I implemented the Calendar:

<%= Html.TextBox("calendar", new { @class = "Calendar" })%>

Now, the calendar was a little tougher to get to work cause of getting the JavaScript just right cause of the amount of files needed.  The Calendar is one of the more complex controls since it uses a popup behavior, animation, timer and threading among others. 

So, that solves simple controls, but what about other controls that need overloads and are not as simple parameter wise.  Well I put all these controls in html helpers also that inject the JavaScript at run time.  I actually started this way, but after using jQuery a little, i really wanted to see if i could implement this using css selectors with the asp.net Ajax framework.  Also it felt like black box controls were creeping back in, but sometimes we need to generate html in c#, so i think it is just about finding the fine line when not to do it and when it is simple and reusable.

Here this an example of the TextboxWatermark as a Html helper:

<%= Html.TextboxWatermark("Enter Username", "username", true) %>

So basically this returns you the input text box,  with the TextboxWatermark behavior attached.  Also it will make sure that the appropriate .js files are in the page along with the html needed to make it work with this particular control.  Now this creates a lot more code in the page just like the controls now do in the existing toolkit, but has its own advantages.  We can create overloads and add additional parameters without having to write JavaScript.  Some people actually don't like to write JavaScript, it is either intimidation or fear of messing up something I guess.  Personally I have used it since the late 90's and is the only language that I have used for 10 years.  Well that and SQL. 

The Calendar control has advantages to being used as a Html helper since we can have several overloads to define format among other parameters that a calendar control can take.  Here is how you set the format:

<%= Html.Calendar("bDay", "MM/dd/yyyy") %>

Also lets say you want a button to invoke the menu, not just clicking in the textbox.

 <%= Html.Calendar("calBtn", "txtDate", "MM/dd/yyyy")%>
 <input type="text" id="txtDate" name="txtDate" />
 <input type="button" id="calBtn" name="calBtn" value="Pick Date" />

Here is a download of a working project with all the necessary files you need to make these controls work either with CSS Selectors or with Html helpers.  I basically created a new mvc web app from scratch using their new templates in Preview 4 that has the membership stuff in it, so that it works with the base starter project that Microsoft provides. 

Download: http://www.jimzimmerman.com/downloads/MvcPreview4AjaxToolkit.zip

Tell me what you think.  I hope you find it useful.  I am leaning towards doing everything with CSS Selectors, but am finding it pretty helpful to use Html helpers also.  I am working on Collapsible Panel and Popup Control next.  I know the Microsoft Ajax team is working on some things with the Toolkit also and look forward to seeing how they integrate it.

Saturday, August 16, 2008 6:27:40 AM UTC #    Comments [1]  |  Trackback

 

 

All content © 2008, Jim Zimmerman
Book
New Book
Links to me
On this page
Sponsors
Calendar
<November 2008>
SunMonTueWedThuFriSat
2627282930311
2345678
9101112131415
16171819202122
23242526272829
30123456
Archives
Tags
Blogroll OPML
Technorati
Favorite Links
Disclaimer

Powered by: newtelligence dasBlog 1.9.6264.0

The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.

Send mail to the author(s) E-mail