Presentation: What's New in MVC 2?

Posted by Joe Wilson on Wednesday, May 12, 2010 12:35 PM

Thanks to all the folks who attended the South Colorado .NET User Group Visual Studio 2010 launch on May 6th.  We went through a giant buffet of new things in Visual Studio, Entity Framework, SharePoint, Silverlight, and ASP.NET.  Slides from my talk on the new features in MVC 2 can be downloaded here.

Big thanks also to Ben Hoelting for putting the event together.  I liked the mini code camp format!  It was a lot of info, but it was a great survey of most of the new features. 

Tags:
Categories: Presentations


kick it on DotNetKicks.com shout it on DotNetShoutOut

REST-like behavior with MVC instead of WCF

Posted by Joe Wilson on Sunday, March 7, 2010 6:16 PM

WCF is a very cool framework for adding an abstraction layer over your services.  You can have .asmx, .svc, and now, REST URLs with no extension.  These endpoints can even return JSON to the caller, which is useful if you’re using jQuery or another JavaScript library.

The knock on WCF?  The configuration story!  It’s getting better in WCF 4, which is scheduled for release in a month or so.  It will have a bunch of defaults so you don’t have to be as explicit in your config file.  Finally, some convention over configuration in the WCF space!  Now someone needs to create a fluent, code-based configuration tool for the exceptions to the default conventions…

Don’t be afraid of JSON!

Jason But if you need to return JSON to a caller today, you don’t need scary WCF config files to do it.  You can get the same URL and the same data payload with a lot less hassle using MVC controller actions.

The steps are:

  1. Create a new action in a controller.
  2. Grab your data or whatever you want to return.
  3. Parse your data to JSON.
  4. Throw it back out to the requestor.

The trick is to use JsonResult as the return type for your controller action and convert your output to a JSON string with the base controller’s Json method:

public JsonResult GetAllDepartments()
{
    var departments = _repository.GetAllDepartments();
    return Json(departments);
}

Now if I browse to this controller action (I’m using the HomeController in this example):

http://localhost/Home/GetAllDepartments

I get a pop-up to save the response stream, which I can open with Notepad:

[{"Name":"Human Resources"},{"Name":"Accounting"},{"Name":"IT"},{"Name":"Pencil Sharpening"}]

Yeah, JSON!  And there was no gory config setup needed!

Using jQuery with JSON

Now we can use jQuery in the MVC view to take advantage of this returned JSON. 

Here’s a view that has a button and a div tag.  jQuery is being used to tie the button click to a call to the controller and action we set up above.  It then gets the JSON coming back, loops through it, and puts it in the div tag so it’s visible to the user.

<script src="../../Scripts/jquery-1.3.2.min.js" type="text/javascript"></script>
    
<script type="text/javascript">
    $(function() {
        $("#btnShowDepartments").click(function() {
            $.getJSON("/Home/GetAllDepartments", null, function(data) {
                for (item in data) {
                    var department = data[item];
                    $("#divDepartments").append(department.Name, ", ");
                }
            });                
        });
    });        
</script>   

<p>
    <input id="btnShowDepartments" type="button" value="Show Departments" />
    <div id="divDepartments"></div>
</p>

This is probably not the best jQuery in the world.  I’m still new to it.  But the point is to show that you can put a URL in, parse the JSON, and use the data however your app needs it.

Tags: , , , ,
Categories: Technical


kick it on DotNetKicks.com shout it on DotNetShoutOut

Presentation: Intermediate ASP.NET MVC - Part 2

Posted by Joe Wilson on Friday, February 19, 2010 3:27 PM

Thanks to all those who came out for the Denver Visual Studio User Group lab for more ASP.NET MVC fun!

The slides, labs, and code can be downloaded here.  I've also included some quick starts\cheat-sheets as handouts.

Tags:
Categories: Presentations


kick it on DotNetKicks.com shout it on DotNetShoutOut

Presentation: Should you use MVC on your next project?

Posted by Joe Wilson on Wednesday, February 3, 2010 12:32 AM

Thanks to all those who came out for the Colorado Springs User Group.  The slides and code can be downloaded here.

There were a few complaints about the black background in Visual Studio making the code harder to read.  I'm sorry about that.  Next time, I'll use the default Visual Studio settings.

Tags:
Categories: Presentations


kick it on DotNetKicks.com shout it on DotNetShoutOut

