- | rssFeed | My book on MSBuild and Team Build | Archives and Categories Tuesday, April 20, 2010

ASP.NET MVC a better TagBuilder

The other day I was working on a site which required a pager, so I searched around a bit and I a pagerby Gunnar Peipman that looked promising. I found a few others but decided against them. Some of them loaded all of the data into memory and then paged from there, and others just flat out didn't work! In any case I had a good experience with Gunnar's. I wanted to take what Gunnar had and create a view helper using my custom view helpers. Along the way I found myself writing some code using the TagBuilderclass that just didn't sit well with me. Take a look at the snippet below which.
TagBuilder startTag = new TagBuilder("a");
startTag.Attributes.Add("href", string.Format("{0}/{1}", this.UrlPrefix, 1));
startTag.SetInnerText("<<");
startTag.Attributes.Add("title", "first page");
httpResponse.Write(startTag.ToString(TagRenderMode.Normal));

TagBuilder previous = new TagBuilder("a");
previous.Attributes.Add("href", string.Format("{0}/{1}", this.UrlPrefix, this.CurrentPage - 1));
previous.SetInnerText("<");
previous.Attributes.Add("title", "previous page");
httpResponse.Write(previous.ToString(TagRenderMode.Normal));
I didn't like the fact that I had to make a bunch of calls to the tag builder to build the HTML for me, it was just uglier than what I wanted. So I decided to create a new tag builder which places an Fluent interface on top of it (ok, maybe its just method chaining). The end result was the FluentTagBuilderclass. I couldn't extend TagBuilderbecause I wanted to change the return types, so instead I created the class to just contain one and to just pass the calls through to it. What I did was to declare all the same properties and methods that the TagBuilder had, but just change the ones who returned void to return the same object itself. So for example I created methods like.
public FluentTagBuilder AddCssClass(string value)
{
    this.TagBuilder.AddCssClass(value);
    return this;
}

public FluentTagBuilder SetInnerHtml(string innerHtml)
{
    this.TagBuilder.InnerHtml = innerHtml;
    return this;
}
With this I can chain different method calls together and create code which looks better. If you've used jQuerythen you are used to this. With this in place I was able to convert the snippet above into the following.
FluentTagBuilder startTag =
    new FluentTagBuilder("a")
    .AddAttribute("href", string.Format("{0}/{1}", this.UrlPrefix, 1))
    .SetInnerText("<<")
    .AddAttribute("title", "first page");

httpResponse.Write(startTag.ToString(TagRenderMode.Normal));

FluentTagBuilder previous =
    new FluentTagBuilder("a")
    .AddAttribute("href", string.Format("{0}/{1}", this.UrlPrefix, this.CurrentPage - 1))
    .SetInnerText("<")
    .AddAttribute("title", "previous page");

httpResponse.Write(previous.ToString(TagRenderMode.Normal));
To me this is a lot easier to read, and to create. If you agree you can grab the class and include it in your projects. Links to full source files are below.
  1. FluentTagBuilder.cs
  2. Pager.cs
Sayed Ibrahim Hashimi
ASP.NET MVC | Fluent interface | View helpers Tuesday, April 20, 2010 5:10:14 AM (GMT Daylight Time, UTC+01:00)  #     | 
Friday, April 02, 2010

ASP.NET MVC Route + Ajax + jQuery

The story around routing in ASP.NET MVC is pretty good. When a resource is requested the action that it is routed to is determined and the parameters to the action are initialized. Counter to that when you build a link you specify the argument values and it creates the url for you. So if your routes happen to change your application shouldn’t be bothered by that. One area which can be problematic for this is if you are making any ajax requests. What I’ve done to minimize the impact of route changes to Ajax requests is to have the script make a call to determine the correct route. In the most trivial case this is extremely easy. You call an action, which has a known route, passing into it the name of the action and controller. It returns to you the url for the route. This works really good, but the problem is when your routes have parameters, how do you handle those?

First let’s take a look at the Javascript and then the definition of the action method which it calls.

// wire up the event for the enter button
$("#searchText").keypress(function (event) {
    if (event.keyCode == 13) {
        // grab the text that is inside the box
        var text = $("#searchText").val();

        var routData = {
            controllerName: 'Search',
            actionName: 'Search',
            paramValues: "{ criteria: '" + text + "' }"
        };

        $.ajax({
            url: "/Home/MapRoute",
            data: routData,
            success: function (data) {
                window.location.href = data;
            },
            error: function (data) {
                alert('there was a failure on the internets');
            }
        });

    }
});

