A lot of people out there are asking, “Why is ASP.NET MVC better than WebForms?” or “Why should I switch from WebForms to MVC?” Usually the response includes these items:
- Clear separation of concerns
- Testability – support for Test-Driven Development
- Fine-grained control over HTML and JavaScript
This doesn’t always mean much if people don’t do TDD and are fine without fine-grained control over HTML and JavaScript.
I’d like to add one more thing to the list: repeatability. Most of us are writing line-of-business applications, and when you write these kinds of web sites, there are common UI patterns that emerge:
- A grid that contains a list of data, and might have Add/Edit/Delete buttons above it.
- A screen that contains a bunch of input fields and Save and Cancel buttons.
- If you save a screen with input fields and a validation error occurs, go back to the same screen and show the validation errors at the top of the screen.
Here’s where the repeatability comes in. Let’s take the Add/Edit/Delete grid screen as an example. I should be able to write code to handle this pattern once and never have to do it again. The next time I do this screen, I should only have to write code to do the following:
- Tell the grid what data to display
- Tell the grid which buttons to show (if any)
The code looks like this:
<%= Html.JQueryGrid()
.LoadDataFromUrl("/customers/GetCustomerData")
.AddEditDeleteButtons()
.Caption("Customers")
.ExpandToFitAllRows()
.Columns(columns =>
{
columns.For(x => x.Id).Visible(false);
columns.For(x => x.CompanyName).AsEditLink();
columns.For(x => x.Address);
columns.For(x => x.City);
columns.For(x => x.State).Width(50);
columns.For(x => x.Zip).Width(50);
columns.For(x => x.CompanyEmail);
columns.For(x => x.PhoneNumber).Width(100);
columns.For(x => x.FaxNumber).Width(100);
columns.For(x => x.Salesperson).Width(100);
}) %>
Pretty clear what that does, right?
Notice what I’m not writing:
- Code that tells the site what to do when Add, Edit, or Delete are clicked
- Code that enables/disables the buttons based on what is selected in the grid
- Grid paging code (other than stored proc or LINQ code that handles this, if necessary)
- Any JavaScript code (it’s all spit out by the code that renders the grid HTML)
I could write the code to do these things, but I’m not going to unless I have to because I need to do something different than the norm. I’m going to let my conventions and HTML helpers do the work.
So why is MVC better at this than WebForms? Because of separation of concerns. In this case, separation of concerns does not mean having a presentation layer, business layer, and data access layer. I’m talking about separation of concerns within the UI layer (your business layer and data access layer will be the same regardless of whether you use MVC or WebForms).
Let’s say that I have a screen with a grid with a list of data. The grid has a pager and Add/Edit/Delete buttons. Here are all of the pieces and parts that go into implementing this in MVC:
- HTML code for the page – View
- Code that writes out HTML and JavaScript for grid – HTML helper fluent interface + HTML renderer + jQuery grid
- Get data for the view – controller method
- Store data for the view – view model, populated with very little or no code using AutoMapper
- Handle AJAX requests from the grid pager – model binder, which passes data to controller
- Return data in a format the grid pager can understand – method on base controller class (lets MVC convert data to JSON and call Response.Write())
- Verify security permissions – action filter attributes on controller methods
- Handle Add/Edit/Delete button clicks – JavaScript written out by HTML grid render
- Determine which screen to show when button is clicked – action filter attribute
- Get data for Add/Edit/Delete screens – controller method
How would you do this in WebForms?
- HTML code for the page – View
- Code that writes out HTML and JavaScript for grid – 3rd party grid control
- Get data for the view – code behind (calling into business layer)
- Store data from the view – copied into controls in code behind
- Handle AJAX requests from the grid pager – web service, configured to accept AJAX requests
- Return data in a format the grid pager can understand – some method + JSON formatter + Response.Write()
- Verify security permissions – code behind
- Handle Add/Edit/Delete button clicks – custom JavaScript
- Determine which screen to show when button is clicked – code behind
- Get data for Add/Edit/Delete screens – code behind (calling into business layer)
Notice the pattern? In WebForms the code behind is doing a lot (not to mention the fact that you can’t easily unit test code behind files). Big code behind files do not lend themselves to repeatability — but small classes that have specific responsibilities are very reusable. This is why separation of concerns matter. In my MVC screen, I deal with a view, controller, view model, model binders, HTML helpers, grid renders, and action filter attributes. In WebForms, I have a view and a code behind.
Having these small classes leads to reusability. I can use the HTML helper that renders the grid for my page on another page that renders a grid. I can create a controller attribute that will check to see if a user is logged in, or in a certain role. I now never have to write code to check that again – I just put an attribute on a controller method and I’m done with security.
It’s also amazingly easy to write code when you’re working with small, specific classes. Can you tell me what this attribute does?
[RequiresPermission(Permissions.AddUser)]
How about this one?
[RedirectWhenGridToolbarButtonClicked]
All of this leads to reusability, less bugs, code that is easy to read and write, and less development time. The ultimate goal is less development time and better quality. By reusing code, you get things done faster (because you write less code) and you have fewer bugs (because you write less code).
On my current project, development time is way down. The screen that I’ve been talking about with the grid and Add/Edit/Delete buttons and paging, and all that? Done in 2 hours. Basic input screen with validation – 4 hours. The first screen with a grid took a few days, but I wrote all the HTML helpers and renderers that write out the HTML and JavaScript for the grid, wrote the model binder that takes HttpRequest form inputs and calls controller methods (based on conventions), and wrote all the code to receive AJAX requests and send back a response. On the next screen, I call my HTML helper methods that write out the grid HTML and pass it some configuration parameters, create some controller methods that load and save data, and I’m done.
This post didn’t cover MVC basics, like what controllers, view models, and model binders are. There are plenty of posts that explain that. I’m just hoping to explain that the reusability of ASP.NET MVC can drastically reduce development time and make things a lot easier for you.