MVC 2: Electric Boogaloo - What's new in MVC 2

Posted by Joe Wilson on Sunday, January 17, 2010 3:28 PM

The MVCbreakin2 team loves their rec center, but an evil real estate developer wants to bulldoze it to build a hipster bar named Ruby's.  The team has been coding their butts off with MVC 2, but will the new features be enough to save the rec center?  Will the cynical real estate developer cackle and complain that it's still not enough? 

Will the MVC team get areas right?  Will the new templated HtmlHelpers strike the right balance of rapid development and fine control?  Will validation concerns get muddled up with UI concerns?  Will more web forms developers make the switch to MVC? 

New Areas

Areas are an organizing tool for large web sites.  If you've got lots of view and lots of controllers plus some shared content, areas can work as a top-level routing mechanism. 

Example areas might be Admin for an administrative site, Store for a ecommerce site, etc.  They can be either folders or separate web projects.  Use the AreaRegistration class to set up your routes in your Global.asax (taken from MSDN article):

public static void RegisterRoutes(RouteCollection routes)
{
    routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

    AreaRegistration.RegisterAllAreas();

    routes.MapRoute(
        "Default",                                              // Route name
        "{controller}/{action}/{id}",                           // URL with parameters
        new { controller = "Home", action = "Index", id = "" }  // Parameter defaults
    );
}

Then override AreaRegistration in the area's folder or in the root of the area web project (also taken from MSDN article):

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;

namespace Store
{
    public class Routes : AreaRegistration
    {
        public override string AreaName
        {
            get { return "Store"; }
        }

        public override void RegisterArea(AreaRegistrationContext context)
        {
            context.MapRoute(
                "Store_Default",
                "Store/{controller}/{action}/{id}",
                new { controller = "Products", action = "List", id = "" }
            );
        }
    }
}

Sharp Architecture has had areas since its first release.  I remember hearing rumors about areas coming in MVC for version 1.0, but I guess it wasn't ready.  The biggest difference between the Microsoft version and the Sharp Architecture version is that the Microsoft version lets you register different web projects. 

Theoretically, multiple teams can work on multiple web projects, then these web projects can be rolled into the same solution and AreaRegistration can make them appear as one big web site to a browser.  I don't know how much of a need that is, but I guess if you have distributed teams that can't get to the same source code repository, it might be useful.  I prefer multiple folders in one web project over multiple web projects to keep build times down and Visual Studio responsiveness up.  I wouldn't break out each logical web site section to its own web project until the folder approach stopped working.

New HtmlHelpers

I'm not sure if Code Camp Server pushed the MVC team into this or just showed how it was possible to take HtmlHelpers to this extreme, but I'm glad to see the same concepts getting rolled into the base class libraries. 

I think the new HtmlHelpers do give a nice range of control over your markup.  You can use:

<%= Html.LabelFor(x => x.FirstName) %>
<%= Html.EditorFor(x => x.FirstName) %>
<%= Html.LabelFor(x => x.LastName) %>
<%= Html.EditorFor(x => x.LastName) %>

if you want to be fairly explicit for each form element.  Or you can save a lot of keystrokes with:

<%= Html.EditorFor(x => x) %>

And if that's not enough, you can decorate your models with some UI hint attributes and roll your own rendering.  I think this is something large web projects will take advantage of for consistent markup and control rendering.  Seems like this has the advantages of homemade server controls without some of the hassles (INamingContainer, I'm looking at you).

New Validation

I did worry for a little bit about the UI stuff bleeding into the model with these new attributes.  Seems like we've got a leaky abstraction there.  But these days I think of the MVC model as the view model, not the domain model.  The view model is married to the view it works with.  It has no other purpose than to represent the data in that view.  Well, now it also validates the data in that view, at least initially.  Oh, and it now controls some of the view rendering.  Damn.  That's three responsibilities for my little view model.

But I don't have an alternative proposal to attribute-heavy view models either.  I don't want partial classes.  I haven't seen much benefit to the buddy class approach.  And I do want to reuse some of my UI hints across view models (like a date picker for DateTime types).  Plus, I want to take advantage of the new client and server side validation of my view model before it gets into the action method.  MVC 2 has this built in.

I guess I'll grin and bear it and work on a new blog post about the Triple Responsibility Principal for View Models (as opposed to SRP). :D

Future

