5 Jul
2010

Asp.Net MVC: My Personal View Rules

Category:UncategorizedTag: :

I’ve been working with a team of guys on several Asp.Net MVC projects since October of 2009.  While that isn’t the greatest amount of time, and I’m still no expert, I thought I’d jot down a few of the practices that we have developed to help make coding a bit smoother.  Asp.Net MVC, as with every new technology can be used poorly, and when use poorly you try to identify why that code was bad, how it could have been done better.

First, lets think about what the view should be doing – in a single responsibility sort of way: turn data into html.  That right there rules out several options.  No retrieving data, no extra data transformations.  Just turn some data into html.  And frankly, that is complicated enough.  So a side goal that I strive for is to create a markup page (the aspx) that is similar to the desired html output.  The main reason for that side goal is to make double checking the output that much easier.  I want to see a ‘div’ in my markup, and have a reasonable idea where that ‘div’ will show up in the html.

  1. Keep as much code out of your views as you can.

Don’t make this rule overly simplistic.  Some code belongs in the view.  A ‘for’ loop to create a table, a simple ‘if’ block to show administrator functionality, stuff like that.  But you shouldn’t be having to specify the DateTime format, or string parsing.  That is what the ViewModels should do for you.  Rule of thumb, if you see a block where there is more C# than HTML, you probably did it wrong.

I will also extend this rule to JavaScript as well.  I’ve talked about the why JavaScript should not be in the views before, so this should not be a shock.  JavaScript belong in separate files.  Period.

2. Make Views typed.

This is true for all views where you have to pass data from the controller to the view.  Make a View Model for the view and pass data via that model.  This opens up a whole host of better patterns for you, like typed HtmlHelpers.  As a result, it is VERY rare that I will share a view model between views, or even Controller Actions.  I make separate models for GET, POST, and DELETE.  I guess my view is, the more the merrier.

3.   Make the View Models specific to the needs of the view.

OK, this isn’t actually a View best practice, but it is highly related.  If you try to keep the model for the view too generic, you end up with a lot of logic in the view to transpose the data into something useful.  The key point is that the data in the model serves the view, so all of the work to get the data into the correct format should be done when putting the data into  the model.  I will often take this to the point where the model will give html elements in the view their CSS classes.  So that means I have more than data from the database in the views.

Side note: when it comes to populating View Models with data specific for the View, AutoMapper rocks!  That is all.

4.  Custom Html Helpers are wonderful things

It is remarkably simple to create your own Html Helper, and once you get the hang of them they are beautiful.  They are wonderful little ways to encapsulate a small amount of logic so you can get it out of the aspx view.  Use them to encapsulate small amounts of code you need in various place through the project.

Another little “trick” I will use from time to time to create custom models just for a html helper (passed in via the view’s view model).  I have a few places where I need to change the markup because of the browser being used…so I create a custom helper that can detect the browser.

5. Standard HTML Helpers are great, but remember html

The key point I’m trying to make here is to become familiar with the output of the standard HTML helpers.  While the helpers can be great, they have their warts (anything having to do with an attribute name/value is a bit ugly).  Sometimes it is easier to swap them out with the standard html (especially with inputs) to get the exact output you want.  As a bonus, it is easier for the next guy coming in to figure out what you were after.   Currently, I’d say I use the helpers about 50% of the time over raw HTML.

Now, typing the html, or the helper, still kind of stinks.  You have to type the same code over and over.  Take a look at what Zen-Coding does.  You can do the same thing with ReSharper Templates or Visual Studio snippets…or just install one of the ReSharper or Visual Studio pluggins.  But beyond that, there is an art to customizing Visual Studio that you should learn.

6. Wrap all links in Url.Content and Url.Action

You have a web app.  You have to navigate between pages, call web services, link in javascript and CSS.  That is just what we do.  All of those links should be wrapped in Url.Content or Url.Action helpers.   The problem that is easy to run into is you move from development to test and the base url for your application changes.  You were testing at http://localhost:898989/ , and now you are deployed to http://myserver/myapp/  and a whole lot of urls just stopped working.  Url.Content and Url.Action are supposed to fix that.  That is why you use them.

7.  Get to know Partial Views for Ajax calls.

Partial views are actually just views that don’t have master pages and the html/body tag sections.  Partials can be executed in a multitude of ways, not just from inside a view on the server, but also from Javascript on the browser. JQuery also has a wonderful little method called $.load that will call a url, take the html that is returned, and slap it into the page.  This can greatly simplify a lot of behaviors.

