Saturday, November 14, 2009

ASP.Net MVC: A Step in the Right Direction

A link to Doug Copestake’s article “ASP.Net MVP: A Step Backwards?” was forwarded to me, and it made some short rounds on twitter. I read it and had a comment underway and thought, “This is WAY to big for a comment, let’s blog it!” So here we are.

Off the top, I’d like to give a tip of the hat to Doug for stepping outside his day to day and taking a look at ASP.Net MVC. It’s very easy to hide behind FUD and just blow off something different. Additionally, his initial findings are pretty much on par with most other folks happy with WebForms who take their first look at MVC.

The MVC framework definitely provides simplicity and separation of concerns to web development. It allows you to only write the code you need to write to get the job done, and it allows you to work with the framework. Views become very small and very specialized, controller actions mature to be very thin, and it’s all easily testable.

This has made my work with the MVC framework extremely successful. Since everything is easily testable, as we refactor the code to clean it up or add new functionality, it all flows very easily.

Control reuse comes from veteran WebForms developers quite often. But if we again point at simplicity, how many of those drag and drop controls were written to cover more functionality than you need? You drag a grid control out and use 15% of it’s power because its made for everyone. Yes, you can write your own application controls to do exactly what you want, but you can do that in MVC, too.

Another point to control reuse is the leveraging of more javascript frameworks to do many of the things that the WebForm control suites do. In jQuery UI alone, you’ll get tabs, accordion panels, sliders and other UI elements, all with out that “server side” part that is so prevalent in WebForms. The win there is in more than just lighter weight controls, you gain a much better user experience. (And who doesn’t like happy users?)

The spaghetti code point Doug makes is quite valid, though I think that’s not a factor of one framework or the other as much as it is the person writing the code. The Glen Vanderburg quote, “A bad developer will move heaven and Earth to do the wrong thing,” applies to all languages and frameworks. The person with the keyboard under their fingers is responsible for keeping their code clean, not the framework developers, but I digress.

Spaghetti code in the views is a big sign that you’ve done something wrong. Even before moving to Spark, if you see lots of server instructions in your views, it’s time to stop and think, “What can I do differently here?” Same can be said of your controllers. If you’re used to writing a 50 line page load method in WebForms, turning similar code into 50 line controller actions is a step in the wrong direction. I guess the main point here is instead of relying/hoping for the framework to do the work for you, the onus is now squarely on the shoulders of the developer to keep his or her code concise.

Doug’s last point is very true, though: if you have a lot invested in WebForms in your current situation, there’s no real reason to switch away from it. Switching for the sake of switching to anything usually isn’t a good move.

That said, you can easily run WebForms and MVC in the same web application. If you have a little something to add to the project that can stand on its own, I’d say a look at MVC is in order.

This definitely would have been too long of a comment… :)

Labels: , ,

Wednesday, October 21, 2009

ASP.Net MVC v. WebForms

This question seems to come up a lot in discussions around these two ASP.Net frameworks. Just this past weekend in Cincy at the MVC Firestarter, it came up a number of times.

So, on the way to work this morning, I was listening to Hanselminutes 184, and the question was FINALLY given an answer by Scott Hunter.

If you care about separation of concerns, testability and tight control of your markup you should go with MVC. If you’re an enterprise developer and just want to get something out there quickly then use WebForms.

That’s paraphrased, I may have gotten the MVC reasons in a different order, but that was the point made.

Scary that “Just get it done,” is the main reason to use WebForms. I’ll stick with my “slow” TDD and long term maintainability in MVC and steer clear of WebForms where DDD apparently stands for Drag, Drop, Deploy.

Labels: , , ,

Monday, August 31, 2009

I forgot what the M in MVC was for

What brought me to this conclusion was my reading up on a little Rails. I finally took the dive. I’ve been avoiding Rails for a while in favor of just learning Ruby. Steve loaned me “Rails for .Net Developers,” and the research was underway in earnest.

On page 89, the light bulb went off. But not for Rails so much as for how I’m writing my ASP.Net MVC apps. Here’s the bit that got me:

• Models are the classes that represent your business domain and that are responsible for communicating with your data. In Rails, this means the tables in your database.
• Views represent your presentation layer, for example, HTML and JavaScript.
• Controllers are responsible for connecting the models and views and managing the flow of the application. doing_it_wrong

That reads pretty straightforward to me. Hell, I’ve stood in front of groups of devs on more than one occasion and said, “Views are what gets rendered, controllers marry up the views with anything they need from the model, and the model is everything that’s not a view or a controller.”