I've already talked about where MVC views are now and where they are going, and the view engine is definitely getting better. I still think views will continue to be the most in-flux part of the MVC framework.  I know people that are very happy with Spark as a view engine, and Louis DeJardin, the guy who wrote Spark, is now working for Microsoft on the ASP.NET team.  I don't have any inside scoop, but I wouldn't be surprised if the web forms view engine got a little."sparkier".

Views may also be the area with the most room for improvement, depending on who you talk to.  When I visit with people about MVC and explain that runat="server" is gone and you can go ahead and close your Visual Studio toolbox, most get a glazed look.  They don't want to hand code the HTML.  They don't want to see a property sheet that doesn't know anything about their current mouse selection.  They don't want to give up their current productivity.

I get that, but I think you can be faster without the designers and toolboxes and property sheets.  It takes a while to get used to using the keyboard more than the mouse, but I think it's worth pushing yourself over that learning curve.

Time to switch from Web Forms to MVC?

I push MVC on projects where I have influence on that decision.  In your world, it's your call.  You may have environment constraints, in-flight projects, etc. 

But it also doesn't have to be all or nothing.  You don't have to wait for a greenfield project to fall in your lap.  You can run MVC inside your current Web Forms project.  It's just another set of HttpHandlers living under ASP.NET.  Here is some guidance for getting your current project set up to do this.

I encourage you to give it a try and see what you think.  It will take a while to get used to the paradigm.  It's not hard - just different.  But I think once you're used to it, you'll like the separation of concerns, the easier testing, and the more natural flow for web development. 

By natural flow, I mean embracing the world of GET and POST.  I mean when you need to expose JSON, an RSS feed, or something else (XML, PDF, file, etc.), Controller Actions are ready to go.  Stop writing custom HttpHandlers or fighting WCF configuration for this stuff.  It should be simpler, and it is with Controller Actions and the right ActionResult.

Make it a goal to get some real-world MVC experience in 2010.  Think of the kids on the MVC team and their beloved rec center.  Don't let their dream die.  They've come too far.  And remember the tag line from Breakin' 2 - "If you can't beat the system.break it!".

Tags: , , , , ,
Categories: Technical


kick it on DotNetKicks.com shout it on DotNetShoutOut

Why Web Forms isn't as bad as it used to be

Posted by Joe Wilson on Friday, January 8, 2010 7:13 PM

I prefer using MVC over Web Forms for ASP.NET development, but the reasons are getting narrower as Web Forms is improving.  Here's my "Why should I use MVC" list from a recent presentation:

  • To get separation of concerns right from the start
  • To avoid ViewState page bloat
  • To avoid messy HTML
  • To avoid messy URLs

But a lot of this is changing in Web Forms with ASP.NET 4.

ViewState Improvements

In previous Web Forms versions, you could disable ViewState for the page with EnableViewState="False", but the controls on your page still had their own ViewState.  You had to explicitly turn it off for every control in the page to make it really go away.

Now there is a ViewStateMode property that the controls in the form pay attention to.  You can disable ViewState for the page with:

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="Default"  ViewStateMode="Disabled" %>

And just set ViewStateMode="Enabled" for controls where you need it.  This is the way it probably should have worked all along.  You'll still have view state, but you turn it on where it makes sense instead of having it on all over the place clogging up your response.

Rendered HTML Improvements

Now that we're all doing more client-side development with JavaScript once again, we need to grab control IDs.  We've all seen view source screens with nightmare IDs like:

<span id="ctl00_MainContent_uxdPHAllContent_TripSelector_lblFromDate">
From Date:
</span>
<input type="text" name="ctl00$MainContent$uxdPHAllContent$TripSelector$txtFromDate" id="ctl00_MainContent_uxdPHAllContent_TripSelector_txtFromDate" />

This happens when server controls are nested inside other server controls.  If you use Master Pages and ContentPlaceHolders, everything looks like this.

Now there is a ClientIDMode property of controls.  You can set ClientIDMode="Static" in the container control to get your exact ID or set ClientIDMode="Predictable" to get an ID you can expect in the rendered HTML.

Another rendering improvement coming in Web Forms is in the ControlRenderingCompatibilityVersion property.  It's a mouthful, but set that guy to 4.0 (side note: why "4.0" instead of just "4"?) in your web.config:

<pages controlRenderingCompatibilityVersion="4.0"/>