Here I am building a Javascript object named routeData. This is declared in JSON. One thing to pay attention to is the fact that the value for paramValues is a string containing JSON. This is passed to the controller action.

I’m using jQuery to make an Ajax request to /Home/MapRoute. This maps to the method shown below.

public JsonResult MapRoute(string actionName, string controllerName, string paramValues)
{
    JavaScriptSerializer jss = new JavaScriptSerializer();

    Dictionary<string, string> parameters = jss.Deserialize<Dictionary<string, string>>(paramValues);

    RouteValueDictionary rd = new RouteValueDictionary();
    foreach (string key in parameters.Keys)
    {
        rd.Add(key, parameters[key]);
    }

    UrlHelper urlHelper = new UrlHelper(this.Request.RequestContext);
    string url = urlHelper.Action(actionName, controllerName, rd);

    return Json(url, JsonRequestBehavior.AllowGet);
}

Here I’m using the JavaScriptSerializer to convert the JSON string into a dictionary, with string as key and value. I use that dictionary to create a RouteValueDictionary which is passed, along with other parameters, into the UrlHelper to generate the url. When you return the Json result you must specify JsonRequestBehavior.AllowGet, otherwise a 500 internal service error will be returned. I think this is new with ASP.NET 2.

When the action method returns, you can use that url. The drawback to this approach is that you will make an extra request to determine the url, but you will be sure that those urls are correct. Also you could cache the results with Output Caching since the routes won’t change.

Sayed Ibrahim Hashimi

ASP.NET MVC | Javascript | jQuery | routing Friday, April 02, 2010 6:24:30 AM (GMT Daylight Time, UTC+01:00)  #     | 
Wednesday, December 09, 2009

Unit testing and ASP.NET MVC Comments

Unit testing has been around for quite a while and a lot of developers have created their own unit tests. Even being a simple concept and having been around for some time I still think that unit tests are misunderstood, poorly written and don't follow best practices by a majority of developers. I'm not a unit testing expert but there are some guidelines that I try to follow when I create unit tests some of those are listed below (in no particular order).

If you make your unit tests too long and to test more than one item then they will become a pain to maintain, which leads to them being ignored and eventually to a complete breakdown to your testing effort in general.

I have recently been thinking more about how unit tests are being used throughout software development. Also I've been reading ASP.NET MVC in Action which coincidently puts a lot of emphasis on unit testing. I found a few quotes in Chapter 3 of that book, which covers Controllers, and thought that I would share them here because I agree with statements whole heartedly. Text in italics are my own comments.

Some of the key take aways: use mocks, and testing should be helpful to your development process not a hindrance.

Don't write unit tests just to write them or just to get the necessary coverage. Make sure that you write the test cases with a specific intent and that they are written well so that you will continue to maintain and execute them as a part of your CI build process.

Here are some unit tests that I've recently written that I'd like to use to show some concrete cases.

[Test]

public void TestGetConfigValue_ValueExistsRequiredTrue()

{

    string key = "83F216CD-1B3E-4fc1-9DA5-4A7D506AF7E8";

    string expectedValue = "C2D4E7CA-BE6B-4976-BFA9-50F5223C603A";

    ConfigurationManager.AppSettings[key] = expectedValue;

 

    string actualValue = ServiceConfigHelper.Instance.GetConfigValue(key, true);

    Assert.AreEqual(expectedValue, actualValue);

}

 

[Test]

public void TestGetConfigValue_ValueExistsRequiredFalse()

{

    string key = "0601AB1F-C78C-4c72-8648-B140D50BDDEC";

    string expectedValue = "92DCADBC-9618-49f3-A412-5C90A945903D";

    ConfigurationManager.AppSettings[key] = expectedValue;

 

    string actualValue = ServiceConfigHelper.Instance.GetConfigValue(key, false);

    Assert.AreEqual(expectedValue, actualValue);

}

 

[Test]

public void TestGetConfigValue_ValueDoesntExistRequiredFalse()

{

    string key = "2D4B5C66-BA37-4ab8-9B51-3F17CF348E6D";

 

    string actualValue = ServiceConfigHelper.Instance.GetConfigValue(key, false);

    Assert.IsNull(actualValue);

}

 

[ExpectedException(typeof(ConfigurationErrorsException))]