So, what’s been going wrong? I’ve been putting way too much business logic in my controllers.

The structure of most of the MVC apps I’ve worked with is a typical .Net program structure. We’ve got a core where our business domain lives, and some type of ORM set up to talk to the database. From there, we expose that domain up to the UI through some simple domain services.

The issue comes when I consume those services. I’m using my controller actions to marry all that up and present it to the UI. I think my “excuse” to this point has been that I have two models: My domain model, which I’m accessing through my domain services, and my presentation model, which lives in the Models directory in the ASP.Net MVC structure. My controllers have to care about two places to get their information to provide to the views. At the least I’m violating the pattern and at worst I’m likely repeating myself somewhere and creating a future maintenance issue.

I think I can see where this happened, too. Think back to the Bad Old Days of dealing with Web Forms, and the cubby code behinds that came with it. In order to avoid the 197 line PageLoad method, I would have a lot of little one-off methods scattered about the code behind file. Switch over to MVC, and it seemed perfectly normal to have a similar structure. Alarms should have gone off when I had 5 classes in the Models directory but 18 methods in one controller class.

But wait, there’s more!

While perusing some code on my current project, I came across the [NonAction] attribute decorating a couple of controller actions. Re-reading that…I had NonAction decorating Actions. That has a certain “Jumbo-Shrimp” feel to it, doesn’t it? I suppose the non-action could fall under “managing the flow of the application” from above, but I’m thinking any non-actions might need to be refactored into the model.

Moving forward on my MVC apps – of any flavor, Ruby or .Net – I think I’ll move to a much thinner controller model in favor of loading up the model with business logic. Though I thought I knew the pattern fairly well, I didn’t practice what I preached.

Labels: , , ,

Tuesday, June 16, 2009

IndyNDA Recap and Covering the ModelBinder

I want to thank all that attended my MVC presentation in Indy last week. I had a great time speaking, and even though I left a portion of my code sample out (model binders, which I’ll cover below), I thought it went well and I got some good questions during and after. I apologize for leaving it out of the talk, but I get to write a little blog post about it.

A couple housekeeping things before I cover the ModelBinder part I missed.

Off the top, the slides and source code from last week are all zipped up for your downloading convenience. (The code through SVN is also available on Codeincubator.)

During the post talk talking, Dave asked me about a grid option in MVC. One we’ve been using is Flexigrid. It will do paging and sorting on its own, and will call back to your controller on its own for a little JSON. The big gotcha here is it runs on an older version of jQuery, which bit us in the butt on Friday on our project.

The blog post that contains the code and explanation…well better explanation than, “I copied and pasted and it worked,” on the partial views is on Steve Sanderson’s blog.

Now on to the part I forgot…

ModelBinder

I’m still feeling bad for not sharing this part. I got a question about it after the presentation, and that’s when I realized I totally forgot the edit page, which was going to demonstrate the ModelBinder. (Among other things, like the FluentHTML controls.)

If you have the code form above, the files in play here are InventoryController.cs and Edit.aspx. AssaultItemEditModel.cs is the object passed, but it’s just a property bag.

For the Reader’s Digest version: the ModelBinder works on the premise of convention over configuration, so the naming of your fields matter in that they have to match the property names of the object you want to bind to.

For the example I missed, we had the edit model that we wanted to bind to:

public class AssaultItemEditModel
{
    public virtual int Id { get; set; }
    public virtual string Type { get; set; }
    public virtual string Description { get; set; }
    public virtual int LoadValue { get; set; }  
}

The view had the form fields hooked to that edit model through the Fluent HTML controls from MVC Contrib.

<% Html.BeginForm("Save", "Inventory");%>
    <h2>Edit <%=Html.Encode(Model.Type) %></h2>
    <%=this.Hidden(x => x.Id) %>
    <ul class="details">
        <li><span>Type: </span><%=this.TextBox(x => x.Type) %></li>
        <li><span>Description: </span><%=this.TextBox(x => x.Description) %></li>
        <li><span>Load Value: </span><%=this.TextBox(x => x.LoadValue) %></li>
        <li><%=this.SubmitButton("Save") %></li>
    </ul>
<% Html.EndForm();%>