You get XHTML 1.0 Strict markup, menu HTML that look like a list (UL and LI tags), and fewer inline style setting like on validation controls.

URL Improvements

You're Marketing department has probably asked you for tighter, prettier URLs at some point.  Vanity URLs are a marketer's dream.  With MVC, creating clean, simple URLs is easy.  Now Web Forms has that same goodness.

The MVC URL routing engine is in System.Web.Routing, so it's not just for MVC anymore.  You can get those URLs marketers always want like http://mysite.com/CoolNewProduct without the coding hassles of old.

Conclusion

Web Forms will still have the post-back model, and MVC will still have Controllers with Actions.  And I would still rather work on an MVC project versus a Web Forms project.  But that can now mostly be for architectural reasons rather than framework artifact reasons.

I like the separation of concerns in MVC better, and I think once you get used to routing and Controllers and Actions in MVC, the Web Forms model seems very un-web like.  I also think MVC projects are much more testable. 

But I am glad to see Microsoft making these improvements in Web Forms.  It was due for some tidying up!

Tags: ,
Categories: Technical


kick it on DotNetKicks.com shout it on DotNetShoutOut

Presentation: Introduction to ASP.NET MVC - Part 1

Posted by Joe Wilson on Saturday, November 21, 2009 11:18 PM

Thanks to all those who came out for the Denver Visual Studio User Group lab.  The slides, labs, and code can be downloaded here.  I've also included some cheat-sheets as handouts.

Tags:
Categories: Presentations


kick it on DotNetKicks.com shout it on DotNetShoutOut

Validation frameworks in ASP.NET MVC

Posted by Joe Wilson on Tuesday, September 15, 2009 9:09 PM

There are plenty of validation frameworks out there.  I've worked with NHibernate Validators, Microsoft's Enterprise Application Blocks, and now Data Annotations.  They primarily work by adding attributes to the properties in the class you want to validate. 

How are they different?

Not very.  The biggest difference between them is their syntax.  I guess the Validation Blocks from Microsoft's P&P group have a lot of configuration options, but I haven't needed that on a project.  The most prominent differences are in the attributes themselves.

You can probably make a case for Data Annotations having a slight advantage since it's going to be in the .NET base-class libraries.  Easier to deal with assemblies installed in the GAC instead of the ones you have to drag around for each deployment.

How do you integrate them with MVC?

Each of these frameworks can be integrated with ASP.NET MVC.  But the best validation call is one you don't have to explicitly make.  Huh?

It's OK to call MyClass.IsValid() in a controller action, but if you can avoid it, I think you're better off.  Otherwise, what happens if you forget to call it?

Instead, use a model binder that invokes the validation framework for you.  This is how I am using the Data Annotations model binder with the MVC framework:

ModelBinders.Binders.DefaultBinder = new Microsoft.Web.Mvc.DataAnnotations.DataAnnotationsModelBinder();

Now this guy will intercept calls to your controller actions and reject invalid incoming view models.  Here is a simple tutorial for using Data Annotations that goes into more detail.

How do you lay this out in the solution?

I have an Application Services layer that my MVC layer calls into.  My controllers have constructors with an interface to an application service injected into them.  My IoC container decides which concrete version of this application service interface to use at runtime.

Next, my Application Service layer has a DTO folder that holds all the DTOs (property-only classes) my views need in my MVC app.  These are not my domain entities.  There might be a lot of overlap with those domain entities, but these DTOs are just there for communication between the UI layer and Application Services layer.  These DTOs are where I put my validation attributes.

So now I've got views that are tightly bound to the DTOs, which are owned in the Application Services layer.  This allows strong typing of the view so I can use the DTO's properties as my view's model.  This is handy for the Html Helpers to get design-time IntelliSense and compile-time checking.

What else can you do?

I haven't spent a lot of time looking into it, but xVal looks pretty strong for client side AND server side validation using Data Annotations and a couple other validation frameworks.  It also lets you validate your business rules in your domain and throw an exception.  I need to check into this more, because that sounds like the validation holy grail for validation to me: getting UI validation and business rule validation into a common format.

Tags: , , ,
Categories: Technical


kick it on DotNetKicks.com shout it on DotNetShoutOut

Review of Sharp Architecture

Posted by Joe Wilson on Monday, September 14, 2009 8:40 PM

