Search
Categories
Navigation
Twitter Updates

Twitter Updates

    follow me on Twitter

    Entries in asp.net mvc (3)

    Friday
    Oct232009

    Breaking Bad Habits: Views & Data

    A pile of cigarette butts - Photo by: hmvh (Flickr) The fact that ASP.NET MVC’s default view engine is built on top of Web Forms brings a lot of advantages, key among them being familiarity for existing ASP.NET developers. The bee-sting notation (<%= %>) has been around for quite a while and a lot of the code you put inside it looks pretty familiar to Microsoft’s web developer crowd.

    The drawback to this familiarity is that it’s easy to start using Web Form-based development habits within your views, especially around the sourcing and retrieval of data. This can cause maintainability issues because views are one of the hardest parts of an application to accurately and robustly test. Lots of testing products exist, along with tools like Selenium and WatiN, but it’s hard enough to get applications to look and work consistently across different browsers, let alone build a holistic and resilient UI test suite. That’s why it’s so important to keep your views clean and devoid of all but the simplest logic.

    A common pitfall when moving to MVC is to not rely on the framework’s methods for passing data from controllers to the view. For instance, in Web Forms-based applications, it’s not all that uncommon for ASPX pages to go digging around in session state for data. In an MVC application, however, this is a violation of separation of concerns, as the view now possesses knowledge for where data is being stored (or at least cached), and is bypassing the controller’s sovereign rights to provide the view with all the data that it needs. Remember, the controller’s purpose in life is to orchestrate processing and pass the appropriate data to a view for rendering.

    If the view needs data that’s stored in the session (or elsewhere) it’s the controllers job to retrieve it and provide it through the standard way of transmitting data from controller to view: ViewData. If the view needs a lot of disparate data to perform it’s duties, it probably means you’re in need of a view Danger sign - Photo by: zigazou76 (Flickr)model object that ties all that data together in a format the view can easily work with. The importance of view model objects shouldn’t be overlooked. They are distinct from the domain model classes typically outlined in MVC discussions and are a critical component to building anything more than the simplest MVC application.

    This brings us back to why the Web Forms view engine can be a slippery slope, allowing us to fall into the pit of despair even though its familiarity makes it seem like the pit of success. Since it, like the rest of ASP.NET MVC, is built on top of ASP.NET you can access all those old properties and objects to poke around directly in session, or even the request, to get values and data the view may need. My advice when those temptations arise is to fight the urge, make sure the controller provides your view with all the data it needs and develop as though you don’t have access to such functionality. Your applications will be better off for it.

    This post targets Visual Studio 2008 and ASP.NET MVC 1.0 .The content may not be relevant for other product, framework or language versions.

    Friday
    Oct092009

    Empty ASP.NET MVC 1.0 Project Template

    Empty stadium seats - Photo by: laffy4k (Flickr)The project template that ships with ASP.NET MVC 1.0 is nice when you first start using the framework and want to see how things work together. Unfortunately, it can be annoying once you actually start using the framework for real projects. This is because the first thing you typically need to do is delete all the extra stuff the template creates, but you don’t want in your project.

    After having done this more than a few times, I decided to create my own empty ASP.NET MVC project template with all the stuff I rip out and change already done. This empty MVC template has a few substantive changes:

    • There are no default controllers or views, just folders for their locations.
    • There are no included JavaScript libraries, jQuery or otherwise.
    • All the Web.config settings dealing with ASP.NET Ajax have been removed
    • All the Web.config settings dealing with the profile, role and membership services have been removed
    • The folder structure for images, Javascript and CSS has been changed to Assets/img/, Assets/js/, and Assets/style/, respectively.

    The guiding principle here is that every project tends to be a bit different, so starting from scratch is often easiest. The stock Web.config files have a lot of extra stuff in them that may not be all that relevant for a lot of projects, and I’d rather add stuff back in than have a bunch of unused boilerplate all the time. Plus, I just don’t see much purpose for ASP.NET Ajax at this point in time.

    The image, script and style folder change is just personal preference; I don’t like having Content and Scripts folders like the standard MVC project template does.

    A quick note about testing projects: This template won’t prompt you to create a testing project like the standard MVC template does. This is not because I think unit testing is dumb (quite the contrary) but because I tend to use my own project structure for testing projects as well, so I don’t have a need for it.

    Installation

    I’ve posted my Empty ASP.NET MVC 1.0 project template, if you’re interested in using it.

    If you’ve never installed a custom template before, it’s as easy as copying the zip file to \My Documents\Visual Studio 2008\Templates\ProjectTemplates\Visual C#\. Alternatively, if you want the project to show up under the Web sub-category you can place the zip file in \My Documents\Visual Studio 2008\Templates\ProjectTemplates\Visual C#\Web\.

    After copying the file to the correct location, you should see the new project template when you go to create projects within VS:

    proj-template

    Creation

    If you’d like to have a similar project template, but don’t like some of the changes I’ve made, it’s fairly simple to create your own Visual Studio project templates. In fact, all the documentation I used is available on MSDN:

    Additionally, if you’d like to look at the standard ASP.NET MVC project template (MvcWebApplicationProjectTemplatev1.cs.zip) you can find it installed to <VisualStudioInstallDirectory>\Common7\IDE\ProjectTemplates\CSharp\Web\<Locale>\.

    I’m sure I’ll keep tweaking things about my empty MVC project template (I’m already considering deleting the Models directory since I tend to use a separate assembly for them) but I’m happy with it as a first iteration. It’s a pretty simple process to create one, so if you’ve been annoyed at the stock MVC template, it’s worth the minimal time investment.

    This post targets Visual Studio 2008 and ASP.NET MVC 1.0 using C#. The content may not be relevant for other product, framework or language versions.

    Friday
    Aug142009

    Anonymous Types in ASP.NET MVC

    ASP.NET MVC tries its best to implement some development short-hand semantics that Ruby & Rails made popular and that other libraries (such as jQuery) have also adopted. One such pattern is the use of key-value pairs for method parameters, using defaults if values aren’t provided.

    For example, here’s a snippet from Rails:

    <%= link_to 'About', { :controller => 'help', :action => 'index' } %>

    And one from jQuery:

    $.ajax({
       type: "POST",
       url: "some.php",
       data: "name=John&location=Boston",
       success: function(msg){
         alert( "Data Saved: " + msg );
       }
    });

    MVC tends to use something similar with the Routing system:

    return RedirectToAction("Details", new {id = dinner.DinnerID});

    This kind of approach can make sense when emphasizing convention over configuration, since default values can be supplied by the framework but it’s possible to override values without making the API overly cumbersome. In contrast, the Virtual Earth Bing Maps API uses a more traditional approach that yields calls like this:

    map.Find("", where, null, null, null, null, null, false, null, null, callbackUpdateMapDinners);

    The downside (within a static language like C# at least) is the lack of type-checking and refactoring support. I tend to think that a clean API and the productivity offered through convention over configuration tend to outweigh these negatives in certain situations.

    All that said, what is MVC actually doing when you pass it one of these anonymous objects?

    It’s All About the String Dictionaries

    Since the System.Web.Mvc source is available, it’s easy to see what all the RedirectTo* methods are doing with these option objects. They all pass them to the RouteValueDictionary(object) constructor, like so:

    protected internal RedirectToRouteResult RedirectToAction(string actionName, object routeValues) {
        return RedirectToAction(actionName, new RouteValueDictionary(routeValues));
    }

    RouteValueDictionary lives in the System.Web.Routing assembly, whose source has not been made available, so to continue our spelunking we’ll need to fire up Red Gate’s .Net Reflector.Red Gate .NET Reflector logo

    A quick aside about .NET Reflector: It’s a fairly well-known tool, but I’m always surprised at how many people do not know about it or simply never use it. This is as crazy as the many web developers who don’t use Firebug. Reflector is an essential tool when learning how .NET libraries work as well as trouble-shooting them. I’ve solved several tricky production problems using nothing but Reflector. More .NET developers should use it!

    If you haven’t loaded the MVC libraries into Reflector, you’ll need to do so before continuing. Assuming you’ve installed MVC, you’ll find the assemblies in the following locations. You can drag & drop the DLLs from Explorer into Reflector to load them:

    • System.Web.Abstractions: [Program Files]\Reference Assemblies\Microsoft\Framework\v3.5\System.Web.Abstractions.dll
    • System.Web.Mvc: [Program Files]\Microsoft ASP.NET\ASP.NET MVC 1.0\Assemblies\System.Web.Mvc.dll
    • System.Web.Routing: [Program Files]\Reference Assemblies\Microsoft\Framework\v3.5\System.Web.Routing.dll

    After loading the System.Web.Routing assembly into Reflector, we can search for the RouteValueDictionary class and find the constructor that accepts an object:

    public RouteValueDictionary(object values)
    {
        this._dictionary = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
        this.AddValues(values);
    }

    The AddValues(object) method is performing the heavy lifting:

    private void AddValues(object values)
    {
        if (values != null)
        {
            foreach (PropertyDescriptor descriptor in TypeDescriptor.GetProperties(values))
            {
                object obj2 = descriptor.GetValue(values);
                this.Add(descriptor.Name, obj2);
            }
        }
    }

    As you can see, RouteValueDictionary is using reflection to transform this object into a Dictionary<string, object> with the property name used as the key. This dictionary is then used by the rest of the framework to execute and resolve routing.

    Supporting Convention Over Configuration

    Such an implementation makes it easy for developers to pass a wide range of data into methods without the specific data elements being known at design time. It’s also possible to rely on default values (such as routing’s default controller and action values). System.Web.Mvc does this with RouteValueDictionary in the RouteValuesHelpers.MergeRouteValues() method:

    public static RouteValueDictionary MergeRouteValues(string actionName, string controllerName, RouteValueDictionary implicitRouteValues, RouteValueDictionary routeValues, bool includeImplicitMvcValues) {
        // Create a new dictionary containing implicit and auto-generated values
        RouteValueDictionary mergedRouteValues = new RouteValueDictionary();
    
        if (includeImplicitMvcValues) {
            // We only include MVC-specific values like 'controller' and 'action' if we are generating an action link.
            // If we are generating a route link [as to MapRoute("Foo", "any/url", new { controller = ... })], including
            // the current controller name will cause the route match to fail if the current controller is not the same
            // as the destination controller.
    
            object implicitValue;
            if (implicitRouteValues != null && implicitRouteValues.TryGetValue("action", out implicitValue)) {
                mergedRouteValues["action"] = implicitValue;
            }
    
            if (implicitRouteValues != null && implicitRouteValues.TryGetValue("controller", out implicitValue)) {
                mergedRouteValues["controller"] = implicitValue;
            }
        }
    
        // Merge values from the user's dictionary/object
        if (routeValues != null) {
            foreach (KeyValuePair<string, object> routeElement in GetRouteValues(routeValues)) {
                mergedRouteValues[routeElement.Key] = routeElement.Value;
            }
        }
    
        // Merge explicit parameters when not null
        if (actionName != null) {
            mergedRouteValues["action"] = actionName;
        }
    
        if (controllerName != null) {
            mergedRouteValues["controller"] = controllerName;
        }
    
        return mergedRouteValues;
    }

    jQuery also provides similar, but more flexible, functionality with the jQuery.extend() function. These kind of merge functions are what allows this pattern to support convention over configuration so well.

    Evil or Not?

    Swimmer on beach with dangerous current; Photo by: owenfm (Flickr) There’s some negative commentary on this aspect of MVC, which often boils down to citing the magic string anti-pattern.

    I’m a bit less dogmatic about this point, seeing it as an approach that has drawbacks, like most do. While it’s true that it does hamper static refactoring tools and type checking, it also allows for a more productive developer experience. It seems to boil down to aesthetic preferences between those that prefer static type checking (despite its inherent verbosity) and those that prefer a more dynamic language approach that relies on unit testing to overcome the deficiencies.

    In other words, it’s just another tool for your toolbox and nothing more.