I subtle little trick I sometimes do with this this is to wrap a section of code that takes a long time to load in a partial.  I will then call that partial into my page AFTER is has loaded on the browser (using the JavaScript function setTimeout to call the $.load ).  Now I get  a page that loads faster, but still has all of the data it needs.

8. Make the Master page work for you.

It isn’t that there is something inherently wrong with the existing master page that you get when you create a new Asp.net MVC project.  Typically it is 80% of what you would want.  But, as soon as I know what my general page is supposed to look like, I rip right into the Master Page.   Also keep in mind that you can nest Master Pages as well.

9. Think about what a designer would want.

Even if you don’t have one.  This is just a general pattern I try to get into.  I think about a designer as someone that can take raw html, add some css and images and make my work look a LOT better.    This means I use the raw html whenever possible, I write my JavaScript click handler so they will work with buttons or links (hint: always return false — and why haven’t I written a JQuery plugin for this yet?).

10.  Version your css and JavaScript

This is actually getting into my next blog post, but figure out a way to version your JavaScript and CSS.  This really isn’t MVC specific, you should do this in almost any project.  The key reason is to help you with a browser’s cache.  You know you have a problem when the first thing someone tells you, when asking for help, is that they cleared their cache already.   My thought on this is your web app’s dll should have a version number, set the project to auto version, and then pop that onto the end of the css/javascript file call.  So it might look like this:  “http://myapp/…/file.css?version=1.0.0.256”.   In my sample code, when in development I stick a timestamp on the file in the same way.

OK, 10 rules is quite enough (I didn’t even think I would get that many).   To the american’s reading this: Happy Independence Day.