I've been using the open-source project Sharp Architecture for about 6 months now, and version 1.0 was recently released.  It's a really great way to get rolling quickly on a project using ASP.NET MVC, Domain Driven Design (DDD), NHibernate, and Castle Windsor.

I won't go into the details about what Sharp Architecture does.  I'll just cover what I like in the first version and what I hope makes it into future releases.

What I like

Decoupled Design

The Sharp Architecture framework uses inversion of control right out of the box with Castle Windsor and the Common Service Locator wrapping container calls.  The MyProject.Core assembly holds your domain model and interfaces to your repositories.  Implementations of those repositories are in MyProject.Data. 

There is also a MyProject.ApplicationServices assembly for times when your controller logic starts to look heavier than it should or you prefer to push all your logic into an application layer so you can skim off the MVC UI for something else at some point (Silverlight?  WCF?).  I like this approach with very lightweight controllers and views for just this reason.

NHibernate Training Wheels

Billy McCafferty, the principal author on the project, is one of the gurus on NHibernate best practices.  NHibernate is very powerful, but some tools, like Fluent NHibernate, which uses C# code to map your domain to your database instead of XML, are.well.kind of fluid.and still under construction.

I'm still new to NHibernate and very new to Fluent NHibernate, so I get tripped up often.  It's nice to know that my solution is using best practices for tools where my footing is not as sure, like NHibernate session management.

Templates + Conventions

The Visual Studio solution and T4 templates inside Sharp Architecture are the things that makes it different from being just another reference implementation or architecture guidance.  You can get a new, empty Sharp Architecture solution going in about 30 seconds with the Visual Studio solution template.  The T4 templates help you add controllers and views built around conventions and the property names in your domain entity.

I used to be a big fan of code generation.  It's nice to spit out a bunch of classes very quickly, but there really has to be a balance.  If it gens too much code, is it all stuff you really need?  If it only gens a little bit, did you need a code generator at all?

Sharp Architecture does a good job of giving you a jump start on stuff you will probably want or at least a place-holder for stuff you will want.  It doesn't litter the solution with too much junk.

For instance, it doesn't generate a repository for your domain entity because you can use use the IRepository<MyDomainEntity> and Repository<MyDomainEntity> in the Sharp Architecture assemblies.  It doesn't generate NHibernate mappings for your domain entity because the mapping conventions cover most cases.  If you do need additional repository methods or have unconventional database mappings, it's easy to inherit from and override these.  Sharp Architecture uses generics, base classes, and conventions to keep from needing a lot of code generation.

Documentation, Sample Code, Wiki, and Forum Support

This project is much better documented than most open-source projects.  In addition to the "getting started" and 'using the framework" documents, there is a sample Northwind project that comes with the download files.  Online, there is a wiki and a Google Group that is actively monitored if you need additional help.

I know real programmers only use Reflector for documentation.  :>  But don't underestimate the value of documentation for widespread adoption.  You can have a great open source project that doesn't get very far if people don't know how to get up and running quickly and fully exploit the framework.  That won't be a problem for Sharp Architecture.

Helpers

Sharp Architecture libraries are chock full of all kinds of convenience methods and helpers.  Want to write a database integration test using SQLite?  Just have your test class inherit from RepositoryTestsBase().  Want to hit the live database?  Inherit from DatabaseRepositoryTestsBase().  Want to design by contract and check incoming parameters and return values easily?  Look into the Check.Require() and Check.Ensure().

There are a ton of things like this that all of us have written before.  Most developers I know walk around with a couple assemblies to make coding their next project easier.  With Sharp Architecture, these are referenced in your solution right after File > New Project.

Folder Structure

Everyone has their own preferred solution and folder structure.  The default project and folder organization in a new Sharp Architecture solution is pretty close to what I was already using and is simple for any developer to jump in and follow along.

This may not seem like a big deal because a good structure looks obvious in hindsight, but it's nice to not have to dig around for things.  I've worked on solutions that have 20+ projects.  Sharp Architecture goes the other way and you end up with about four projects in a normal MVC app.

What I would like to see in future releases

I don't have any strong objections to the design choices in Sharp Architecture.  Most of the things here are more a matter of style/taste than anything.

Controllers back in MyProject.Web project

You can read Billy's view on putting the controllers in the web project. His main point is that controllers should be written and tested independent of the other assemblies so you don't succumb to temptation and do things like go straight to your repository from your controller instead of asking your IoC tool to give you the concrete repository instance.