[Test]

public void TestGetConfigValue_ValueDoesntExistRequiredTrue()

{

    string key = "0511216E-3AA6-4b80-B215-E980A459466D";

 

    // ConfigurationErrorsException here

    string actualValue = ServiceConfigHelper.Instance.GetConfigValue(key, true);

}

 

[ExpectedException(typeof(ArgumentNullException))]

[Test]

public void TestGetConfigValue_KeyIsNull_RequiredFalse()

{

    string actualValue = ServiceConfigHelper.Instance.GetConfigValue(null, false);

}

 

[ExpectedException(typeof(ArgumentNullException))]

[Test]

public void TestGetConfigValue_KeyIsNull_RequiredTrue()

{

    string actualValue = ServiceConfigHelper.Instance.GetConfigValue(null, true);

}

 

// Setup method here

[SetUp]

public void Reset()

{

    ServiceConfigHelper.Instance.Reset();

    if (ConfigurationManager.AppSettings.HasKeys())

    {

        List<string> allKeys = new List<string>();

        foreach (string key in ConfigurationManager.AppSettings.Keys)

        {

            allKeys.Add(key);

        }

 

        foreach (string key in allKeys)

        {

            ConfigurationManager.AppSettings[key] = string.Empty;

        }

    }

}

Notice that each of these has a very descriptive name, if you saw the name of the test in the list of failed test cases you immediately know what to work on; you don't even have to look at the test. Notice that all of the tests are pretty small, and that each test case tests a very specific piece of functionality. Also you might have noticed that I use Guids in test cases I like to do this just to make sure that I have random data and that test cases don't conflict with each other.

Also I'd like to note that in the book the guys recommend reading Working Effectively with Legacy Code for good coverage of unit testing, I haven't read it but I might grab a copy.

 

Sayed Ibrahim Hashimi

ASP.NET MVC | unit testing Wednesday, December 09, 2009 6:37:17 AM (GMT Standard Time, UTC+00:00)  #     | 
Saturday, November 07, 2009

ASP.NET MVC Sudoku Solver

A while back I had a session on MSDeploy where I demonstrated deploying an ASP.NET MVC Sudoku solver that I wrote a while back. A few people asked me to publish it, so I did you can get it at http://sudokumvc.codeplex.com/

 

Sayed Ibrahim Hashimi

ASP.NET MVC | Codeplex Saturday, November 07, 2009 9:51:27 AM (GMT Standard Time, UTC+00:00)  #     | 

ASP.NET MVC View Helpers

Are you using ASP.NET MVC? If so you may be interested in an article that I've just made available on Codeplex published to ASP.NET MVC Custom View Helpers. View helpers are usages of extension methods to insert markup into your pages. For example <%= Html.TextBox("username") %> and <%= Html.ValidationMessage("username") %> are both using view helpers. These are really just extension methods on the HtmlHelper class. There are a few different ways that you can create your own custom view helpers. I examine the different approaches, discuss the current implementation of the view helpers shipped with ASP.NET MVC itself, and I provide some helper classes for you to create your own custom view helpers.

ASP.NET MVC Custom View Helpers

Sayed Ibrahim Hashimi

 

 

ASP.NET MVC | Codeplex | Writing Saturday, November 07, 2009 8:55:15 AM (GMT Standard Time, UTC+00:00)  #     | 
Monday, September 14, 2009

ASP.NET MVC: Build Views

If you are using ASP.NET MVC you might have noticed that you will not be alerted of build errors that exist in your views until you navigate to those pages. When you do so you'll get a runtime error L Thankfully there is a very easy way to change this. All you have to do is open the project file and change the value for the MvcBuildViews property to true. To do this, follow these steps.

  1. Right-click on the MVC project and select 'Unload Project'
  2. Right-click on the unloaded MVC project and select 'Edit …'. This will open up the project file itself, which of course is an MSBuild file.
  3. Look for the MvcBuildView property and set its value to true. It should be <MvcBuildViews>true</MvcBuildViews>
  4. Save the project file, right-click on the MVC project again and select 'Reload project'

After this if you introduce any build errors into the views then you will be notified when the project is built.

Sayed Ibrahim Hashimi

ASP.NET MVC | msbuild Monday, September 14, 2009 5:37:16 AM (GMT Daylight Time, UTC+01:00)  #     | 
Friday, September 04, 2009

Speaking at Tallahassee Code Camp