And finally, the controller method that’s going to process it all. Rather than taking in the standard id, it takes in a model object to which it will do all the Request.Form for you and hook the form values up to the object properties.

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Save(AssaultItemEditModel item)
{
     //do save
     var assaultItem = new AssaultItem(item.Id)
         {
             Description = item.Description, 
             Type = item.Type, 
             LoadValue = item.LoadValue
         };

     Service.SaveAssaultItem(assaultItem);

     var message = "Item saved successfully.";

     return this.RedirectToAction(x => x.Index(message));
}

Don’t mind my extra step on newing up an object with the id as a parameter, that’s due to the way I hooked up Fluent nHibernate at the start. Basically, to generate my maps from my domain objects, the id gets a private setter as convention. So, to get the right object to save, I had to set up the constructor to take an id argument. I cut some corners to get the demo app out the door. (John, you may hit me with, “Quicker does not equal better,” as soon as you read this.)

The other way around that is to not use my domain objects in my views, but that’s another discussion.

Under the hood where the magic happens, I haven’t dug too deep. But, I use this daily and it works swimmingly. You don’t even have to take in the model the view is bound to, just have the form fields match the model you’re taking in. I don’t recommend doing ad hoc model binding, in practice we set our edit model as a child of the view model.

I feel bad that I didn’t get this up on the screen, because it’s some fun stuff to show off.

Labels: , ,

Monday, June 8, 2009

“But WebForms did that FOR me.”

While working with the model binder the other day, the statement in the title was made by one of the guys on my team. It’s not an unreasonable statement, but I think it spells out an important distinction between traditional ASP.Net WebForm development and using the new, shiny ASP.Net MVC framework.

The point in question was in posting an object after edit, the id didn’t automatically come through. The model binders do some magic under the hood, and it’s welcome magic in my opinion as I’ll gladly leave Request.Form back with Classic ASP. However, you still have to tell the model binder the whole story, it’s not done for you anymore. (Nor was it done for you before WebForms.)

Solution to our little problem: Stash the object id in a hidden form field. (Yes, I know, right click and view source and there’s my object id.) Once looked down upon in WebForms development…well, other than that one GIANT hidden form field called “ViewState”…the hidden field looks to be making a quiet comeback.

Basically, if you want the model binder to find all the bits to your object, you have to make it available when the page posts. So, it needs to be in the form somewhere.

Personally, I don’t see this as a step backwards, but I grew up in the salad days of request/response. Having to provide that info so you can get it back seems normal. The flip side is also true: if you don’t need it, you don’t have to add it.

The more I work with ASP.Net MVC, the more I fall in love with it.

Labels: ,

Sunday, June 7, 2009

Back to Indy for NDA

image A few weeks back, I made my first trip to speak in Indy at the Indy Code Camp. I had a great time, met some new people, and got invited back to speak at IndyNDA on ASP.Net MVC.

So, this Thursday, June 11, if you’re in the greater Indianapolis metropolitan area and want to learn a little MVC, then I hope to see you at IndyNDA.

Labels: , ,

Sunday, March 30, 2008

Tech Night - Getting Started with MVC

First of all, thanks to all those who attended TechNight this past week at QSI HQ. That was by far the largest crowd I'd seen gather for TechNight, much bigger than the 10 people that came in September when I presented on ASP.Net AJAX. With Quick having the much larger training center now, I hope this trend continues.

The Snag

During post-presentation discussions, I finally arrived at why the test failed. Short version: I'm an idiot.

Long version: I went with the whole TDD approach to the presentation to show how MVC was more testable. It is a great way to show some of the advantages of MVC. It is if you continue to run those tests as you refactor your code.

What I did was stop at red, green, refactor. I didn't try for more green following an additional refactoring. I had written and passed my product controller tests, where the big test failure happened in the presentation, before I had added my model code to the product controller. That's a pretty big change, and clearly the tests caught that change...in front of an audience, rather than in the comfort of my own home.

So, my protests of, "These passed at home!" were correct, because I only ran them once. Like I said, idiocy.

In the end we got it working for purposes of the demo, and I owe Mel, Steve, Steve, Kris and many others who shouted advice a big thanks for helping me over the hurdle. However, the demo ended up testing what I didn't need or want to test: That LINQ was doing what it said. I had wanted to add one more test prior to the demo that hit an in memory collection of products, that would have solved my problem with the connection string and been a much better test of the controller code.

Here is a link to the code and slides: Getting Started With MVC

That resisdes in a SVN repository on Google Code (thanks Steve), and will get updated as I update the presentation. Clearly I have a couple of tests to add, some data issues to clear up, and will probalby change the slides some as it progresses. Keep an eye on it in the near future for updates, CODoDN will be here before I know it.

Labels: , , , , ,