That's fair.  But my controllers don't do much besides call into my application services layer, render views, and redirect to other controller actions. I don't mind having a reference to the entire web project in my unit test project so I can test routes and the very little logic I have in my controllers.

In his article, Billy describes the pain of having done it the way I prefer.  Maybe this is one of those lessons where you have to experience it yourself to internalize it.  Like my dad telling me the stove was hot when I was a kid. :>

MyProject.Infrastructure instead of MyProject.Data

Maybe I've looked at Code Camp Server or listened to Jeffery Palermo talk too much, but I lump data handling into a larger category of infrastructure concerns.  Sharp Architecture's solution template gives you MyProject.Data, which holds the NHibernate mappings and conventions and the repository classes. 

But I have other utility/infrastructure things I need to put somewhere, like logging, emailing, etc.  I prefer having MyProject.Infrastructure with Data as a folder underneath it so I can throw all my infrastructure implementation code into that assembly.  The interfaces for these guys still go in MyProject.Core, just like the repository interfaces.

More emphasis on MyProject.ApplicationServices

I prefer to have as little logic as possible in my views and controllers.  I push as much as I can to an application layer so the UI can be replaced or added to with minimal impact.  In Sharp Architecture, you've got MyProject.ApplicationServices for exactly that.  There is one Northwind example that uses this layer, but in the other samples, the controllers are doing the application flow work.

I think it's better to have an application service passed into the controller's constructor so the controller doesn't need to know much except routing, views, redirects, the app service method to call, etc.  I don't mind having these UI logic concerns in the controller, but anything beyond that needs to be in an application layer.

I've borrowed a metaphor from the SQL Server installer that talk about "surface area".  I think of the MyProject.ApplicationServices layer as a wrapper around all the app logic.  The methods in this layer are the surface area of my application, and they encapsulates all the implementation code my domain is dealing with.  Calls into this layer can kick off multi-step processes, handle workflow, and usually have long, explicit method names.  But this is the only part of my domain's surface area that is exposed externally.

The MVC web project is one client calling into this application layer, but there may be others someday on even medium-sized projects.  If your app becomes more popular and you've followed this approach, you can add other clients that call into your application service layer like a WCF service, a system tray tool, etc.  These guys may have their own specialized methods or may use something the web application was using.  Either way, you are very intentional on the surface area you expose.

I think Sharp Architecture gets this right.  I would just like to give it a little nudge to make it a point of emphasis.

Conclusion

I've used a lot of different frameworks, but Sharp Architecture comes closest to the way I want to work already.  The templates and documentation put it well beyond a reference implementation, and you can work with a simplified solution sitting on top of a lot of abstracted complexity to get you productive right away.

Tags: , , ,
Categories: Technical


kick it on DotNetKicks.com shout it on DotNetShoutOut

Handling Exceptions in ASP.NET MVC

Posted by Joe Wilson on Sunday, August 23, 2009 6:24 PM

Here is a quick run down of the various approaches for handling exceptions.  I use a combination of these that I'll show at the end.

Try-Catch everywhere

You've seen this code before, right?

public void DoSomething()
{
	try
	{
		var x = 1;
	}
	catch (Exception ex)
	{
		ShowExceptionToUser(ex.Message);
	}
}

The approach of putting try-catch block around every ounce of code seems like a good idea intuitively.  Sure, you're showing the user an ugly error message, but it's better than crashing the server.

But what is the user supposed to do with the information you show them?  How are they supposed to fix your null reference exception?

The other knock on this approach is there is so much ceremony code!

Generic error page

This approach goes the other way with a generic error page displayed to the user for any unhandled error.  It's easy to implement in the Web.config file, and the user never sees a null reference exception or stack trace.

Here's what I'm using:

<customErrors mode="RemoteOnly" defaultRedirect="~/Error">
	<error statusCode="403" redirect="~/Error" />
	<error statusCode="404" redirect="~/Error" />
</customErrors>

There are two problems with the generic error page approach.  First, the user has no information about what happened.  Maybe it's something they could have resolved?  Maybe they should try again later?  Just saying "Pardon our mess" doesn't give them any details.

Second, it doesn't give you any details either.  The next time you talk to the user, they will tell you they got an error in your app.  "Oh, what happened?" you'll ask. 

