My First Asp.Net MVC Project

This week I started working on my first Asp.Net MVC project. The requirements I am working with approximately 5 pages or views, and is read-only in nature, which I thought would be perfect for trying out the MVC stuff. I am also trying to take a incorporate some other methodologies, tools and techniques I have been reading up on, including:

  • Domain Driven Design (DDD)
    • Repository Pattern
    • Your not going to need it principle (YNGTNI)
    • Keep it simple stupid (KISS)
  • Test Driven Design (TDD)
  • Mocking (RhinoMocks)
  • Inversion of Control (Ninject)

So far the project is going really well. I’ve created all of my views, controllers, and basic page navigation, including some URL validation that redirects the user to a page when a parameter is missing from the URL (id part of the default route).  I’ve also added Inversion of Control using the Ninject framework, which I am using to specify my data store type (In memory for testing, or a Sql based store for actual data). I choose Ninject due to it’s small footprint, focused feature set, and Compact Framework and Silverlight compatibility (while not needed for this project, I have a use for that level of compatibility on another project and wanted to limit the number of frameworks I’m using).

CodeBetter: I should have thrown this out in the beginning, but I’m using the Foundations of Programming Ebook and learning application written by Karl Seguin as a starting point. I highly recommend it for people looking to get into the Alt.Net mindset (DDD, TDD, mocking, etc).

Solution Structure: At the start of this project, I was working up an elaborate solution (directory) structure, where I would separate out my Infrastructure, MVC, Domain, and Data Stores into separate projects. I quickly realized, that this wasn’t needed based on my requirements, nor did it follow KISS or YNGTNI. Instead, I separate out my concerns in the MVC project using namespaces. Since I am making heavy use of Unit Testing, if the need ever arose I could split stuff out into separate projects in less then a day, update my tests and be good to go. So in the end, I have one MVC project, a unit test project, and a Web Test project.

TDD: I have two testing projects, one for unit tests, using Xunit, RhinoMocks, and TestDriven.Net. This project tests all of the code for the model/domain, controllers, and infrastructure. My other project uses MSTest and is for my Web Tests, which are more of integration tests. There are some things you just can’t unit test that well, like did the menu get hidden when no account number was present in the URL.

Routes: I am using the default route Controller/Action/Id that comes with the sample project. This fits my requirements perfectly, as I need the Id part for an account number. My requirement was to have a URL in the form of http://Website/Home?Account=123456 which I was able to change to http://Website/Home/Account/123456

Code Behind: I’ve gone ahead and removed all of the code behind files on my views and user controls to make sure I don’t take any shortcuts with the code behind files. I’ve read several work around’s for getting strongly typed views without a code behind file, so I’m covered there. By removing the code behind you eliminate 2 extra files per view/user control (ViewName.cs and ViewName.Designer.cs). Just remove the AutoEventWireup and Codebehind attributes in your view, and change the inherits attribute to System.Web.Mvc.ViewPage or ViewUserControl

<%@ Page Title=”" Language=”C#” MasterPageFile=”~/Views/Shared/Site.Master” Inherits=”System.Web.Mvc.ViewPage” %>

Passing Id: One thing I found out I needed to do often, was construct a URL the conformed to my routing. Thankfully there is a helper method you can use in your view called Html.ActionLink, which has several overloads. I’m using the overloaded method that takes the name of the controller, action, an object for values, and an object for html attributes. Getting the object passed for values was not apparent right away, but you make use anonymous types.  The following code creates the following link, Home/Account/123456 (assuming ViewData[“Id”] is set to 123456), and gives it a text value of Home.

Html.ActionLink(“Home”, “Account”, “Home”, new { id = ViewData["Id"] }, null)

Filters: As I mentioned before, in using the default route, I’m “mapping” id to an account number. If there is no account number present, or the account number does not correspond to an actual account, I need to take some action. At first I had a helper method that I would call from each action that needed the account number, and redirect to a certain view if the account number was missing. Then I read about filters, and the OnActionExecuting override in the controller object, and thought that would be a better way to go. However, I’m going to go back to calling a method in each action for the following reasons:

  • Easier to unit test. With OnActionExecuting override, I havn’t been able to find a way to unit test the method or the overall behavior.
  • Code Smell. While one could argue writing the same line of code in each each action method smells, it is not as bad as having to check for certain action names in OnActionExecuting. For example, I didn’t want to redirect to the account entry page if I was already there. This way, I can call the method only on the action methods that need it.
  • Easier to understand. To the uninitiated MVC programmer, it’s simpler to follow then attributes or a base class override.

That’s all for now. I’ll try to write an update after I finish the project. I need to work with grids, and the new MS Charting controls, so that would be worthy of a follow up post.

This entry was posted in Programming (Web) and tagged , . Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre lang="" line="" escaped="" highlight="">