This weekend I will be speaking at the Tallahassee Code Camp on Saturday September 5, 2009. I will be presenting two sessions which are:

ASP.NET Custom View Helpers

If you have been using ASP.NET MVC then you certainly have been using some of the built in
view helper methods that are available, you know those expressions like
Html.TextBox("textBoxName")
and Html.ValidationMessage("Required").
View helpers are nothing more than extension methods which create HTML that is injected into
your views based on the method and its parameters. Creating your own view helpers is very
simple and can be extremely beneficial. By writing your own custom view helpers you will
benefit in at least the following ways

  • Simplifies Your Views
  • Easies Rehydrating HTML Elements with ModelState Values
  • Standardizes the Creation of Common HTML Components
  • Helps you Implement the DRY (Don't Repeat Yourself) Principal

We will take an in depth look at how you can easily and effectively create your own view helpers. We will also discuss how the default view helpers were created and the benefits that they provide.

Utilizing Web Deployment Projects

In this session we will take a look at how Web Deployment Projects can be used to assist in the deployment of web sites and web applications; including ASP.NET Web Applications and ASP.NET MVC Web Applications. We will give an overview of what Web Deployment Projects are and the functionality that is available out of the box. A Web Deployment Project is a wrapper for the aspnet_compiler.exe tool in the form of an MSBuild project and adds value to using the tool itself. Because they are MSBuild files we are able to customize and extend the process. We will discuss how we can customize the process to perform common steps such as

  1. Creating Virtual Directories
  2. Updating values in the web.config file
  3. Encrypting the web.config file
  4. Minimizing JavaScript files
  5. Versioning the Assemblies

 

If you are in the area and interested in these topics make sure to drop in!

Sayed Ibrahim Hashimi

ASP.NET MVC | msbuild | speaking | Web Deployment Projects Friday, September 04, 2009 5:40:20 AM (GMT Daylight Time, UTC+01:00)  #     | 
Tuesday, June 02, 2009

Speaking at Jaxdug Wednesday June 3, 2009

This coming Wednesday I will be at the Jacksonville Developer User Group. The topic title is "Leveraging Web Deployment Projects". I am still thinking about a new title, I'm not extremely happy about that one. Here is the description about the talk.

In this session we will take a look at how Web Deployment Projects can be used to assist in the deployment of web sites and web applications; including ASP.NET Web Applications and ASP.NET MVC Web Applications. We will give an overview of what Web Deployment Projects are and the functionality that is available out of the box. A Web Deployment Project is a wrapper for the aspnet_compiler.exe tool in the form of an MSBuild project and adds value to using the tool itself. Because they are MSBuild files we are able to customize and extend the process. We will discuss how we can customize the process to perform common steps such as

  1. Creating Virtual Directories
  2. Updating values in the web.config file
  3. Encrypting the web.config file
  4. Minimizing JavaScript files
  5. Versioning the Assemblies

In this session we will not be covering MSBuild itself, so I will not go into too much detail about MSBuild specifics. More to be discussed is how you can take advantage of Web Deployment Projects and how that build process can be extended and customized.

 

Sayed Ibrahim Hashimi

ASP.NET MVC | msbuild | Visual Studio 2008 | speaking Tuesday, June 02, 2009 4:25:06 AM (GMT Daylight Time, UTC+01:00)  #     | 
Monday, May 18, 2009

ASP.NET MVC: Validation Error ‘A value is required.’

If you are using the ASP.NET MVC DefaultModelBinder to manage the creating complex types from your views' form data, as well as the validation summary helper provided with ASP.NET MVC then you may have run into the situation I did. I created a simple form, in a dummy app, to create a new contact. The page is named AddContactClassic.aspx, the contents are shown below.

<% @ Page Title ="" Language ="C#" MasterPageFile ="~/Views/Shared/Site.Master" Inherits ="System.Web.Mvc.ViewPage" %>

 

<% @ Import Namespace ="Sedodream.Web.Common.Contact" %>

 

< asp : Content ID ="Content1" ContentPlaceHolderID ="TitleContent" runat ="server">

      Add Contact Classic

</ asp : Content >

 

< asp : Content ID ="Content2" ContentPlaceHolderID ="MainContent" runat ="server">

    < h2 > Add Contact Classic </ h2 >

   

    <% = Html.ValidationSummary( "Errors exist" ) %>

   

    < ol >< li >< span class ="success-message"> <% = ViewData[ "SuccessMessage" ]%> </ span ></ li ></ ol >

    <% using (Html.BeginForm())

       { %>

    < fieldset >

        < legend > Account Information </ legend >

        < ol >

            < li >

                < label for ="FirstName"> First name </ label >

                <% = Html.TextBox( "FirstName" ) %>

                <% = Html.ValidationMessage( "FirstName" , "*" ) %>

            </ li >

            < li >

                < label for ="LastName"> Last name </ label >

                <% = Html.TextBox( "LastName" ) %>

                <% = Html.ValidationMessage( "LastName" , "*" ) %>

            </ li >

            < li >

                < label for ="Email"> Email </ label >

                <% = Html.TextBox( "Email" )%>

                <% = Html.ValidationMessage( "Email" , "*" )%>

            </ li >

            < li >

                < label for ="Phone"> Phone </ label >

                <% = Html.TextBox( "Phone" )%>

                <% = Html.ValidationMessage( "Phone" , "*" )%>

            </ li >

            < li >

                < div class ="option-group" id ="Gender">

                    <% = Html.RadioButton( "Gender" , Gender .Male.ToString())%> < span > <% = Gender .Male.ToString() %> </ span >

                    <% = Html.RadioButton( "Gender" , Gender .Female.ToString())%> < span > <% = Gender .Female.ToString() %> </ span >

                </ div >

            </ li >            

            < li >

                < input type ="submit" value ="Add contact" />

            </ li >

        </ ol >

    </ fieldset >

    <% } %>

 

</ asp : Content >

In my ContactController class the following methods are defined.

public ActionResult AddContactClassic()

{

    return View();

}

[ AcceptVerbs ( HttpVerbs .Post)]

public ActionResult AddContactClassic( Contact contact)

{

    if (contact == null ) { throw new ArgumentNullException ( "contact" ); }

 

    InternalAddContact(contact);

 

    return View();

}

When I ran the app and filled in all the values on the AddContactClassic page I was a bit surprised to see an error simply stating "A value is required." Here is a screen shot.

So I assumed that I must have misspelled one of the names of the fields that were passed to the Html.TextBox method. Obviously this was not the issue, so then I remembered that the Contact class that I defined had another property, Id, which I was not contained in a field on the form. This is the case because this page is supposed to create a new Contact, so its Id will not be set. I changed the AddContactClassic(Contact) method to ignore the Id property when binding was occurring. Here is the new method.

[ AcceptVerbs ( HttpVerbs .Post)]

public ActionResult AddContactClassic( [ Bind (Exclude= "Id" )] Contact contact)

{

    if (contact == null ) { throw new ArgumentNullException ( "contact" ); }

 

    InternalAddContact(contact);

 

    return View();

}

By using the Bind attribute I was able to let the DefaultModelBinder know that I was not interested in getting the value for that field. Once I made this change everything worked fine.

In the Contact class I had incorrectly defined the Id property to be Guid instead of Guid? which is better. If I had correctly declared that then the DefaultModelBinder would have known that it was not a required field and it would not have complained. But if you are using the Entity Framework, you may still have this issue anyway because it does not always create nullable fields for all nullable columns in my experience. Even after changing the Contact class to use Guid? I have purposefully left the Bind(Exclude="Id") on the method in case something changes in the implementation of the Contact class.

Sayed Ibrahim Hashimi

ASP.NET MVC Monday, May 18, 2009 4:24:19 AM (GMT Daylight Time, UTC+01:00)  #     | 
Thursday, April 23, 2009

ASP.NET MVC: Using Entity Objects in Views

If you are you using ASP.NET MVC and the Entity Framework then odds are that you will want to use some of your entity object inside of your views. If you've tried this you may have run into the following exception: "CS0234: The type or namespace name 'Entity' does not exist in the namespace 'System.Data' (are you missing an assembly reference?)".


So the first guess is to add a reference to System.Data.Entity assembly in the MVC project. I did that then ran my app again, and I encountered the same error. After some poking around I figured out that you need to manually add the assembly to the web.config file. So in your web.config file you need to add the element

<add assembly="System.Data.Entity, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />

Under the compiliation\assembiles node, here it is:

Once you've done this you should be able to run your app with no problems.

 

Sayed Ibrahim Hashimi

ASP.NET MVC | Entity Framework Thursday, April 23, 2009 7:18:20 PM (GMT Daylight Time, UTC+01:00)  #     |