"Uh, it was on that screen with the customer info.  I forgot what I was doing, but it just showed the error screen, so I rebooted and went home for the day."  Not much to go on.  Something about the customer screen?  This will be a fun one to try to reproduce!

Just log it

The last approach is to log all errors.  You collect as much detail as you need.  The date and time the exception was thrown, which user/IP address, which server they were on, the stack trace, etc.

Error logging can be extensive and can be stored in rolling text files or database tables.  I prefer using the database so I don't need to grant write permissions to the ASP.NET process running that web app.

Here's an Application_Error event from a couple projects back:

protected void Application_Error(object sender, EventArgs e) 
{
	var ctx = HttpContext.Current;
	var ex = ctx.Server.GetLastError();

	if (ex != null)
	{
		_logService.LogExceptionToDatabase(ex);
	}
}

I've done lots of applications like this, where the Application_Error event in Global.asax calls into a logger like log4net or Enterprise Library to store exception details.  It works great.  You get the details you need to fix the bug.

But what about the user?  What are the users looking at when this exception is thrown?

#3 Combo Platter

Today, I'm using bits and pieces of all of these. 

I show the user a generic error page for all unhandled exceptions.  It may not have much info, but something weird just happened in the app and I wasn't expecting it.  I need more information about what happened so I can decide what needs to be done.

That's why I'm also using ELMAH to log all unhandled exceptions.  The exception details get stored in my database and I even get a fancy exception viewer since ELMAH works as an Http Handler.  My favorite ELMAH feature is you can usually see the yellow screen you would have seen if running locally on your box.

Finally, sometimes there are handled exceptions.  This is where an exception occurred before in the code (database is down, file not found, etc.), it might occur again, and I've got some thoughts about what to do next time it happens.

In my view, the only place you need try-catch blocks is when you are worried that an exception might happen and you want to give the user some information, give the user instructions, or silently fix the problem yourself.  If I can't do one of these, I just show the "Oops" page try to come up with a plan later.

For instance, if the database is down, I'd tell the user to try again later, but I couldn't save their record right now.  If the user is trying to upload a file and it's not there, they can try to correct the file upload input box and resubmit.

How to wire this up in ASP.NET MVC

Set up ELMAH

First, download ELMAH binaries, add a reference to the ELMAH assembly in your web project, and follow the instructions to get your Web.config set up correctly.

Now test out ELMAH.  You should be able to browse to elmah.axd and see the list of exceptions.  Try throwing one in your code somewhere and see if ELMAH logs it.

You can make the elmah.axd page secure, log to a database, etc.

Set up the Generic Error Page

Once ELMAH is logging, wire up your Web.config to show the generic error page:

<customErrors mode="RemoteOnly" defaultRedirect="~/Error">
	<error statusCode="403" redirect="~/Error" />
	<error statusCode="404" redirect="~/Error" />
</customErrors>

I'm using an Error controller with a single Index action so I can send the user to the root and to the Error route to show the error page.  Here's the controller:

public class ErrorController : Controller
{
	public ActionResult Index()
	{
		return View("Error");
	}
}

It's calling a view also called "Error" that is in ~\Views\Shared\Error.aspx.  Make your version of that page.  Remember, this is the page for unhandled exceptions.

Next, you need to register this route.  I've moved my route registration out of my Global.asax, but if you have a default ASP.NET MVC install, look for your routing registrations in Application_Start:

routes.MapRoute("UnhandledExceptions", "Error", new { controller = "Error", action = "Index" });

This is just setting up some defaults in the route to simplify redirects to the generic error page.  At this point, you should be able to browse to any controller with a thrown exception in the action and see your generic error page displayed.

Set up the ElmahHandleError attribute

There's one step left!  You've now got two ways of dealing with unhandled exceptions.  We need to consolidate these so the generic page is show AND the ELMAH logging is called.

You know how you have to add the HandleError attribute to your controller classes to get the generic error page to appear?  This attribute doesn't call ELMAH.  This is by design, since the HandleError attribute is, well, handling the error.  ELMAH is for unhandled errors and doesn't log otherwise.

But you can have it both ways and use ELMAH as your handled exception logging tool as well.  Follow these steps to create an ElmahHandleError attribute and replace the HandleError attribute on your controllers with this new attribute.

That's it!  You should be all set with an exception handling strategy.

Tags: ,
Categories: Technical


kick it on DotNetKicks.com shout it on DotNetShoutOut

Image Details