18 thoughts on “Asp.Net MVC: My Personal View Rules

  1. Not sure I agree about having as many view models as possible. If you can chuck one or two collections into the property bag then surely it is a lot simpler than creating a view model – project-wide this must be immensely simpler and easier to maintain.

  2. @Jag: I didn’t directly address the “keep login out of your views”, but indirectly that is covered by #1. If you don’t have any code in the view, you wont end up with logic in there either.

    @nick: as many as possible isn’t quite right, but having view-models specific to a purpose is more what I was after. And frankly, I don’t see having many classes as making the project much simpler. With fewer classes you end up with loads of special case logic that actually does become a maintenance issue.

  3. I can’t entirely agree with making view models that are specific enough to include formatted strings, because at the end of the day, everything displayed is a string. I use an extension method for DateTimes to format them, because I’m not going to throw away entities by having to map them all to something else. I realize there are things like AutoMapper, but frankly I don’t think a “ToAwesomeString()” method in a view is selling my soul.

  4. i’ve been using asp.net mvc around the same period of time and all my web dev now is exclusively mvc. I have never used HTML helpers (relying on pure html), but I have used some advanced constructs and had a little adventure with mvccontrib (though I didn’t use it in a later project).

    The project that I am buidling now is my third professional project in MVC. The first too scripts can be seen at: http://codebix.com, and http://astro91.com

    Not top-of the line stuff but we have a fair mix of jquery though I am no designer the websites are doing okay.

    But my MVC learning odyssey has now seemed to arrive at a dead end, I know enough to get by and there’s no major push to learn more. Most of the articles are no revelation either.

    Now I need some motivation to dig out adcanced mvc content and read it.

  5. @Jeff Putz: Actually, I’m fine with .ToAwesomeString() in the view. It is way better than .ToString(“my-crazy-date/time-format-string”) all over your code.

  6. I think balance is the key. I don’t want to litter my project with viewmodels that serve as simple wrappers anymore than I want to litter my views with logic, but the fact is (in my mind anyway) that *view* logic is perfectly suited for putting in the view. String formatting is view logic, so there’s no reason to not include it. That said, I agree that helper methods are a good refactoring for cleaning up the HTML; I just think that it’s possible to take the “rules” too far in the other direction.

  7. I also think something like formatting does belong in the view. Though I am still relatively new to MVVM and how to apply it efficiently. Currently stuff like date formatting is handled in the view via ValueConverters. (Then again this is a Silverlight application, not ASP.Net MVC) The important thing is I don’t want a web server doing anything with formatting.

    I’m still iffy about adopting MVVM whole-hog over MVP for WPF and Silverlight for instance; but in MVP, the formatting of values was definitely something that was the responsibility of the View.

  8. Thank you Chris for this article.

    I particularly find interesting the ongoing discussion about rule #3 and what it means for the separation of concerns between layers. I think that inheritance should be used to build model classes.

    Generic business rules should be coded in a base model class (ex: Customer) and presentation rules should end up in an inherited model class (ex: CustomerInvoice). In my example ‘CustomerInvoice’ class would have the responsibilities of formating customers fields and validate data for its use in the context of an invoice. ‘Customer’ class would also validate data but in the context of the business entity (ex: name should always be supplied no matter the context).

    It could help maintainability and having classes closer to the data layer or to the presentation layer but not both while keeping most of the rules (business and presentation) out of the view. And its good OOP practice.

  9. @Steve: Asp.Net MVC is not Silverlight/MVVM. Different rules will apply. Specifically because of the differences between the UI control sets. Some of what I talked about works for Silverlight/MVVM, but the particular advice of converting everything to string (for Asp.Net MVC) does not apply to Silverlight/MVVM where you have control sets that natively read the DateTime type.

  10. @Pierre, this might shock you a bit. But I’ve gone down that road before, and don’t want to go there again. In fact, I would not suggest it at all for a web application. It complicates your ORM mapping considerably, and really serves no purpose that could not be better handled with composition.

    So my CustomerInvoice would NOT inherit from Customer (or anything for that matter), but instead would have a Customer property. What inevitably happens when you start using domain class inheritance is that you get domain classes with more crap properties than valuable ones. Plus, now to hidrate the entity you have a tone more work that HAS to happen. Not so with composition.

    Second, my preference is to have one set of domain classes specifically for the data access (Repository Domain), and separate Domain classes for Views, and sometimes different domain layers for other layers, and have a robust mapping layer. This seems like a lot more work, but it actually keeps things much cleaner.

    But, in all fairness, my Domain (in the DDD sense) is not your Domain. Mine works very well for me. Not knowing your Domain, yours could work well for you, but not for me, and vise versa.

  11. @Chris: True, So long as the server respects the current client’s Locale in both directions where applicable. In my experience, passing dates formatted as strings in anything other than ISO format eventually leads to pain.

  12. With regards to having separate Model and ViewModel objects, this becomes almost obligatory because you do not want your data model polluted with UI concerns, such as validation rules, etc. In most ‘enterprise size’ applications you will be using different assemblies for your domain model, service layer, and UI so you will require something like AutoMapper to map between your domain and view models.

    I saw an interesting variation in one of Scott Guthrie’s screencasts where he was using Linq or EF and everything was in one project. He was therefore able to use a partial class for the domain model which included all the validation rules and UI hints. This approach will not work using separate assemblies because partial classes have to be in the same assembly but it is an interesting option for smaller-scale projects.

  13. @Rob: I have the greatest respect for Scott Guthrie, he is an amazing individual in Microsoft and doing excellent work. But I can’t recommend doing that. Even for Scott, I can only imagine his thinking was that he only had 30 minutes to do a demo, and how could he cram as much in there as possible.

    Demos to not equate good coding practices.

    But, I will say this in defense of that method: if you can code the site, start to finish, in under 4 hours then I don’t care what your code looks like. Worst case you can rewrite the entire thing later. If your project is going to take days to complete, then an ounce of prevention is worth a pound of cure.

  14. Hey Chris,

    Great article and very sound advice!

    I hope you don’t mind, but I intend to make quite a few references to your article in my talk at mvcconf.com next week to demonstrate how the Spark View Engine can help you achieve a lot of what you talk about here. If that’s a problem for you, would you mind letting me know by email?

    All the best,
    Rob

  15. Great post. It has a very good tips. Thank you.

    I have a question concerning the rule #7. In wich way or how do you use the setTimeout to call the load() function? Could you post an example or something like that?

    Thanks a lot.

  16. Chris Brandsma :
    Second, my preference is to have one set of domain classes specifically for the data access (Repository Domain), and separate Domain classes for Views, and sometimes different domain layers for other layers, and have a robust mapping layer. This seems like a lot more work, but it actually keeps things much cleaner.

    Do you have a code sample that demonstrate this? It’d be a lot easier to see this than to mentally map it out. I’d like to see the automapper/composition over inheritance action in code.

    By the way, the comment system here is kind of wacky. I was lost at first because your replies started on one page and the original comment ended on another.

Comments are closed.