<?xml version="1.0" encoding="UTF-8" ?>
<?xml-stylesheet type="text/xsl" href="http://weblogs.asp.net/utility/FeedStylesheets/rss.xsl" media="screen"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:wfw="http://wellformedweb.org/CommentAPI/"><channel><title>Stephen Walther on ASP.NET MVC : ASP.NET MVC</title><link>http://weblogs.asp.net/stephenwalther/archive/tags/ASP.NET+MVC/default.aspx</link><description>Tags: ASP.NET MVC</description><dc:language>en</dc:language><generator>CommunityServer 2007 SP1 (Build: 20510.895)</generator><item><title>ASP.NET MVC Design Gallery at www.ASP.net</title><link>http://weblogs.asp.net/stephenwalther/archive/2008/12/18/asp-net-mvc-design-gallery-at-www-asp-net.aspx</link><pubDate>Thu, 18 Dec 2008 17:16:34 GMT</pubDate><guid isPermaLink="false">c06e2b9d-981a-45b4-a55f-ab0d8bbfdc1c:6795097</guid><dc:creator>swalther</dc:creator><slash:comments>0</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://weblogs.asp.net/stephenwalther/rsscomments.aspx?PostID=6795097</wfw:commentRss><comments>http://weblogs.asp.net/stephenwalther/archive/2008/12/18/asp-net-mvc-design-gallery-at-www-asp-net.aspx#comments</comments><description>&lt;p&gt;We just launched a new Design Gallery for ASP.NET MVC websites at:&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;&lt;a href="http://www.ASP.net/mvc/gallery"&gt;http://www.ASP.net/mvc/gallery&lt;/a&gt;&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;You can vote on the designs that you like and that you don't like. You also can contribute new designs of your own.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://weblogs.asp.net/blogs/stephenwalther/WindowsLiveWriter/ASP.NETMVCDesignGalleryatwww.ASP.net_8224/image_2.png"&gt;&lt;img style="border-right-width: 0px; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" border="0" alt="image" src="http://weblogs.asp.net/blogs/stephenwalther/WindowsLiveWriter/ASP.NETMVCDesignGalleryatwww.ASP.net_8224/image_thumb.png" width="655" height="226" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;After you download a design from the gallery, you can use the design by following these steps:&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;1) Copy the files from DesignTemplateCS or DesignTemplateVB folder into your project (use DesignTemplateCS for C# projects and DesignTemplateVB for VB.NET projects)&lt;/p&gt;    &lt;p&gt;2) Do a search and replace the string [YourProjectName]. Replace [YourProjectName] with the actual name of your project as displayed in the Solution Explorer window.&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;This last step is necessary to get the namespaces right. Most of the templates use helper methods to render the menu items so that the selected menu item is correctly displayed.&lt;/p&gt;  &lt;p&gt;Microsoft also is hosting a design competition to encourage members of the community to submit new designs. Submit a new design before January 31, 2009. ScottGu will be selecting the best three designs and announcing them on his blog. Learn more about the ASP.NET MVC Design Competition here:&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;&lt;a title="http://www.asp.net/mvc/gallery/competition/" href="http://www.asp.net/mvc/gallery/competition/"&gt;http://www.asp.net/mvc/gallery/competition/&lt;/a&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;img src="http://weblogs.asp.net/aggbug.aspx?PostID=6795097" width="1" height="1"&gt;</description><category domain="http://weblogs.asp.net/stephenwalther/archive/tags/ASP.NET+MVC/default.aspx">ASP.NET MVC</category><category domain="http://weblogs.asp.net/stephenwalther/archive/tags/ASP.NET/default.aspx">ASP.NET</category></item><item><title>Essential Visual Studio Tips &amp; Tricks that Every Developer Should Know</title><link>http://weblogs.asp.net/stephenwalther/archive/2008/10/21/essential-visual-studio-tips-amp-tricks-that-every-developer-should-know.aspx</link><pubDate>Tue, 21 Oct 2008 16:04:06 GMT</pubDate><guid isPermaLink="false">c06e2b9d-981a-45b4-a55f-ab0d8bbfdc1c:6696632</guid><dc:creator>swalther</dc:creator><slash:comments>102</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://weblogs.asp.net/stephenwalther/rsscomments.aspx?PostID=6696632</wfw:commentRss><comments>http://weblogs.asp.net/stephenwalther/archive/2008/10/21/essential-visual-studio-tips-amp-tricks-that-every-developer-should-know.aspx#comments</comments><description>&lt;p&gt;In this blog entry, I list the essential tips and tricks that every developer who uses Visual Studio 2008 should know. I wanted to keep this list brief. I also wanted to focus on only those tips and tricks that I use on a daily basis. Almost all of these tips and tricks are just as useful regardless of whether you are building an ASP.NET Web Forms or ASP.NET MVC application.&lt;/p&gt;  &lt;h4&gt;Tip #1 &amp;#8211; You don&amp;#8217;t need to select a line to copy or delete it&lt;/h4&gt;  &lt;p&gt;I always cringe whenever I see someone select an entire line of code in the Visual Studio code editor before copying the line or deleting the line (see Figure 1). You don&amp;#8217;t need to do this.&lt;/p&gt;  &lt;p&gt;&lt;b&gt;Figure 1&lt;/b&gt;&lt;/p&gt;  &lt;p&gt;&lt;a href="http://weblogs.asp.net/blogs/stephenwalther/WindowsLiveWriter/EssentialVisualStudioTipsTricksthatEvery_7EF8/clip_image002_2.jpg"&gt;&lt;img style="border-right: 0px; border-top: 0px; border-left: 0px; border-bottom: 0px" height="125" alt="clip_image002" src="http://weblogs.asp.net/blogs/stephenwalther/WindowsLiveWriter/EssentialVisualStudioTipsTricksthatEvery_7EF8/clip_image002_thumb.jpg" width="625" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;If you want to copy a line of code then you can simply press CTRL-c to copy the line and press CTRL-v to paste the line. If you want to delete a line, don&amp;#8217;t select it first, just press CTRL-x. You&amp;#8217;ll be surprised how much time this one tip will save you.&lt;/p&gt;  &lt;h4&gt;Tip #2 &amp;#8211; You can add a namespace automatically by pressing CTRL-. &lt;/h4&gt;  &lt;p&gt;In the old days, before Visual Studio 2008, if you used a class in your code that was not a member of any of the existing namespaces imported in your code then you had no choice but to look up the class in the documentation and enter a &lt;b&gt;using&lt;/b&gt; statement to import the new namespace.&lt;/p&gt;  &lt;p&gt;Visual Studio 2008 is smart enough to import namespaces for you automatically. If you type the name of a class that inhabits a namespace that has not been imported then Visual Studio displays a red bar beneath the class in the editor (see Figure 2). You can press CTRL-. to display a dialog box for picking the right namespace to import. Finally, just press the ENTER key to select a namespace (see Figure 3).&lt;/p&gt;  &lt;p&gt;&lt;b&gt;Figure 2&lt;/b&gt;&lt;/p&gt;  &lt;p&gt;&lt;a href="http://weblogs.asp.net/blogs/stephenwalther/WindowsLiveWriter/EssentialVisualStudioTipsTricksthatEvery_7EF8/clip_image004_2.jpg"&gt;&lt;img style="border-right: 0px; border-top: 0px; border-left: 0px; border-bottom: 0px" height="288" alt="clip_image004" src="http://weblogs.asp.net/blogs/stephenwalther/WindowsLiveWriter/EssentialVisualStudioTipsTricksthatEvery_7EF8/clip_image004_thumb.jpg" width="414" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Figure 3 &lt;/p&gt;  &lt;p&gt;&lt;a href="http://weblogs.asp.net/blogs/stephenwalther/WindowsLiveWriter/EssentialVisualStudioTipsTricksthatEvery_7EF8/clip_image006_2.jpg"&gt;&lt;img style="border-right: 0px; border-top: 0px; border-left: 0px; border-bottom: 0px" height="332" alt="clip_image006" src="http://weblogs.asp.net/blogs/stephenwalther/WindowsLiveWriter/EssentialVisualStudioTipsTricksthatEvery_7EF8/clip_image006_thumb.jpg" width="420" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;h4&gt;Tip #3 &amp;#8211; Never create properties by hand&lt;/h4&gt;  &lt;p&gt;Never type property declarations by hand. It takes forever! Instead, just type &lt;b&gt;prop + TAB + TAB&lt;/b&gt;. When you type prop + TAB + TAB, you get a code snippet (template) for entering a property. Use TAB to move between the template parameters. Press the ENTER key when you are finished creating the property (see Figure 4). This tip has saved me from many days of tedious property typing.&lt;/p&gt;  &lt;p&gt;&lt;b&gt;Figure 4&lt;/b&gt;&lt;/p&gt;  &lt;p&gt;&lt;a href="http://weblogs.asp.net/blogs/stephenwalther/WindowsLiveWriter/EssentialVisualStudioTipsTricksthatEvery_7EF8/clip_image008_2.jpg"&gt;&lt;img style="border-right: 0px; border-top: 0px; border-left: 0px; border-bottom: 0px" height="249" alt="clip_image008" src="http://weblogs.asp.net/blogs/stephenwalther/WindowsLiveWriter/EssentialVisualStudioTipsTricksthatEvery_7EF8/clip_image008_thumb.jpg" width="443" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;h4&gt;Tip #4 &amp;#8211; You can remove and sort unnecessary using statements&lt;/h4&gt;  &lt;p&gt;Whenever I finish creating a class, I always clean up the list of &lt;b&gt;using&lt;/b&gt; statements that appear at the top of the class file. I like to remove any unused &lt;b&gt;using&lt;/b&gt; statements to reduce the amount of visual clutter in my classes. You can remove &lt;b&gt;using&lt;/b&gt; statements that are not required by your code by right-clicking the top of your code file, selecting the menu option &lt;b&gt;Organize Usings&lt;/b&gt;, and select&lt;b&gt; Remove and Sort&lt;/b&gt;.&lt;/p&gt;  &lt;p&gt;&lt;b&gt;Figure 5&lt;/b&gt;&lt;/p&gt;  &lt;p&gt;&lt;a href="http://weblogs.asp.net/blogs/stephenwalther/WindowsLiveWriter/EssentialVisualStudioTipsTricksthatEvery_7EF8/clip_image010_2.jpg"&gt;&lt;img style="border-right: 0px; border-top: 0px; border-left: 0px; border-bottom: 0px" height="645" alt="clip_image010" src="http://weblogs.asp.net/blogs/stephenwalther/WindowsLiveWriter/EssentialVisualStudioTipsTricksthatEvery_7EF8/clip_image010_thumb.jpg" width="438" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;h4&gt;Tip #5 &amp;#8211; Use CTRL-k+c to comment out code&lt;/h4&gt;  &lt;p&gt;If you need to temporarily disable a block of code, or a section of an ASP.NET page, then you can comment out the region by pressing CTRL-k+c. I always do this when I want to rewrite an existing section of code, but I am afraid to delete the old code before writing the new code.&lt;/p&gt;  &lt;p&gt;For example, Figure 6 illustrates commented out code in the code editor.&lt;/p&gt;  &lt;p&gt;&lt;b&gt;Figure 6&lt;/b&gt;&lt;/p&gt;  &lt;p&gt;&lt;a href="http://weblogs.asp.net/blogs/stephenwalther/WindowsLiveWriter/EssentialVisualStudioTipsTricksthatEvery_7EF8/clip_image012_2.jpg"&gt;&lt;img style="border-right: 0px; border-top: 0px; border-left: 0px; border-bottom: 0px" height="378" alt="clip_image012" src="http://weblogs.asp.net/blogs/stephenwalther/WindowsLiveWriter/EssentialVisualStudioTipsTricksthatEvery_7EF8/clip_image012_thumb.jpg" width="569" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;You can use the very same key combination to comment out code almost anywhere. For example, you can comment out code in ASP.NET pages, web.config files, and JavaScript files (see Figure 7).&lt;/p&gt;  &lt;p&gt;&lt;b&gt;Figure 7&lt;/b&gt;&lt;/p&gt;  &lt;p&gt;&lt;a href="http://weblogs.asp.net/blogs/stephenwalther/WindowsLiveWriter/EssentialVisualStudioTipsTricksthatEvery_7EF8/clip_image014_2.jpg"&gt;&lt;img style="border-right: 0px; border-top: 0px; border-left: 0px; border-bottom: 0px" height="378" alt="clip_image014" src="http://weblogs.asp.net/blogs/stephenwalther/WindowsLiveWriter/EssentialVisualStudioTipsTricksthatEvery_7EF8/clip_image014_thumb.jpg" width="569" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;You can perform the opposite operation, and uncomment out code, by using the keyboard combination CTRL-k+u. &lt;/p&gt;  &lt;h4&gt;Tip #6 &amp;#8211; You can close all documents except the current one&lt;/h4&gt;  &lt;p&gt;After working in Visual Studio for an extended period of time, I end up with a lot of open documents. I like to quickly switch between open documents by hitting the keyboard combination CTRL-TAB (you can also use CTRL-TAB to shift focus to different tool windows). If you have too many documents open, using CTRL-TAB becomes more difficult because you must hunt through the set of open documents.&lt;/p&gt;  &lt;p&gt;There are two ways that you can close open documents. You can use the menu option &lt;b&gt;Window, Close All Documents&lt;/b&gt;. Better yet, you can right click a tab that corresponds to an open document and select the menu option &lt;b&gt;Close All But This&lt;/b&gt;. Selecting this latter option closes all open documents except the document corresponding to the tab (see Figure 8).&lt;/p&gt;  &lt;p&gt;&lt;b&gt;Figure 8&lt;/b&gt;&lt;/p&gt;  &lt;p&gt;&lt;a href="http://weblogs.asp.net/blogs/stephenwalther/WindowsLiveWriter/EssentialVisualStudioTipsTricksthatEvery_7EF8/clip_image016_2.jpg"&gt;&lt;img style="border-right: 0px; border-top: 0px; border-left: 0px; border-bottom: 0px" height="345" alt="clip_image016" src="http://weblogs.asp.net/blogs/stephenwalther/WindowsLiveWriter/EssentialVisualStudioTipsTricksthatEvery_7EF8/clip_image016_thumb.jpg" width="287" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;h4&gt;Tip #7 &amp;#8211; You can open a database by double-clicking the database file in App_Data&lt;/h4&gt;  &lt;p&gt;After you add a user instance of a SQL Express database to a project (a RANU database), you can quickly connect to the database by double-clicking the .mdf file in the App_Data folder. Double-clicking the database opens the Server Explorer/Database Explorer window and expands the objects in the database automatically.&lt;/p&gt;  &lt;h4&gt;Tip #8 &amp;#8211; You can copy a file or folder into a project by dragging and dropping &lt;/h4&gt;  &lt;p&gt;I&amp;#8217;m always composing new Visual Studio projects from bits and pieces of previous Visual Studio projects. For example, I might need to add a folder from a previous project or a set of files. You can add existing files to Visual Studio by right-clicking in the Solution Explorer window and selecting the menu option &lt;b&gt;Add, Existing Item&lt;/b&gt;. But, this method of adding files is slow. Furthermore, you can&amp;#8217;t add folders using this method.&lt;/p&gt;  &lt;p&gt;The best method of adding files and folders to Visual Studio project is to just drag the files or folder into the Solution Explorer window (or copy and paste the files or folder). For example, I am constantly adding my MoviesDB.mdf movies SQL Express database to new projects. I keep this file on my desktop and drag it into the App_Data folder whenever I need the database in a new Visual Studio project. &lt;/p&gt;  &lt;h4&gt;Tip #9 &amp;#8211; Use CTRL-SPACE to perform statement completion&lt;/h4&gt;  &lt;p&gt;This tip is particularly relevant for developers who build applications by using test-driven development. When doing test-driven development, you write a unit test first and then write code that satisfies the unit test. When writing the unit test, you often have to fight with statement auto-completion.&lt;/p&gt;  &lt;p&gt;There is an easy solution to this problem. Disable automatic statement completion by selecting &lt;b&gt;Tools, Options, Text Editor, All Languages&lt;/b&gt; and uncheck the &lt;b&gt;Auto list members&lt;/b&gt; checkbox (see Figure 9).&lt;/p&gt;  &lt;p&gt;&lt;b&gt;Figure 9&lt;/b&gt;&lt;/p&gt;  &lt;p&gt;&lt;a href="http://weblogs.asp.net/blogs/stephenwalther/WindowsLiveWriter/EssentialVisualStudioTipsTricksthatEvery_7EF8/clip_image018_2.jpg"&gt;&lt;img style="border-right: 0px; border-top: 0px; border-left: 0px; border-bottom: 0px" height="367" alt="clip_image018" src="http://weblogs.asp.net/blogs/stephenwalther/WindowsLiveWriter/EssentialVisualStudioTipsTricksthatEvery_7EF8/clip_image018_thumb.jpg" width="628" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;After you disable auto list members, you can still display suggestions for statement completion by using the keyboard combination CTRL-SPACE.&lt;/p&gt;  &lt;h4&gt;Tip #10 &amp;#8211; Add new items by pressing CTRL-N or CTRL-SHIFT+A&lt;/h4&gt;  &lt;p&gt;In general, using the mouse to perform an action in Visual Studio is slower than entering a keyboard combination. The fastest way to add a new item into a Visual Studio project is to hit the keyboard combination CTRL-n or the keyboard combination CTRL-SHIFT+A. The first keyboard combination works in ASP.NET Websites and the second keyboard combination works in both Website and ASP.NET MVC Web Application projects. &lt;/p&gt;  &lt;p&gt;This keyboard combination opens the Add New Item dialog box (see Figure 10).&lt;/p&gt;  &lt;p&gt;&lt;b&gt;Figure 10 &lt;/b&gt;&lt;/p&gt;  &lt;p&gt;&lt;a href="http://weblogs.asp.net/blogs/stephenwalther/WindowsLiveWriter/EssentialVisualStudioTipsTricksthatEvery_7EF8/clip_image020_2.jpg"&gt;&lt;img style="border-right: 0px; border-top: 0px; border-left: 0px; border-bottom: 0px" height="382" alt="clip_image020" src="http://weblogs.asp.net/blogs/stephenwalther/WindowsLiveWriter/EssentialVisualStudioTipsTricksthatEvery_7EF8/clip_image020_thumb.jpg" width="628" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;You can use the TAB and arrow keys to select an item from the dialog box and navigate to the Open button. Press the ENTER key to invoke the Open button.&lt;/p&gt;  &lt;h4&gt;Tip #11 &amp;#8211; You don&amp;#8217;t need to type file extensions when adding a file&lt;/h4&gt;  &lt;p&gt;This tip is related to the previous one. After using the TAB key to navigate to the &lt;b&gt;Name&lt;/b&gt; textbox in the Add New Item dialog box, you can enter the name of the new item. When typing the name of a new item, you don&amp;#8217;t need to include the file extension. Visual Studio can determine the file extension from the selected template.&lt;/p&gt;  &lt;p&gt;For example, when adding a new Web Form named MyPage.aspx, you can simply type MyPage. When adding a new Master Page named Site.master, you can simply type Site.&lt;/p&gt;  &lt;p&gt;You might think that avoiding typing a couple of characters would not matter. But, if you add dozens of files to a Visual Studio project, the number of characters that you can avoid typing quickly adds up.&lt;/p&gt;  &lt;h4&gt;Summary&lt;/h4&gt;  &lt;p&gt;If you have a tip or trick for Visual Studio 2008, please share it in the comments. However, nothing obscure please. I want to focus on only those tips and tricks that matter on a daily basis.&lt;/p&gt;&lt;img src="http://weblogs.asp.net/aggbug.aspx?PostID=6696632" width="1" height="1"&gt;</description><category domain="http://weblogs.asp.net/stephenwalther/archive/tags/ASP.NET+MVC/default.aspx">ASP.NET MVC</category><category domain="http://weblogs.asp.net/stephenwalther/archive/tags/ASP.NET/default.aspx">ASP.NET</category><category domain="http://weblogs.asp.net/stephenwalther/archive/tags/Tips/default.aspx">Tips</category></item><item><title>A Guide to Learning ASP.NET MVC Beta 1</title><link>http://weblogs.asp.net/stephenwalther/archive/2008/10/17/a-guide-to-learning-asp-net-mvc-beta-1.aspx</link><pubDate>Fri, 17 Oct 2008 15:18:32 GMT</pubDate><guid isPermaLink="false">c06e2b9d-981a-45b4-a55f-ab0d8bbfdc1c:6684697</guid><dc:creator>swalther</dc:creator><slash:comments>22</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://weblogs.asp.net/stephenwalther/rsscomments.aspx?PostID=6684697</wfw:commentRss><comments>http://weblogs.asp.net/stephenwalther/archive/2008/10/17/a-guide-to-learning-asp-net-mvc-beta-1.aspx#comments</comments><description>&lt;p&gt;Now that ASP.NET MVC Beta 1 is out, how do you learn how to use it? How do you learn how to build ASP.NET MVC applications? Here are my recommendations.&lt;/p&gt;  &lt;p&gt;The main website for all information related to ASP.NET MVC is located at:&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;&lt;a href="http://www.ASP.net/mvc"&gt;http://www.ASP.net/mvc&lt;/a&gt;&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;This website contains a large number of videos, tutorials, and links to blogs and documentation. Here is how I would suggest navigating all of this material.&lt;/p&gt;  &lt;p&gt;First, I would recommend watching the video &lt;b&gt;Creating a Tasklist Application with ASP.NET MVC&lt;/b&gt; located at: &lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;&lt;a href="http://www.asp.net/learn/mvc-videos/video-395.aspx"&gt;http://www.asp.net/learn/mvc-videos/video-395.aspx&lt;/a&gt;&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;In this 38 minute video, I walk through creating a very basic MVC application from start to finish. The sample application is nothing fancy. The goal of the video is to give you an overall sense of the different parts of an ASP.NET MVC application.&lt;/p&gt;  &lt;p&gt;If you want to learn how to build a more complex application then I would recommend that you watch the series of Pair Programming videos that I created with Paul Litwin. In these videos, we build a &lt;b&gt;Workout Log&lt;/b&gt; application. This is a simple database driven application that includes form validation. You can view this series of videos here:&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;&lt;a href="http://www.asp.net/learn/mvc-videos/#MVCPairProgramming"&gt;http://www.asp.net/learn/mvc-videos/#MVCPairProgramming&lt;/a&gt;&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;If you are interested in watching a series of videos that really dive into the advanced features of building an ASP.NET MVC application, then I would suggest that you watch Rob Conery&amp;#8217;s series of videos on building an e-commerce application:&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;&lt;a href="http://www.asp.net/learn/mvc-videos/#MVCStorefrontStarterKit"&gt;http://www.asp.net/learn/mvc-videos/#MVCStorefrontStarterKit&lt;/a&gt;&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;Next, to get a better understanding of each of the different features of ASP.NET MVC, I would recommend that you take a look at the preview of the ASP.NET MVC Quickstarts. The Quickstarts provide you with basic documentation on each of the different features of the ASP.NET MVC framework:&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;&lt;a href="http://quickstarts.asp.net/previews/mvc/"&gt;http://quickstarts.asp.net/previews/mvc/&lt;/a&gt;&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;Next, take advantage of the ASP.NET MVC tutorials and videos located at:&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;&lt;a href="http://www.ASP.net/mvc"&gt;http://www.ASP.net/mvc&lt;/a&gt;&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;The tutorials and videos are cross-linked. You can choose to either watch the video or read the corresponding tutorial. The written tutorials are available in both VB.NET and C#. Here&amp;#8217;s a list of the current tutorials available at the &lt;a href="http://www.ASP.net/mvc"&gt;www.ASP.net/mvc&lt;/a&gt; website:&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;&lt;a href="http://www.asp.net/learn/mvc/tutorial-01-vb.aspx"&gt;Creating a Tasklist Application with ASP.NET MVC&lt;/a&gt;&lt;/p&gt;    &lt;p&gt;Stephen Walther builds an entire ASP.NET MVC application from start to finish. This tutorial is a great introduction for people who are new to the ASP.NET MVC Framework and who want to get a sense of the process of building an ASP.NET MVC application.&lt;/p&gt;    &lt;h5&gt;&lt;a href="http://www.asp.net/learn/mvc/tutorial-02-vb.aspx"&gt;Understanding Models, Views, and Controllers&lt;/a&gt;&lt;/h5&gt;    &lt;p&gt;Confused about Models, Views, and Controllers? In this tutorial, Stephen Walther introduces you to the different parts of an ASP.NET MVC application.&lt;/p&gt;    &lt;h5&gt;&lt;a href="http://www.asp.net/learn/mvc/tutorial-03-vb.aspx"&gt;Understanding Controllers, Controller Actions, and Action Results&lt;/a&gt;&lt;/h5&gt;    &lt;p&gt;In this tutorial, Stephen Walther introduces you to ASP.NET MVC controllers. You learn how to create new controllers and return different types of action results.&lt;/p&gt;    &lt;h5&gt;&lt;a href="http://www.asp.net/learn/mvc/tutorial-04-vb.aspx"&gt;Understanding Views, View Data, and HTML Helpers&lt;/a&gt;&lt;/h5&gt;    &lt;p&gt;What is an ASP.NET MVC View and how does it differ from a HTML page? In this tutorial, Stephen Walther introduces you to Views and demonstrates how you can take advantage of View Data and HTML Helpers within a View.&lt;/p&gt;    &lt;h5&gt;&lt;a href="http://www.asp.net/learn/mvc/tutorial-05-vb.aspx"&gt;An Introduction to URL Routing&lt;/a&gt;&lt;/h5&gt;    &lt;p&gt;In this tutorial, Stephen Walther demonstrates how browser requests get mapped to controller actions through URL Routing. You also learn how to create a custom route that displays different blog entries depending on the date passed in a URL.&lt;/p&gt;    &lt;h5&gt;&lt;a href="http://www.asp.net/learn/mvc/tutorial-06-vb.aspx"&gt;Preventing JavaScript Injection Attacks&lt;/a&gt;&lt;/h5&gt;    &lt;p&gt;Prevent JavaScript Injection Attacks and Cross-Site Scripting Attacks from happening to you. In this tutorial, Stephen Walther explains how you can easily defeat these types of attacks by HTML encoding your content.&lt;/p&gt;    &lt;h5&gt;&lt;a href="http://www.asp.net/learn/mvc/tutorial-07-vb.aspx"&gt;Creating Unit Tests for ASP.NET MVC Applications&lt;/a&gt;&lt;/h5&gt;    &lt;p&gt;Learn how to create unit tests for controller actions. In this tutorial, Stephen Walther demonstrates how to test whether a controller action returns a particular view, returns a particular set of data, or returns a different type of action result.&lt;/p&gt;    &lt;h5&gt;&lt;a href="http://www.asp.net/learn/mvc/tutorial-08-vb.aspx"&gt;Creating Custom HTML Helpers&lt;/a&gt;&lt;/h5&gt;    &lt;p&gt;Create new HTML Helpers to make it easier to generate view content. Stephen Walther demonstrates two simple methods of creating new HTML Helpers.&lt;/p&gt;    &lt;h5&gt;&lt;a href="http://www.asp.net/learn/mvc/tutorial-09-vb.aspx"&gt;Creating Model Classes with LINQ to SQL&lt;/a&gt;&lt;/h5&gt;    &lt;p&gt;Learn how to create data access classes by taking advantage of Microsoft LINQ to SQL. In this video tutorial, you learn how to use a LINQ to SQL DataContext class directly within a controller. You also learn how to use the Repository Pattern to create testable and flexible database-driven applications.&lt;/p&gt;    &lt;h5&gt;&lt;a href="http://www.asp.net/learn/mvc/tutorial-10-vb.aspx"&gt;Displaying a Table of Database Data&lt;/a&gt;&lt;/h5&gt;    &lt;p&gt;Need to display a set of database records in an MVC view? Learn two methods of displaying records in an HTML table. You learn how to perform all of the formatting inline. You also learn how to create a template with an MVC partial.&lt;/p&gt;    &lt;h5&gt;&lt;a href="http://www.asp.net/learn/mvc/tutorial-11-vb.aspx"&gt;Using ASP.NET MVC with Different Versions of IIS&lt;/a&gt;&lt;/h5&gt;    &lt;p&gt;In this tutorial, you learn how to use ASP.NET MVC, and URL Routing, with different versions of Internet Information Services. You learn different strategies for using ASP.NET MVC with IIS 7.0 (classic mode), IIS 6.0, and earlier versions of IIS. &lt;/p&gt;    &lt;h5&gt;&lt;a href="http://www.asp.net/learn/mvc/tutorial-12-vb.aspx"&gt;Creating Page Layouts with View Master Pages&lt;/a&gt;&lt;/h5&gt;    &lt;p&gt;By taking advantage of view master pages, you can create a common layout for the pages in your ASP.NET MVC application. In this tutorial, Stephen Walther introduces you to view master pages. You learn how to create a two-column page layout.&lt;/p&gt;    &lt;h5&gt;&lt;a href="http://www.asp.net/learn/mvc/tutorial-13-vb.aspx"&gt;Passing Data to View Master Pages&lt;/a&gt;&lt;/h5&gt;    &lt;p&gt;In this tutorial, Stephen Walther explains how you can pass database data to a view master page. He demonstrates how to create an Application controller that modifies the view data returned by every controller action within an ASP.NET MVC application.&lt;/p&gt;    &lt;h5&gt;&lt;a href="http://www.asp.net/learn/mvc/tutorial-14-vb.aspx"&gt;Understanding Action Filters&lt;/a&gt;&lt;/h5&gt;    &lt;p&gt;In this tutorial, you are introduced to action filters. You learn how action filters work and how to implement custom action filters. We create a custom action filter that logs the stages of processing a controller action and action result to the Visual Studio Output window. &lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;Finally, there is a lot of really great ASP.NET MVC information in blogs. These are the blogs that I would read on a daily basis:&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;&lt;a href="http://weblogs.asp.net/scottgu/"&gt;ScottGu&amp;#8217;s Blog&lt;/a&gt; &amp;#8211; Scott Guthrie&amp;#8217;s blog is the best place to get the latest ASP.NET MVC announcements. He also writes really clearly written walkthroughs that describe how to build basic MVC applications.&lt;/p&gt;    &lt;p&gt;&lt;a href="http://haacked.com/"&gt;Haacked&lt;/a&gt; &amp;#8211; Phil Haack is the Program Manager for ASP.NET MVC.&lt;/p&gt;    &lt;p&gt;&lt;a href="http://stephenwalther.com/"&gt;Stephen Walther on ASP.NET MVC&lt;/a&gt; &amp;#8211; My blog on ASP.NET MVC. I&amp;#8217;ve written over 40 tips on building applications with ASP.NET MVC. I&amp;#8217;ve also been building two complete sample applications.&lt;/p&gt;    &lt;p&gt;&lt;a href="http://www.hanselman.com/blog/"&gt;Computer Zen&lt;/a&gt; &amp;#8211; Scott Hanselman&amp;#8217;s web log. Scott is another member of my team. His blog is a great source for all technical information on .NET.&lt;/p&gt;    &lt;p&gt;&lt;a href="http://blog.wekeroad.com/"&gt;WekeRoad&lt;/a&gt; &amp;#8211; Rob Conery has been building an entire ecommerce application with ASP.NET MVC.&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;Hope these resources help! If you have any other recommendations for sources of information on ASP.NET MVC, then please leave a comment. Remember that you can always find the latest ASP.NET MVC information by visiting the primary Microsoft ASP.NET MVC site:&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;&lt;a href="http://www.ASP.net/mvc"&gt;http://www.ASP.net/mvc&lt;/a&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;img src="http://weblogs.asp.net/aggbug.aspx?PostID=6684697" width="1" height="1"&gt;</description><category domain="http://weblogs.asp.net/stephenwalther/archive/tags/ASP.NET+MVC/default.aspx">ASP.NET MVC</category><category domain="http://weblogs.asp.net/stephenwalther/archive/tags/ASP.NET/default.aspx">ASP.NET</category></item><item><title>jQuery in ASP.NET Talk at PDC</title><link>http://weblogs.asp.net/stephenwalther/archive/2008/10/15/jquery-in-asp-net-talk-at-pdc.aspx</link><pubDate>Wed, 15 Oct 2008 13:24:54 GMT</pubDate><guid isPermaLink="false">c06e2b9d-981a-45b4-a55f-ab0d8bbfdc1c:6682071</guid><dc:creator>swalther</dc:creator><slash:comments>4</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://weblogs.asp.net/stephenwalther/rsscomments.aspx?PostID=6682071</wfw:commentRss><comments>http://weblogs.asp.net/stephenwalther/archive/2008/10/15/jquery-in-asp-net-talk-at-pdc.aspx#comments</comments><description>&lt;p&gt;Mark your calendars and come to my &lt;strong&gt;jQuery in ASP.NET&lt;/strong&gt; talk at PDC. It looks like my talk is scheduled for Tuesday (10/28/2008) at 5:15pm. You can still register for the PDC here:&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;&lt;a title="http://www.microsoftpdc.com/" href="http://www.microsoftpdc.com/"&gt;http://www.microsoftpdc.com/&lt;/a&gt;&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;I'm really excited about the new possibilities that jQuery, in combination with the new features of the ASP.NET AJAX framework, opens up for building Ajax applications. My talk discusses using jQuery in the context of both ASP.NET Web Forms and ASP.NET MVC applications.&lt;/p&gt;  &lt;p&gt;Here's the talk description:&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;blockquote&gt;   &lt;h3&gt;Introducing jQuery in ASP.NET &lt;/h3&gt;    &lt;p&gt;jQuery is an open source JavaScript library that has a passionate following among Ajax developers. Microsoft is integrating the open source jQuery library into both the ASP.NET Web Forms and ASP.NET MVC frameworks and providing full product support.&lt;/p&gt;    &lt;p&gt;ASP.NET AJAX and jQuery is a powerful combination for building client-side, database-driven web applications. By taking advantage of ASP.NET AJAX client templates and controls, you can create declarative client-side user interfaces. By taking advantage of jQuery&amp;#8217;s rich selector and animation support, you can easily select page elements and augment your pages with compelling visual effects.&lt;/p&gt;    &lt;p&gt;In this talk, learn how you can use jQuery to build richly interactive client-side Ajax applications when developing either ASP.NET Web Forms or ASP.NET MVC applications. Learn how JQuery works in combination with ASP.NET AJAX to provide the best framework for building Ajax applications.&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;Looking forward to seeing you at the PDC!&lt;/p&gt;&lt;img src="http://weblogs.asp.net/aggbug.aspx?PostID=6682071" width="1" height="1"&gt;</description><category domain="http://weblogs.asp.net/stephenwalther/archive/tags/JavaScript/default.aspx">JavaScript</category><category domain="http://weblogs.asp.net/stephenwalther/archive/tags/AJAX/default.aspx">AJAX</category><category domain="http://weblogs.asp.net/stephenwalther/archive/tags/ASP.NET+MVC/default.aspx">ASP.NET MVC</category><category domain="http://weblogs.asp.net/stephenwalther/archive/tags/ASP.NET/default.aspx">ASP.NET</category><category domain="http://weblogs.asp.net/stephenwalther/archive/tags/Talks/default.aspx">Talks</category></item><item><title>New ASP.NET MVC Tutorials</title><link>http://weblogs.asp.net/stephenwalther/archive/2008/10/10/new-asp-net-mvc-tutorials.aspx</link><pubDate>Sat, 11 Oct 2008 00:00:03 GMT</pubDate><guid isPermaLink="false">c06e2b9d-981a-45b4-a55f-ab0d8bbfdc1c:6670580</guid><dc:creator>swalther</dc:creator><slash:comments>13</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://weblogs.asp.net/stephenwalther/rsscomments.aspx?PostID=6670580</wfw:commentRss><comments>http://weblogs.asp.net/stephenwalther/archive/2008/10/10/new-asp-net-mvc-tutorials.aspx#comments</comments><description>&lt;p&gt;I created three new ASP.NET MVC tutorials that were just published at the &lt;a href="http://www.ASP.net/mvc"&gt;www.ASP.net/mvc&lt;/a&gt; website. There are both C# and VB.NET versions of each tutorial. Here are the direct links and descriptions:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;     &lt;h5&gt;&lt;a href="http://www.asp.net/learn/mvc/tutorial-09-vb.aspx" target="_blank"&gt;Creating Custom HTML Helpers&lt;/a&gt;&lt;/h5&gt;   &lt;/li&gt; &lt;/ul&gt;  &lt;blockquote&gt;   &lt;p&gt;Create new HTML Helpers to make it easier to generate view content. Stephen Walther demonstrates two simple methods of creating new HTML Helpers.&lt;/p&gt; &lt;/blockquote&gt;  &lt;ul&gt;   &lt;li&gt;     &lt;h5&gt;&lt;a href="http://www.asp.net/learn/mvc/tutorial-10-vb.aspx" target="_blank"&gt;Creating Model Classes with LINQ to SQL&lt;/a&gt;&lt;/h5&gt;   &lt;/li&gt; &lt;/ul&gt;  &lt;blockquote&gt;   &lt;p&gt;Learn how to create data access classes by taking advantage of Microsoft LINQ to SQL. In this video tutorial, you learn how to use a LINQ to SQL DataContext class directly within a controller. You also learn how to use the Repository Pattern to create testable and flexible database-driven applications.&lt;/p&gt; &lt;/blockquote&gt;  &lt;ul&gt;   &lt;li&gt;     &lt;h5&gt;&lt;a href="http://www.asp.net/learn/mvc/tutorial-11-vb.aspx" target="_blank"&gt;Displaying a Table of Database Data&lt;/a&gt;&lt;/h5&gt;   &lt;/li&gt; &lt;/ul&gt;  &lt;blockquote&gt;   &lt;p&gt;Need to display a set of database records in an MVC view? Learn two methods of displaying records in an HTML table. You learn how to perform all of the formatting inline. You also learn how to create a template with an MVC partial.&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;Please let me know if there are specific tutorials that you want to see in the future.&lt;/p&gt;&lt;img src="http://weblogs.asp.net/aggbug.aspx?PostID=6670580" width="1" height="1"&gt;</description><category domain="http://weblogs.asp.net/stephenwalther/archive/tags/ASP.NET+MVC/default.aspx">ASP.NET MVC</category><category domain="http://weblogs.asp.net/stephenwalther/archive/tags/ASP.NET/default.aspx">ASP.NET</category></item><item><title>ASP.NET MVC Tip #45 – Use Client View Data</title><link>http://weblogs.asp.net/stephenwalther/archive/2008/10/08/asp-net-mvc-tip-45-use-client-view-data.aspx</link><pubDate>Wed, 08 Oct 2008 16:44:08 GMT</pubDate><guid isPermaLink="false">c06e2b9d-981a-45b4-a55f-ab0d8bbfdc1c:6665799</guid><dc:creator>swalther</dc:creator><slash:comments>10</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://weblogs.asp.net/stephenwalther/rsscomments.aspx?PostID=6665799</wfw:commentRss><comments>http://weblogs.asp.net/stephenwalther/archive/2008/10/08/asp-net-mvc-tip-45-use-client-view-data.aspx#comments</comments><description>&lt;blockquote&gt;   &lt;p&gt;&lt;i&gt;In this tip, I explore one approach to building Ajax applications with ASP.NET MVC. I show how you can use view data when building Ajax applications with ASP.NET MVC in the same way as you would use view data when building server-side application. I demonstrate how to create a custom HTML Helper that renders client view data.&lt;/i&gt;&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;One of the primary benefits of building an ASP.NET MVC application is that it enables you to build web applications that support a sharp separation of concerns. This sharp separation of concerns enables you to build applications that are highly testable and highly adaptable to future change.&lt;/p&gt;  &lt;p&gt;Communication between the different parts of an ASP.NET MVC application is extremely constrained. An MVC view only talks to an MVC controller and never directly to an MVC model class. The only way that a view is allowed to talk to a controller is through view data. These constraints enable the sharp separation of concern.&lt;/p&gt;  &lt;p&gt;If you are not careful, you can break this clear separation of concerns when building Ajax applications. In an Ajax application, the browser talks directly to the server. An Ajax request is made against a controller action and the controller action returns a JSON result.&lt;/p&gt;  &lt;p&gt;In this tip, I demonstrate how you can use view data to pass data from a controller action to a view in an Ajax application in the very same that you use view data in a non-Ajax application. In this tip, I demonstrate how you can create a custom HTML Helper that renders client view data. &lt;/p&gt;  &lt;h4&gt;Creating the Client View Data Helper&lt;/h4&gt;  &lt;p&gt;The code for the client view data HTML Helper is contained in Listing 1. The ClientViewDataHelper class defines an extension method, named ClientViewData(), that extends the AjaxHelper helper class (The AjaxHelper class is exposed as the View.Ajax property in a server-side view).&lt;/p&gt;  &lt;p&gt;&lt;b&gt;Listing 1 &amp;#8211; MvcAjax\ClientViewDataHelper.cs&lt;/b&gt;&lt;/p&gt;  &lt;pre class="c#" name="code"&gt;using System.Web.Mvc;

namespace MvcAjax
{
    public static class ClientViewDataHelper
    {

        public static string ClientViewData(this AjaxHelper helper)
        {
            var clientViewData = new ClientViewData();
            var context = helper.ViewContext;
            var result = clientViewData.Serialize(context.Controller.ViewData);
            result = &amp;quot;&amp;lt;script type='text/javascript'&amp;gt; var viewData=&amp;quot; + result + &amp;quot;; &amp;lt;/script&amp;gt;&amp;quot;;
            return result;
        }

    }
}&lt;/pre&gt;

&lt;p&gt;The ClientViewData() helper renders the current view&amp;#8217;s view data into a JavaScript object. The Helper makes the server-side view data available to client-side JavaScript code.&lt;/p&gt;

&lt;p&gt;The ClientViewData() HTML Helper calls the ClientViewData.Serialize() method to serialize the server-side view data into the client-side object. The code for the ClientViewData class is contained in Listing 2.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Listing 2 &amp;#8211; MvcAjax\ClientViewData.cs&lt;/b&gt;&lt;/p&gt;

&lt;pre class="c#" name="code"&gt;using System.Web.Mvc;
using System.Web.Script.Serialization;

namespace MvcAjax
{
    public class ClientViewData
    {

        public string Serialize(ViewDataDictionary viewData)
        {
            var ser = new JavaScriptSerializer();
            ser.RegisterConverters(new JavaScriptConverter[] { new ViewDataConverter(), new ModelStateConverter() });
            return ser.Serialize(viewData);
        }


    }
}&lt;/pre&gt;

&lt;p&gt;The ClientViewData class takes advantage of the JavaScriptSerializer class included in the .NET framework. Notice that the JavaScriptSerializer is configured to use two custom JavaScript convertors named ViewDataConverter and ModelStateConverter. These special converters are needed to properly convert the ViewDataDictionary and ModelStateDictionary objects into JavaScript objects (the converters are included in the code download at the end of this blog entry).&lt;/p&gt;

&lt;h4&gt;A Walkthrough of Client View Data&lt;/h4&gt;

&lt;p&gt;To demonstrate how you can use the ClientViewData() helper in an ASP.NET MVC Ajax application, let&amp;#8217;s do a walkthrough of creating a basic Movie database application. Our application will consist of a single page that contains a dropdown list of movie categories and an HTML table that displays movies that match the selected category (see Figure 1).&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Figure 1 &amp;#8211; The Movie Database Application&lt;/b&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://weblogs.asp.net/blogs/stephenwalther/WindowsLiveWriter/ASP.NETMVCTip45UseClientViewData_88DE/clip_image002_2.jpg"&gt;&lt;img style="border-right: 0px; border-top: 0px; border-left: 0px; border-bottom: 0px" height="426" alt="clip_image002" src="http://weblogs.asp.net/blogs/stephenwalther/WindowsLiveWriter/ASP.NETMVCTip45UseClientViewData_88DE/clip_image002_thumb.jpg" width="431" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here&amp;#8217;s the cool part. Both the list of movie categories and the list of movies are stored in client view data. When the page is first rendered to the browser, the categories and movies are included in a JavaScript object rendered by the ClientViewData() helper. When you select a new movie category, the client view data is updated with an Ajax call. All interaction between the view (both client-side and server-side) happens through view data. A sharp separation of concerns is maintained.&lt;/p&gt;

&lt;p&gt;The view is contained in Listing 3. There&amp;#8217;s a lot happening in this view.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Listing 3 &amp;#8211; Views\Home\Index.aspx&lt;/b&gt;&lt;/p&gt;

&lt;pre class="xml" name="code"&gt;&amp;lt;%@ Page Language=&amp;quot;C#&amp;quot; AutoEventWireup=&amp;quot;true&amp;quot; CodeBehind=&amp;quot;Index.aspx.cs&amp;quot; Inherits=&amp;quot;Tip45.Views.Home.Index&amp;quot; %&amp;gt;
&amp;lt;%@ Import Namespace=&amp;quot;MvcAjax&amp;quot; %&amp;gt;
&amp;lt;!DOCTYPE html PUBLIC &amp;quot;-//W3C//DTD XHTML 1.0 Transitional//EN&amp;quot; &amp;quot;http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd&amp;quot;&amp;gt;
&amp;lt;html xmlns=&amp;quot;http://www.w3.org/1999/xhtml&amp;quot; &amp;gt;
&amp;lt;head runat=&amp;quot;server&amp;quot;&amp;gt;
    &amp;lt;title&amp;gt;Index&amp;lt;/title&amp;gt;
    
    &amp;lt;style type=&amp;quot;text/css&amp;quot;&amp;gt;
    
    #movieContainer
    {
        display:none;
    }
    
    td, th
    {
        padding: 5px;
    }
    
    &amp;lt;/style&amp;gt;

    &amp;lt;script type=&amp;quot;text/javascript&amp;quot; src=&amp;quot;../../Content/MicrosoftAjax.debug.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;
    &amp;lt;script type=&amp;quot;text/javascript&amp;quot; src=&amp;quot;../../Content/MicrosoftAjaxTemplates.debug.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;
    &amp;lt;script type=&amp;quot;text/javascript&amp;quot; src=&amp;quot;../../Content/jquery-1.2.6.min.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;   
    &amp;lt;script type=&amp;quot;text/javascript&amp;quot; src=&amp;quot;../../Content/MvcAjax.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt; 
    &amp;lt;script type=&amp;quot;text/javascript&amp;quot;&amp;gt;

        var categoryView;
        var moviesView;

        $(pageReady);

        function pageReady()
        {
            categoryView = $create(Sys.UI.DataView, {}, {}, {}, $get(&amp;quot;categories&amp;quot;));
            movieView = $create(Sys.UI.DataView, {}, {}, {}, $get(&amp;quot;movies&amp;quot;));
        
            showCategories();
            $(&amp;quot;#categories&amp;quot;).change(function() { selectCategory($(this).val()); });
        }

        function selectCategory(categoryId)
        {
            if (categoryId)
            {
                $(&amp;quot;#movieContainer&amp;quot;).hide();
                updateViewData(&amp;quot;/Home/Movies/&amp;quot; + categoryId, &amp;quot;movies&amp;quot;, showMovies);
            }
        }

        function showCategories()
        {
            categoryView.set_data(viewData.categories);
            $(&amp;quot;#categories&amp;quot;).prepend( $(&amp;quot;&amp;lt;option&amp;gt;Select Category&amp;lt;/option&amp;gt;&amp;quot;) )[0].selectedIndex = 0;
        }

        function showMovies()
        {
            movieView.set_data(viewData.movies);
            $(&amp;quot;#movieContainer&amp;quot;).fadeIn(&amp;quot;normal&amp;quot;);            
        }    
    
    &amp;lt;/script&amp;gt;

&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
    &amp;lt;div&amp;gt;
    

    &amp;lt;%= Ajax.ClientViewData() %&amp;gt;

    &amp;lt;label for=&amp;quot;categories&amp;quot;&amp;gt;Category:&amp;lt;/label&amp;gt;
    &amp;lt;select id=&amp;quot;categories&amp;quot;&amp;gt;
    &amp;lt;option value=&amp;quot;{{Id}}&amp;quot;&amp;gt;{{Name}}&amp;lt;/option&amp;gt;
    &amp;lt;/select&amp;gt;

    &amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;

    &amp;lt;table id=&amp;quot;movieContainer&amp;quot;&amp;gt;
    &amp;lt;thead&amp;gt;
        &amp;lt;tr&amp;gt;
            &amp;lt;th&amp;gt;Title&amp;lt;/th&amp;gt;
            &amp;lt;th&amp;gt;Director&amp;lt;/th&amp;gt;
        &amp;lt;/tr&amp;gt;
    &amp;lt;/thead&amp;gt;
    &amp;lt;tbody id=&amp;quot;movies&amp;quot;&amp;gt;  
        &amp;lt;tr&amp;gt;
            &amp;lt;td&amp;gt;{{Title}}&amp;lt;/td&amp;gt;
            &amp;lt;td&amp;gt;{{Director}}&amp;lt;/td&amp;gt;        
        &amp;lt;/tr&amp;gt;
    &amp;lt;/tbody&amp;gt;
    &amp;lt;/table&amp;gt;

    
    &amp;lt;/div&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;&lt;/pre&gt;

&lt;p&gt;The first thing that you should notice is that the ClientViewData() helper method is called in the body of the page. The ClientViewData() helper renders the code in Listing 4.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Listing 4 &amp;#8211; Rendered Client View Data&lt;/b&gt;&lt;/p&gt;

&lt;p&gt;&amp;lt;script type='text/javascript'&amp;gt; var viewData={&amp;quot;categories&amp;quot;:[{&amp;quot;Id&amp;quot;:1,&amp;quot;Name&amp;quot;:&amp;quot;Adventure&amp;quot;,&amp;quot;Position&amp;quot;:1},{&amp;quot;Id&amp;quot;:2,&amp;quot;Name&amp;quot;:&amp;quot;Animation&amp;quot;,&amp;quot;Position&amp;quot;:2},{&amp;quot;Id&amp;quot;:3,&amp;quot;Name&amp;quot;:&amp;quot;Drama&amp;quot;,&amp;quot;Position&amp;quot;:2},{&amp;quot;Id&amp;quot;:4,&amp;quot;Name&amp;quot;:&amp;quot;Horror&amp;quot;,&amp;quot;Position&amp;quot;:-2}],&amp;quot;movies&amp;quot;:[{&amp;quot;Id&amp;quot;:1,&amp;quot;CategoryId&amp;quot;:3,&amp;quot;Title&amp;quot;:&amp;quot;Titanic&amp;quot;,&amp;quot;Director&amp;quot;:&amp;quot;James Cameron&amp;quot;,&amp;quot;DateReleased&amp;quot;:&amp;quot;\/Date(866876400000)\/&amp;quot;},{&amp;quot;Id&amp;quot;:2,&amp;quot;CategoryId&amp;quot;:1,&amp;quot;Title&amp;quot;:&amp;quot;Star Wars II&amp;quot;,&amp;quot;Director&amp;quot;:&amp;quot;George Lucas&amp;quot;,&amp;quot;DateReleased&amp;quot;:&amp;quot;\/Date(233996400000)\/&amp;quot;},{&amp;quot;Id&amp;quot;:3,&amp;quot;CategoryId&amp;quot;:1,&amp;quot;Title&amp;quot;:&amp;quot;Jurassic Park&amp;quot;,&amp;quot;Director&amp;quot;:&amp;quot;Steven Spielberg&amp;quot;,&amp;quot;DateReleased&amp;quot;:&amp;quot;\/Date(740300400000)\/&amp;quot;},{&amp;quot;Id&amp;quot;:4,&amp;quot;CategoryId&amp;quot;:4,&amp;quot;Title&amp;quot;:&amp;quot;Jaws&amp;quot;,&amp;quot;Director&amp;quot;:&amp;quot;Steven Spielberg&amp;quot;,&amp;quot;DateReleased&amp;quot;:&amp;quot;\/Date(170665200000)\/&amp;quot;},{&amp;quot;Id&amp;quot;:5,&amp;quot;CategoryId&amp;quot;:4,&amp;quot;Title&amp;quot;:&amp;quot;Ghost&amp;quot;,&amp;quot;Director&amp;quot;:&amp;quot;Jerry Zucker&amp;quot;,&amp;quot;DateReleased&amp;quot;:&amp;quot;\/Date(645346800000)\/&amp;quot;},{&amp;quot;Id&amp;quot;:7,&amp;quot;CategoryId&amp;quot;:3,&amp;quot;Title&amp;quot;:&amp;quot;Forrest Gump&amp;quot;,&amp;quot;Director&amp;quot;:&amp;quot;Robert Zemeckis&amp;quot;,&amp;quot;DateReleased&amp;quot;:&amp;quot;\/Date(771922800000)\/&amp;quot;},{&amp;quot;Id&amp;quot;:8,&amp;quot;CategoryId&amp;quot;:2,&amp;quot;Title&amp;quot;:&amp;quot;Ice Age&amp;quot;,&amp;quot;Director&amp;quot;:&amp;quot;Chris Wedge&amp;quot;,&amp;quot;DateReleased&amp;quot;:&amp;quot;\/Date(1025074800000)\/&amp;quot;},{&amp;quot;Id&amp;quot;:9,&amp;quot;CategoryId&amp;quot;:2,&amp;quot;Title&amp;quot;:&amp;quot;Shrek&amp;quot;,&amp;quot;Director&amp;quot;:&amp;quot;Andrew Adamson&amp;quot;,&amp;quot;DateReleased&amp;quot;:&amp;quot;\/Date(993452400000)\/&amp;quot;},{&amp;quot;Id&amp;quot;:10,&amp;quot;CategoryId&amp;quot;:1,&amp;quot;Title&amp;quot;:&amp;quot;Independence Day&amp;quot;,&amp;quot;Director&amp;quot;:&amp;quot;Roland Emmerich&amp;quot;,&amp;quot;DateReleased&amp;quot;:&amp;quot;\/Date(835254000000)\/&amp;quot;},{&amp;quot;Id&amp;quot;:22,&amp;quot;CategoryId&amp;quot;:4,&amp;quot;Title&amp;quot;:&amp;quot;The Ring&amp;quot;,&amp;quot;Director&amp;quot;:&amp;quot;Gore Verbinski&amp;quot;,&amp;quot;DateReleased&amp;quot;:&amp;quot;\/Date(1057388400000)\/&amp;quot;}],&amp;quot;model&amp;quot;:null,&amp;quot;modelState&amp;quot;:{&amp;quot;isValid&amp;quot;:true}}; &amp;lt;/script&amp;gt;&lt;/p&gt;

&lt;p&gt;The code in Listing 4 is hard to read, but if you look closely then you&amp;#8217;ll notice that the code defines a JavaScript object named viewData. The viewData object has two properties named categories and movies that represent the movie categories and movies.&lt;/p&gt;

&lt;p&gt;The view takes advantage of both ASP.NET AJAX and jQuery to render the list of movie categories and movies. When the view is first loaded in the browser, the following code executes:&lt;/p&gt;

&lt;pre class="js:nocontrols" name="code"&gt;var categoryView;
var moviesView;

$(pageReady);

function pageReady()
{
   categoryView = $create(Sys.UI.DataView, {}, {}, {}, $get(&amp;quot;categories&amp;quot;));
   movieView = $create(Sys.UI.DataView, {}, {}, {}, $get(&amp;quot;movies&amp;quot;));
        
   showCategories();
   $(&amp;quot;#categories&amp;quot;).change(function() { selectCategory($(this).val()); });
}&lt;/pre&gt;

&lt;p&gt;The jQuery command $(pageReady) causes the pageReady() function to execute after the page DOM is fully loaded. The pageReady() function creates two ASP.NET AJAX client-side controls. Two ASP.NET AJAX DataView controls that represent the dropdown list of categories and HTML table of movies are created.&lt;/p&gt;

&lt;p&gt;Next, the showCategories() method is called. This method adds the categories from view data to the dropdown list. The showCategories() function looks like this:&lt;/p&gt;

&lt;pre class="js:nocontrols" name="code"&gt;function showCategories()
{
    categoryView.set_data(viewData.categories);
    $(&amp;quot;#categories&amp;quot;).prepend( $(&amp;quot;&amp;lt;option&amp;gt;Select Category&amp;lt;/option&amp;gt;&amp;quot;) )[0].selectedIndex = 0;
}&lt;/pre&gt;

&lt;p&gt;This simple function assigns the categories from view data to the categoryView ASP.NET AJAX control with the help of the set_data() method. Next, the function adds a default dropdown list option labeled &lt;b&gt;Select Category&lt;/b&gt;.&lt;/p&gt;

&lt;p&gt;When you select a new category, the selectCategory() function executes:&lt;/p&gt;

&lt;pre class="js:nocontrols" name="code"&gt;function selectCategory(categoryId)
{
    if (categoryId)
    {
        $(&amp;quot;#movieContainer&amp;quot;).hide();
        updateViewData(&amp;quot;/Home/Movies/&amp;quot; + categoryId, &amp;quot;movies&amp;quot;, showMovies);
    }
}&lt;/pre&gt;

&lt;p&gt;This function starts by hiding the HTML table that contains the movies. Next, it calls a method named updateViewData() to invoke a server-side ASP.NET MVC action and get the list of matching movies. The matching movies are assigned to the client view data viewData.movies property. Finally, the updateViewData() method calls the showMovies() method to show the movies represented by the client viewData.movies property in the HTML table.&lt;/p&gt;

&lt;p&gt;Here is the code for showMovies():&lt;/p&gt;

&lt;pre class="js:nocontrols" name="code"&gt;function showMovies()
{
    movieView.set_data(viewData.movies);
    $(&amp;quot;#movieContainer&amp;quot;).fadeIn(&amp;quot;normal&amp;quot;);            
}    &lt;/pre&gt;

&lt;p&gt;Just like the showCategories() method, the showMovies() method retrieves the data from client view data and assigns the data to the DataView. The showMovies() method then fades in the HTML table of movies by taking advantage of a jQuery animation effect.&lt;/p&gt;

&lt;p&gt;The final piece of this application is the controller that returns the view and returns the movie data. The controller is contained in Listing 4.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Listing 4 &amp;#8211; Controllers\HomeController.cs&lt;/b&gt;&lt;/p&gt;

&lt;pre class="c#" name="code"&gt;using System.Web.Mvc;
using Tip45.Models;

namespace Tip45.Controllers
{
    [HandleError]
    public class HomeController : Controller
    {
        private IMovieRepository _repository;

        public HomeController()
        {
            _repository = new MovieRepository();
        }

        public ViewResult Index()
        {

            ViewData[&amp;quot;categories&amp;quot;] = _repository.ListCategories();          
            ViewData[&amp;quot;movies&amp;quot;] = _repository.ListMovies();

            return View(&amp;quot;Index&amp;quot;);
        }

        public ActionResult Movies(int id)
        {
            return Json(_repository.ListMovies(id));
        }


    }
}&lt;/pre&gt;

&lt;p&gt;The Home controller is straightforward. It exposes two actions named Index() and Movies(). The Index() action returns the view. Notice that the categories and movies are added to server-side view data before the view is returned. The ClientViewData() helper in the Index view serializes the server-side view data to the client-side viewData JavaScript object.&lt;/p&gt;

&lt;p&gt;The Movies() action returns a JsonResult that represents a list of matching movies. The Movies() action is invoked by the client-side code in the selectCategory() method discussed previously. &lt;/p&gt;

&lt;h4&gt;A Moment of Reflection&lt;/h4&gt;

&lt;p&gt;The advantage of the approach discussed in this tip is that it enables you to preserve the same clean separation of concerns when building an ASP.NET MVC application as when building a server-side ASP.NET MVC application. By taking advantage of client view data, you can require that all communication between the client view and the server happen through view data.&lt;/p&gt;

&lt;p&gt;Another advantage of this approach is that the initial data for the categories dropdown list is available when the page first loads. A separate Ajax request does not need to be performed to retrieve the categories from the server because the categories are already present in the client view data.&lt;/p&gt;

&lt;p&gt;One potential disadvantage of the approach discussed in this blog entry is it does not support graceful degradation. If someone attempts to use the Movie database application with JavaScript disabled, then nothing works. The initial dropdown of categories will never be displayed.&lt;/p&gt;

&lt;p&gt;If you are building an ASP.NET application for internal use in your company then this drawback, most likely, won&amp;#8217;t matter to you. You can require everyone in your company to use a browser that supports JavaScript. &lt;/p&gt;

&lt;p&gt;If you are building a public facing website then this JavaScript requirement might be more of a concern. However, it is important to understand that everything in the code used in this tip is compatible with recent browsers (Internet Explorer, Firefox, Chrome). The change of the millennium was 8 years ago now, it is time to start expecting users to have modern browsers.&lt;/p&gt;

&lt;p&gt;If you are building a highly interactive web application, then requiring users to use a browser that supports JavaScript seems reasonable. To put things in perspective, it would be crazy to make it a requirement that a desktop application continue to work with C# turned off.&lt;/p&gt;

&lt;h4&gt;Summary&lt;/h4&gt;

&lt;p&gt;In this tip, I demonstrated how you can create an HTML Helper that renders client view data. By taking advantage of client view data, you can maintain the same sharp separation of concerns in an ASP.NET MVC Ajax application as you maintain in an ASP.NET MVC server-side application.&lt;/p&gt;

&lt;p&gt;I built this application by taking advantage of ASP.NET AJAX client templates used in combination with jQuery. This combination enables you to easily build pure Ajax applications.&lt;/p&gt;

&lt;p&gt;I want to emphasize that the approach to building an ASP.NET MVC Ajax application described in this blog entry is only one approach. In future tips, I plan on exploring several approaches to the same problem.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://weblogs.asp.net/blogs/stephenwalther/Downloads/Tip45/Tip45.zip"&gt;&lt;strong&gt;Download the Code&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://weblogs.asp.net/aggbug.aspx?PostID=6665799" width="1" height="1"&gt;</description><category domain="http://weblogs.asp.net/stephenwalther/archive/tags/JavaScript/default.aspx">JavaScript</category><category domain="http://weblogs.asp.net/stephenwalther/archive/tags/AJAX/default.aspx">AJAX</category><category domain="http://weblogs.asp.net/stephenwalther/archive/tags/ASP.NET+MVC/default.aspx">ASP.NET MVC</category><category domain="http://weblogs.asp.net/stephenwalther/archive/tags/ASP.NET/default.aspx">ASP.NET</category><category domain="http://weblogs.asp.net/stephenwalther/archive/tags/Tips/default.aspx">Tips</category></item><item><title>ASP.NET MVC Application Building: Family Video Website #5 – Multiple File Upload with Progress</title><link>http://weblogs.asp.net/stephenwalther/archive/2008/10/02/asp-net-mvc-application-building-family-video-website-5-multiple-file-upload-with-progress.aspx</link><pubDate>Fri, 03 Oct 2008 04:10:58 GMT</pubDate><guid isPermaLink="false">c06e2b9d-981a-45b4-a55f-ab0d8bbfdc1c:6655611</guid><dc:creator>swalther</dc:creator><slash:comments>6</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://weblogs.asp.net/stephenwalther/rsscomments.aspx?PostID=6655611</wfw:commentRss><comments>http://weblogs.asp.net/stephenwalther/archive/2008/10/02/asp-net-mvc-application-building-family-video-website-5-multiple-file-upload-with-progress.aspx#comments</comments><description>&lt;blockquote&gt;   &lt;p&gt;&lt;i&gt;In this series of tutorials, I build an entire ASP.NET MVC application from start to finish. In this entry, I add a Silverlight file upload control to make it easier to upload multiple media files.&lt;/i&gt;&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;This blog entry is part of a series of entries on using ASP.NET MVC to build an entire Family Video Website from scratch. Before reading this entry, you might want to read the previous four entries:&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;&lt;a href="http://weblogs.asp.net/stephenwalther/archive/2008/08/01/asp-net-mvc-application-building-1-family-video-website-upload-the-videos.aspx"&gt;Family Video Website #1 &amp;#8211; Upload the Videos&lt;/a&gt; &amp;#8211; In this entry, I create an MVC controller that accepts large file uploads.&lt;/p&gt;    &lt;p&gt;&lt;a href="http://weblogs.asp.net/stephenwalther/archive/2008/08/15/asp-net-mvc-application-building-1-family-video-website-add-the-database.aspx"&gt;Family Video Website #2 &amp;#8211;Add the Database&lt;/a&gt; &amp;#8211; In this entry, I added a database so that I could associate additional information, such as a title and description, with each video.&lt;/p&gt;    &lt;p&gt;&lt;a href="http://weblogs.asp.net/stephenwalther/archive/2008/09/03/asp-net-mvc-application-building-family-video-website-3-play-videos-with-silverlight.aspx"&gt;Family Video Website #3 &amp;#8211; Play Videos with Silverlight&lt;/a&gt; &amp;#8211; In this entry, I added the ASP.NET MediaPlayer Silverlight player to the application.&lt;/p&gt;    &lt;p&gt;&lt;a href="http://weblogs.asp.net/stephenwalther/archive/2008/09/18/asp-net-mvc-application-building-family-video-website-4-paging-silverlight-and-flip.aspx"&gt;Family Video Website #4 &amp;#8211; Paging, Silverlight, and Flip&lt;/a&gt; &amp;#8211; In this entry, I add paging to the application and demonstrate how you can encode and upload videos recorded with a Flip video camera.&lt;/p&gt; &lt;/blockquote&gt;  &lt;h4&gt;Accepting File Uploads&lt;/h4&gt;  &lt;p&gt;The standard HTML &amp;lt;input type=&amp;#8221;file&amp;#8221; /&amp;gt; element does not work well when you need to upload large files (see Figure 1). It doesn&amp;#8217;t display a progress bar. When you initiate the upload, your browser freezes. You have no idea whether something is happening or not.&lt;/p&gt;  &lt;p&gt;The standard HTML &amp;lt;input type=&amp;#8221;file&amp;#8221; /&amp;gt; element also does not provide you with a method of uploading multiple files at once. Typically, I dump a large number of pictures from my camera onto my hard drive and I want to upload several of these pictures to the Family website. The standard HTML file upload element forces you to upload the files, laboriously, one at a time.&lt;/p&gt;  &lt;p&gt;&lt;b&gt;Figure 1 &amp;#8211; The standard HTML file upload element&lt;/b&gt;&lt;/p&gt;  &lt;p&gt;&lt;a href="http://weblogs.asp.net/blogs/stephenwalther/WindowsLiveWriter/ASP.NETMVCApplicationBuildingFamilyVideo_129CE/clip_image001_2.jpg"&gt;&lt;img style="border-right: 0px; border-top: 0px; border-left: 0px; border-bottom: 0px" height="290" alt="clip_image001" src="http://weblogs.asp.net/blogs/stephenwalther/WindowsLiveWriter/ASP.NETMVCApplicationBuildingFamilyVideo_129CE/clip_image001_thumb.jpg" width="487" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;For these reasons, I decided to investigate alternatives to the standard HTML file upload element. I investigated both Ajax and Silverlight solutions.&lt;/p&gt;  &lt;h4&gt;Using Ajax for File Uploads&lt;/h4&gt;  &lt;p&gt;My first inclination was to implement an Ajax solution for accepting file uploads. Using Ajax, you can continuously poll the server from the browser to check on the progress of uploading a file. In that way, you can display a progress bar.&lt;/p&gt;  &lt;p&gt;I found good reviews of different Ajax file upload solutions for ASP.NET in Matt Berseth&amp;#8217;s blog and Jon Galloway&amp;#8217;s blog at the following addresses:&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;&lt;a href="http://mattberseth.com/blog/2008/07/aspnet_file_upload_with_realti_1.html"&gt;http://mattberseth.com/blog/2008/07/aspnet_file_upload_with_realti_1.html&lt;/a&gt;&lt;/p&gt;    &lt;p&gt;&lt;a href="http://weblogs.asp.net/jgalloway/archive/2008/01/08/large-file-uploads-in-asp-net.aspx"&gt;http://weblogs.asp.net/jgalloway/archive/2008/01/08/large-file-uploads-in-asp-net.aspx&lt;/a&gt;&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;After reading Matt Berseth&amp;#8217;s positive review of two open source Ajax file upload components, named NeatUpload and the ASP.Net File Upload/Download Module, I experimented with using these components in an ASP.NET MVC application. You can download these components from the following URLs:&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;&lt;a href="http://www.brettle.com/neatupload"&gt;http://www.brettle.com/neatupload&lt;/a&gt;&lt;/p&gt;    &lt;p&gt;&lt;a href="http://darrenjohnstone.net/2008/07/15/aspnet-file-upload-module-version-2-beta-1/"&gt;http://darrenjohnstone.net/2008/07/15/aspnet-file-upload-module-version-2-beta-1/&lt;/a&gt;&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;I made some progress in adapting these components to work in the context of an ASP.NET MVC application. However, after reading Jon Galloway&amp;#8217;s blog, I decided to investigate a Silverlight solution to the problem of accepting file uploads.&lt;/p&gt;  &lt;h4&gt;Using Silverlight for File Uploads&lt;/h4&gt;  &lt;p&gt;When I need an answer to a Silverlight question, I turn to either &lt;a href="http://timheuer.com/blog/Default.aspx"&gt;Tim Heuer&lt;/a&gt; or &lt;a href="http://silverlight.net/blogs/jesseliberty/"&gt;Jesse Liberty&lt;/a&gt; who are the Silverlight gurus on my team. Tim recommended that I look into the following two Silverlight controls:&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;&lt;a href="http://sandbox.inetsolution.com/fileuploadwebsite/FileUploaderTestPage.aspx"&gt;http://sandbox.inetsolution.com/fileuploadwebsite/FileUploaderTestPage.aspx&lt;/a&gt;&lt;/p&gt;    &lt;p&gt;&lt;a href="http://www.michielpost.nl/Silverlight/MultiFileUploader/"&gt;http://www.michielpost.nl/Silverlight/MultiFileUploader/&lt;/a&gt;&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;Both of these Silverlight controls enable you to upload multiple files. However, the first control is commercial and the second control is open source (the author says that the control will be released as open source with the Silverlight 2.0 release). Therefore, I decided to use the second control, the Multi File Uploader control, for the Family Videos Website application.&lt;/p&gt;  &lt;p&gt;The Multi File Uploader is made of two parts. The user interface for accepting a file upload is created with a Silverlight control. You display the file upload user interface by adding a Silverlight control named mpost.SilverlightMultiFileUpload.xap to your project.&lt;/p&gt;  &lt;p&gt;The Silverlight control is embedded in the Create.aspx view in Listing 1. The Create view enables you to upload new videos or pictures to the Family Video Website (see Figure 2). You can use the upload control to upload multiple pictures or videos at the same time.&lt;/p&gt;  &lt;p&gt;&lt;b&gt;Figure 2 &amp;#8211; Using the Multi File Uploader Silverlight control&lt;/b&gt;&lt;/p&gt;  &lt;p&gt;&lt;a href="http://weblogs.asp.net/blogs/stephenwalther/WindowsLiveWriter/ASP.NETMVCApplicationBuildingFamilyVideo_129CE/clip_image003_2.jpg"&gt;&lt;img style="border-right: 0px; border-top: 0px; border-left: 0px; border-bottom: 0px" height="495" alt="clip_image003" src="http://weblogs.asp.net/blogs/stephenwalther/WindowsLiveWriter/ASP.NETMVCApplicationBuildingFamilyVideo_129CE/clip_image003_thumb.jpg" width="611" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;&lt;b&gt;Listing 1 &amp;#8211; Views\Media\Create.aspx&lt;/b&gt;&lt;/p&gt;  &lt;pre class="xml" name="code"&gt;&amp;lt;%@ Page Language=&amp;quot;VB&amp;quot; AutoEventWireup=&amp;quot;false&amp;quot; CodeBehind=&amp;quot;Create.aspx.vb&amp;quot; Inherits=&amp;quot;FamilyVideos.Create&amp;quot; %&amp;gt;
&amp;lt;!DOCTYPE html PUBLIC &amp;quot;-//W3C//DTD XHTML 1.0 Transitional//EN&amp;quot; &amp;quot;http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd&amp;quot;&amp;gt;
&amp;lt;html xmlns=&amp;quot;http://www.w3.org/1999/xhtml&amp;quot; &amp;gt;
&amp;lt;head id=&amp;quot;Head1&amp;quot; runat=&amp;quot;server&amp;quot;&amp;gt;
    &amp;lt;title&amp;gt;Add New Media&amp;lt;/title&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
    &amp;lt;div&amp;gt;
    
    &amp;lt;h1&amp;gt;Upload Media&amp;lt;/h1&amp;gt;


&amp;lt;%--        
    MaxFileSizeKB:     Maximum file size in KBs. &amp;lt;br /&amp;gt;
    MaxUploads:     Maximum number of simultaneous uploads &amp;lt;br /&amp;gt;
    FileFilter:    File filter, for example ony jpeg use: FileFilter=Jpeg (*.jpg) |*.jpg &amp;lt;br /&amp;gt;
    CustomParam: Your custom parameter, anything here will be available in the WCF webservice &amp;lt;br /&amp;gt;
--%&amp;gt;
    &amp;lt;div id=&amp;quot;silverlightControlHost&amp;quot;&amp;gt;
        &amp;lt;object data=&amp;quot;data:application/x-silverlight,&amp;quot; type=&amp;quot;application/x-silverlight-2-b2&amp;quot; width=&amp;quot;500&amp;quot; height=&amp;quot;260&amp;quot;&amp;gt;
            &amp;lt;param name=&amp;quot;source&amp;quot; value=&amp;quot;/mpost.SilverlightMultiFileUpload.xap&amp;quot;/&amp;gt;
            &amp;lt;param name=&amp;quot;initParams&amp;quot; value=&amp;quot;MaxFileSizeKB=50000,MaxUploads=2,FileFilter=,CustomParam=yourparameters&amp;quot; /&amp;gt;
            &amp;lt;a href=&amp;quot;http://go.microsoft.com/fwlink/?LinkID=115261&amp;quot; style=&amp;quot;text-decoration: none;&amp;quot;&amp;gt;
                 &amp;lt;img src=&amp;quot;http://go.microsoft.com/fwlink/?LinkId=108181&amp;quot; alt=&amp;quot;Get Microsoft Silverlight&amp;quot; style=&amp;quot;border-style: none&amp;quot;/&amp;gt;
            &amp;lt;/a&amp;gt;
        &amp;lt;/object&amp;gt;
    &amp;lt;/div&amp;gt;


    &amp;lt;a href=&amp;quot;/Home/Index&amp;quot;&amp;gt;Finished Uploading&amp;lt;/a&amp;gt;

    
    &amp;lt;/div&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;&lt;/pre&gt;

&lt;p&gt;&amp;#160;&lt;/p&gt;

&lt;p&gt;The Silverlight control is embedded in the Create.aspx view with the help of an HTML &amp;lt;object&amp;gt; tag. The &amp;lt;object&amp;gt; tag includes a source parameter that points to the mpost.SilverlightMultiFileUpload.xap file in the project.&lt;/p&gt;

&lt;p&gt;Notice that the &amp;lt;object&amp;gt; tag also contains an initParams parameter. You can use this parameter to configure several options for the Multi File Uploader control such as the maximum allowable size of an uploaded file.&lt;/p&gt;

&lt;p&gt;The second part of the Multi File Uploader control is a WCF service. The Silverlight control calls the WCF service to pass the file being uploaded from the browser to the web server.&lt;/p&gt;

&lt;p&gt;In order to use the service, you must add a reference to your project to the mpost.FileUploadServiceLibrary.dll assembly. This assembly contains a base WCF service that you can use as the base class for your WCF service.&lt;/p&gt;

&lt;p&gt;The WCF service used by the Family Videos application is contained in Listing 2 and the code-behind class for this service is contained in Listing 3.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Listing 2 &amp;#8211; SilverlightUploadService.svc&lt;/b&gt;&lt;/p&gt;

&lt;pre class="xml" name="code"&gt;&amp;lt;%@ ServiceHost 
  Language=&amp;quot;VB&amp;quot; 
  Debug=&amp;quot;true&amp;quot; 
  Service=&amp;quot;FamilyVideos.SilverlightUploadService&amp;quot;
  CodeBehind=&amp;quot;SilverlightUploadService.svc.vb&amp;quot; %&amp;gt;&lt;/pre&gt;

&lt;p&gt;&lt;b&gt;&lt;/b&gt;&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Listing 3 &amp;#8211; SilverlightUploadService.svc.vb&lt;/b&gt;&lt;/p&gt;

&lt;pre class="vb" name="code"&gt;Imports System.IO
Imports System.Web.Hosting

Public Class SilverlightUploadService
    Inherits mpost.FileUploadServiceLibrary.UploadService


    Protected Overrides Sub FinishedFileUpload(ByVal fileName As String, ByVal parameters As String)
        ' Create new media object
        Dim newMedia As New Media()
        newMedia.Title = &amp;quot;New Media&amp;quot;
        newMedia.FileName = fileName
        newMedia.Description = &amp;quot;No Description&amp;quot;
        newMedia.EntryDate = DateTime.Now

        ' Add new media object to database
        Dim repository As New MediaRepository()
        repository.Insert(newMedia)

        ' Rename file
        Dim oldName = GetFullPath(fileName)
        Dim newName = GetFullPath(newMedia.MediaName)
        File.Move(oldName, newName)
    End Sub

    Protected Overrides Function GetUploadFolder() As String
        Return Globals.UploadPath
    End Function


    Private Function GetFullPath(ByVal fileName As String) As String
        Return HostingEnvironment.ApplicationPhysicalPath + &amp;quot;/&amp;quot; + GetUploadFolder() + &amp;quot;/&amp;quot; + fileName
    End Function



End Class&lt;/pre&gt;

&lt;p&gt;When you create a service to use with the Multi File Uploader control, you override two methods of the base UploadService class. You override both the GetUploadFolder() and FinishedFileUpload() methods.&lt;/p&gt;

&lt;p&gt;The GetUploadFolder() method returns the path to the folder where the file is uploaded. The FinishedFileUpload() method is called after the file is finished uploading. In Listing 3, the FinishedFileUpload() method is used to add a new Media item to the database that represents a newly uploaded file.&lt;/p&gt;

&lt;p&gt;Finally, in order to get the Multi File Uploader control to work, you must add configuration information to your web configuration (web.config) file for the WCF service. The Family Videos application uses the system.serviceModel configuration section in Listing 4.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Listing 4 &amp;#8211; web.config&lt;/b&gt;&lt;/p&gt;

&lt;pre class="xml" name="code"&gt;    &amp;lt;system.serviceModel&amp;gt;
        &amp;lt;serviceHostingEnvironment aspNetCompatibilityEnabled=&amp;quot;true&amp;quot; /&amp;gt;
        &amp;lt;behaviors&amp;gt;
            &amp;lt;serviceBehaviors&amp;gt;
                &amp;lt;behavior name=&amp;quot;FamilyVideos.SilverlightUploadServiceBehavior&amp;quot;&amp;gt;
                    &amp;lt;serviceMetadata httpGetEnabled=&amp;quot;true&amp;quot;/&amp;gt;
                    &amp;lt;serviceDebug includeExceptionDetailInFaults=&amp;quot;true&amp;quot;/&amp;gt;
                &amp;lt;/behavior&amp;gt;
            &amp;lt;/serviceBehaviors&amp;gt;
        &amp;lt;/behaviors&amp;gt;
        &amp;lt;services&amp;gt;
            &amp;lt;service behaviorConfiguration=&amp;quot;FamilyVideos.SilverlightUploadServiceBehavior&amp;quot; 
                name=&amp;quot;FamilyVideos.SilverlightUploadService&amp;quot;&amp;gt;
                &amp;lt;endpoint address=&amp;quot;&amp;quot; binding=&amp;quot;basicHttpBinding&amp;quot; contract=&amp;quot;mpost.FileUploadServiceLibrary.IUploadService&amp;quot;&amp;gt;
                    &amp;lt;identity&amp;gt;
                        &amp;lt;dns value=&amp;quot;localhost&amp;quot;/&amp;gt;
                    &amp;lt;/identity&amp;gt;
                &amp;lt;/endpoint&amp;gt;
                &amp;lt;endpoint address=&amp;quot;mex&amp;quot; binding=&amp;quot;mexHttpBinding&amp;quot; contract=&amp;quot;IMetadataExchange&amp;quot;/&amp;gt;
            &amp;lt;/service&amp;gt;
        &amp;lt;/services&amp;gt;
    &amp;lt;/system.serviceModel&amp;gt;&lt;/pre&gt;

&lt;p&gt;&amp;#160;&lt;/p&gt;

&lt;p&gt;The Multi File Uploader works quite nicely with Firefox (see Figure 3). This means that the Family Video Website works great with both Internet Explorer and Firefox.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Figure 3 &amp;#8211; Multi file Uploader used in Firefox&lt;/b&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://weblogs.asp.net/blogs/stephenwalther/WindowsLiveWriter/ASP.NETMVCApplicationBuildingFamilyVideo_129CE/clip_image005_2.jpg"&gt;&lt;img style="border-right: 0px; border-top: 0px; border-left: 0px; border-bottom: 0px" height="538" alt="clip_image005" src="http://weblogs.asp.net/blogs/stephenwalther/WindowsLiveWriter/ASP.NETMVCApplicationBuildingFamilyVideo_129CE/clip_image005_thumb.jpg" width="625" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;Summary&lt;/h4&gt;

&lt;p&gt;I&amp;#8217;m very happy with the new method of uploading files described in this blog entry. The Silverlight control solution solves both of the problems associated with the standard HTML file upload element discussed in the beginning of this blog entry. First, the Silverlight control provides you with visual feedback on the progress of a file being uploaded. Second, it enables you to efficiently upload multiple files at a time (for example, all of the pictures that you dump to your hard drive from your digital camera).&lt;/p&gt;

&lt;p&gt;There is still more work to do on the Family Videos Website. For example, we still need to tackle missing pieces of the application such as form validation and authentication.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://weblogs.asp.net/blogs/stephenwalther/Downloads/AppBuildingFamily5/FamilyVideos5.zip"&gt;&lt;strong&gt;Download the Code&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://weblogs.asp.net/aggbug.aspx?PostID=6655611" width="1" height="1"&gt;</description><category domain="http://weblogs.asp.net/stephenwalther/archive/tags/ASP.NET+MVC/default.aspx">ASP.NET MVC</category><category domain="http://weblogs.asp.net/stephenwalther/archive/tags/ASP.NET/default.aspx">ASP.NET</category><category domain="http://weblogs.asp.net/stephenwalther/archive/tags/Application+Building/default.aspx">Application Building</category></item><item><title>ASP.NET MVC Application Building: Forums #6 – Ajax</title><link>http://weblogs.asp.net/stephenwalther/archive/2008/09/22/asp-net-mvc-application-building-forums-6-ajax.aspx</link><pubDate>Mon, 22 Sep 2008 07:36:59 GMT</pubDate><guid isPermaLink="false">c06e2b9d-981a-45b4-a55f-ab0d8bbfdc1c:6637675</guid><dc:creator>swalther</dc:creator><slash:comments>10</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://weblogs.asp.net/stephenwalther/rsscomments.aspx?PostID=6637675</wfw:commentRss><comments>http://weblogs.asp.net/stephenwalther/archive/2008/09/22/asp-net-mvc-application-building-forums-6-ajax.aspx#comments</comments><description>&lt;blockquote&gt;   &lt;p&gt;&lt;i&gt;In this series of blog entries, I build an entire MVC Forums application from start to finish. In this entry, I create the user interface for the Forums application by taking advantage of ASP.NET AJAX client templates. I build the entire user interface for the Forums application with a single view and perform all updates to the view with Ajax calls.&lt;/i&gt;&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;A page request is just an excuse for a bad user experience. We are conditioned to expect our web pages to freeze whenever we interact with a website. For example, each and every time we submit a form, we expect the browser to freeze and the clock to tick until the browser updates the page again.&lt;/p&gt;  &lt;p&gt;There is no reason to inflict this pain on the users of your web applications. By taking advantage of Ajax, you can create web applications that respond instantly to user interactions. In this blog entry, I demonstrate how you can take advantage of Ajax when building ASP.NET MVC applications.&lt;/p&gt;  &lt;p&gt;In particular, I discuss how you can take advantage of a new feature of ASP.NET AJAX arriving with the next version of this technology. I discuss how you can take advantage of ASP.NET AJAX client templates in the context of an ASP.NET MVC application. A preview version of client templates is available now and you can start experimenting with this technology in the applications that you are building today.&lt;/p&gt;  &lt;p&gt;In this blog entry, I demonstrate how you can create a single page ASP.NET MVC application. Single page web applications are the holy grail of the Ajax developer. In a single page application, only one page must be requested by the browser. After the single page has loaded, all updates to the page are performed using client-side JavaScript. All new data displayed in the page is retrieved by performing Ajax calls against the server.&lt;/p&gt;  &lt;p&gt;An Ajax application takes a little more work to write (yes, you do have to write some JavaScript), but it provides your users with a much better user experience.&lt;/p&gt;  &lt;h4&gt;The Single Page&lt;/h4&gt;  &lt;p&gt;The single page (the single view) in our MVC Forums application is contained in Listing 1. Notice that the header of the view contains several JavaScript includes. The view takes advantage of the following JavaScript libraries:&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;&amp;#183; MicrosoftAjax.js &amp;#8211; The main Microsoft AJAX Library. Required for all things AJAX related in the Microsoft universe.&lt;/p&gt;    &lt;p&gt;&amp;#183; MicrosoftAjaxTemplates.js &amp;#8211; The preview version of the client template library included with the next version of Microsoft AJAX. &lt;/p&gt;    &lt;p&gt;&amp;#183; MicrosoftMvcAjax.js &amp;#8211; Extensions to the Microsoft AJAX Library for the MVC framework. This script is included with the default MVC Visual Studio project template.&lt;/p&gt;    &lt;p&gt;&amp;#183; MvcAjax.js &amp;#8211; A JavaScript Library that I wrote which contains useful helper methods for invoking controller actions.&lt;/p&gt;    &lt;p&gt;&amp;#183; MvcForums.js &amp;#8211; A JavaScript library that contains all of the user-interface logic used by the MVC Forums application.&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;&lt;b&gt;Listing 1 &amp;#8211; Views\Forums\Index.aspx&lt;/b&gt;&lt;/p&gt;  &lt;pre class="xml" name="code"&gt;&amp;lt;%@ Page Language=&amp;quot;C#&amp;quot; AutoEventWireup=&amp;quot;true&amp;quot; CodeBehind=&amp;quot;Index.aspx.cs&amp;quot; Inherits=&amp;quot;MvcForums.Views.Forum.Index&amp;quot; %&amp;gt;
&amp;lt;!DOCTYPE html PUBLIC &amp;quot;-//W3C//DTD XHTML 1.0 Transitional//EN&amp;quot; &amp;quot;http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd&amp;quot;&amp;gt;
&amp;lt;html xmlns=&amp;quot;http://www.w3.org/1999/xhtml&amp;quot; &amp;gt;
&amp;lt;head runat=&amp;quot;server&amp;quot;&amp;gt;
    &amp;lt;title&amp;gt;MVC Forums&amp;lt;/title&amp;gt;
    &amp;lt;link href=&amp;quot;../../Content/MvcForums.css&amp;quot; rel=&amp;quot;stylesheet&amp;quot; type=&amp;quot;text/css&amp;quot; /&amp;gt;
    &amp;lt;script src=&amp;quot;../../Content/MicrosoftAjax.debug.js&amp;quot; type=&amp;quot;text/javascript&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;
    &amp;lt;script src=&amp;quot;../../Content/MicrosoftAjaxTemplates.debug.js&amp;quot; type=&amp;quot;text/javascript&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;
    &amp;lt;script src=&amp;quot;../../Content/MicrosoftMvcAjax.debug.js&amp;quot; type=&amp;quot;text/javascript&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;
    &amp;lt;script src=&amp;quot;../../Content/MvcAjax.js&amp;quot; type=&amp;quot;text/javascript&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;
    &amp;lt;script src=&amp;quot;../../Content/MvcForums.js&amp;quot; type=&amp;quot;text/javascript&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
    &amp;lt;div id=&amp;quot;banner&amp;quot; class=&amp;quot;panel&amp;quot;&amp;gt;
        &amp;lt;h1&amp;gt;MVC Forums&amp;lt;/h1&amp;gt;
    &amp;lt;/div&amp;gt;

    &amp;lt;div id=&amp;quot;main&amp;quot;&amp;gt;
        &amp;lt;div id=&amp;quot;threadsAndMessageContainer&amp;quot;&amp;gt;
            &amp;lt;% Html.RenderPartial(&amp;quot;~/Partials/Threads.ascx&amp;quot;); %&amp;gt;
            &amp;lt;% Html.RenderPartial(&amp;quot;~/Partials/Message.ascx&amp;quot;); %&amp;gt;        
        &amp;lt;/div&amp;gt;
    &amp;lt;% Html.RenderPartial(&amp;quot;~/Partials/Post.ascx&amp;quot;); %&amp;gt;
    &amp;lt;% Html.RenderPartial(&amp;quot;~/Partials/Reply.ascx&amp;quot;); %&amp;gt;
    &amp;lt;% Html.RenderPartial(&amp;quot;~/Partials/LoginRegister.ascx&amp;quot;); %&amp;gt;
    &amp;lt;/div&amp;gt;
    
    &amp;lt;script type=&amp;quot;text/javascript&amp;quot;&amp;gt;
        Sys.Application.initialize();
    &amp;lt;/script&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;&lt;/pre&gt;

&lt;p&gt;Notice, furthermore, that the body of the view in Listing 1 consists of a series of calls to the Html.RenderPartial() method. The MVC Forums application contains several forms: a login form, a registration form, a post form, a reply form. Each of these forms is contained in a separate partial. Placing the different forms displayed by the single page Ajax application in different partials makes it easier to manage the application. For example, the partial used to post a new message, named Post.ascx, is contained in Listing 2.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Listing 2 &amp;#8211; Partials\Post.ascx&lt;/b&gt;&lt;/p&gt;

&lt;pre class="xml" name="code"&gt;&amp;lt;%@ Control Language=&amp;quot;C#&amp;quot; AutoEventWireup=&amp;quot;true&amp;quot; CodeBehind=&amp;quot;Post.ascx.cs&amp;quot; Inherits=&amp;quot;MvcForums.Partials.Post&amp;quot; %&amp;gt;
&amp;lt;form id=&amp;quot;post&amp;quot; action=&amp;quot;&amp;quot; class=&amp;quot;panel&amp;quot;&amp;gt;

&amp;lt;label for=&amp;quot;post.Subject&amp;quot;&amp;gt;Subject:&amp;lt;/label&amp;gt;
&amp;lt;br /&amp;gt;
&amp;lt;input name=&amp;quot;post.Subject&amp;quot; /&amp;gt;

&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;
&amp;lt;label for=&amp;quot;post.Body&amp;quot;&amp;gt;Body:&amp;lt;/label&amp;gt;
&amp;lt;br /&amp;gt;
&amp;lt;textarea name=&amp;quot;post.Body&amp;quot; cols=&amp;quot;60&amp;quot; rows=&amp;quot;8&amp;quot;&amp;gt;&amp;lt;/textarea&amp;gt;

&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;
&amp;lt;button id=&amp;quot;post.Post&amp;quot;&amp;gt;Post&amp;lt;/button&amp;gt;
&amp;lt;button id=&amp;quot;post.Cancel&amp;quot;&amp;gt;Cancel&amp;lt;/button&amp;gt;


&amp;lt;/form&amp;gt;&lt;/pre&gt;

&lt;p&gt;There is nothing special about the partial in Listing 2. It simply consists of a form for posting a new message to the MVC Forums.&lt;/p&gt;

&lt;h4&gt;Using Client Templates&lt;/h4&gt;

&lt;p&gt;By taking advantage of client templates, you can format any array of JavaScript objects. Client templates are included with the next version of the Microsoft AJAX Library. You can download a preview of client templates from the following address:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;a href="http://www.codeplex.com/aspnet/Release/ProjectReleases.aspx?ReleaseId=16766"&gt;http://www.codeplex.com/aspnet/Release/ProjectReleases.aspx?ReleaseId=16766&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The MVC Forums application uses client templates in two places. First, a client template is used to format the list of message threads (see Figure 1). The list of threads is retrieved from the server with an Ajax call. When the data is returned, the data is formatted with the help of a client template. &lt;/p&gt;

&lt;p&gt;&lt;b&gt;Figure 1 &amp;#8211; Message threads are rendered with a client template&lt;/b&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://weblogs.asp.net/blogs/stephenwalther/WindowsLiveWriter/ASP.NETMVCApplicationBuildingForums6Ajax_88D/clip_image002_2.jpg"&gt;&lt;img style="border-right: 0px; border-top: 0px; border-left: 0px; border-bottom: 0px" height="378" alt="clip_image002" src="http://weblogs.asp.net/blogs/stephenwalther/WindowsLiveWriter/ASP.NETMVCApplicationBuildingForums6Ajax_88D/clip_image002_thumb.jpg" width="628" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Second, a client template is used to format a particular thread (see Figure 2). A thread consists of a post and all of the replies to the post. The contents of a thread are also retrieved with an Ajax call from the server.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Figure 2 &amp;#8211; A particular thread is rendered with a client template&lt;/b&gt;&lt;/p&gt;

&lt;p&gt;&lt;b&gt;&lt;a href="http://weblogs.asp.net/blogs/stephenwalther/WindowsLiveWriter/ASP.NETMVCApplicationBuildingForums6Ajax_88D/clip_image004_2.jpg"&gt;&lt;img style="border-right: 0px; border-top: 0px; border-left: 0px; border-bottom: 0px" height="378" alt="clip_image004" src="http://weblogs.asp.net/blogs/stephenwalther/WindowsLiveWriter/ASP.NETMVCApplicationBuildingForums6Ajax_88D/clip_image004_thumb.jpg" width="628" border="0" /&gt;&lt;/a&gt;&lt;/b&gt;&lt;/p&gt;

&lt;p&gt;A client template is a fragment of XHTML. The client template used for formatting the list of threads is contained in Listing 3.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Listing 3 &amp;#8211; allThreads template&lt;/b&gt;&lt;/p&gt;

&lt;pre class="xml" name="code"&gt;&amp;lt;ul id=&amp;quot;allThreads&amp;quot; class=&amp;quot;sys-template&amp;quot;&amp;gt; 
&amp;lt;li&amp;gt;
        {{Author}} &amp;amp;mdash; 
        &amp;lt;a href=&amp;quot;&amp;quot; messageid=&amp;quot;{{Id}}&amp;quot;&amp;gt;{{Subject}}&amp;lt;/a&amp;gt; 
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;&lt;/pre&gt;

&lt;p&gt;There are two things that you should notice about the template in Listing 3. First, notice that the opening tag for the template includes a CSS class attribute that points to the sys-template class. This class is defined in the Forums.css file. The sys-template CSS class sets the display attribute to the value None in order to hide the contents of the template.&lt;/p&gt;

&lt;p&gt;Second, notice the expressions {{Author}} and {{Subject}}. These expressions are replaced automatically with the values of these properties. These expressions work very much like the &amp;lt;%# Eval() %&amp;gt; expressions in a server-side ASP.NET page.&lt;/p&gt;

&lt;p&gt;The template in Listing 3 is used by a client-side AJAX DataView control. This control is instantiated with the following line of code in the MvcForums.js library:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;_allThreadsView = $create(Sys.UI.DataView, {}, {}, {}, $get(&amp;quot;allThreads&amp;quot;));&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This line of code creates a new client-side DataView control named _allThreadsView that uses the template in Listing 3. After you create a DataView control, you can assign data to the control by using its set_data() property. You can assign any JavaScript array that you want to the DataView by calling set_data(). In the MvcForums application, the array of data is retrieved from the server through an Ajax call which I describe in the next section.&lt;/p&gt;

&lt;h4&gt;Actions are Invoked with Ajax&lt;/h4&gt;

&lt;p&gt;The MVC Forums application never performs a normal post to the server. All information is conveyed back and forth between the Index view and the server through Ajax calls. The Ajax calls invoke MVC controller actions asynchronously.&lt;/p&gt;

&lt;p&gt;In order to invoke MVC controller actions asynchronously, I had to write some helper methods. The MvcAjax.js file contains the following two helper methods:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&amp;#183; MvcAjax.MvcHelpers.invokeGet(url, succeededCallback, failedCallback)&lt;/p&gt;

  &lt;p&gt;&amp;#183; MvcAjax.MvcHelpers.invokePost(url, form, succeededCallback, failedCallback)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The first method enables you to invoke MVC controller actions by performing an HTTP GET. The second method enables you to invoke MVC controller actions by performing an HTTP POST.&lt;/p&gt;

&lt;p&gt;For example, the list of message threads is retrieved from the following action exposed by the Forum controller:&lt;/p&gt;

&lt;pre class="c#" name="code"&gt;public JsonResult Threads()
{
    return Json(_repository.SelectThreads());
}&lt;/pre&gt;

&lt;p&gt;This controller action returns a JSON result that represents all of the message threads. The list of threads is retrieved from the ForumRepository.&lt;/p&gt;

&lt;p&gt;The following JavaScript code is used to display the threads:&lt;/p&gt;

&lt;pre class="js" name="code"&gt;MvcAjax.MvcHelpers.invokeGet(&amp;quot;Forum/Threads&amp;quot;, updateAllThreads);

function updateAllThreads(data)
{
    _allThreadsView.set_data(data);
}&lt;/pre&gt;

&lt;p&gt;The invokeGet() method invokes the Forum controller Threads() action. When this action returns the JSON result, the JSON result is passed to the updateAllThreads() method which assigns the data to the _allThreadsView client-side DataView control. At this point, the data is formatted by the client template and the list of threads is displayed.&lt;/p&gt;

&lt;h4&gt;Network Traffic is Kept to a Minimum&lt;/h4&gt;

&lt;p&gt;By taking advantage of client templates, you can reduce your network traffic to the bare minimum necessary to transfer database records between the server and the client. Unlike an UpdatePanel, when you use a client template, you don&amp;#8217;t need to ship actual HTML markup between the server and client. The only thing that gets passed between server and client is pure data.&lt;/p&gt;

&lt;p&gt;Furthermore, because we are working in the context of an MVC application, there is no view data that needs to be passed between server and client. Because we are building a single page application, there is really no reason for view data to exist. View data enables you to preserve state across postbacks. However, we don&amp;#8217;t ever lose any state because we never perform a postback.&lt;/p&gt;

&lt;p&gt;Passing only essential information between the server and client improves the responsiveness of the Forums application. The entire page is not reloaded whenever you click a new message thread to view it. Instead, only the message thread itself is transferred across the wire.&lt;/p&gt;

&lt;h4&gt;Forms Appear Instantly&lt;/h4&gt;

&lt;p&gt;When you click the New Post link in the MVC Forums application, the form for posting a new message appears instantly (see Figure 3). Unlike a normal web application, the form is not retrieved from the server using a page request. Instead, the Post form is loaded when the first page is loaded. It is hidden (with display:none) until you need to post a new message.&lt;/p&gt;

&lt;p&gt;All of the forms in the MVC Forums application work in the same way. The Login, Register, Reply, and Post forms are all shipped to the browser with the first page request. You can post and reply to messages very quickly because these forms do not need to be repeatedly requested from the server.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Figure 3 &amp;#8211; Posting a new message&lt;/b&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://weblogs.asp.net/blogs/stephenwalther/WindowsLiveWriter/ASP.NETMVCApplicationBuildingForums6Ajax_88D/clip_image006_2.jpg"&gt;&lt;img style="border-right: 0px; border-top: 0px; border-left: 0px; border-bottom: 0px" height="438" alt="clip_image006" src="http://weblogs.asp.net/blogs/stephenwalther/WindowsLiveWriter/ASP.NETMVCApplicationBuildingForums6Ajax_88D/clip_image006_thumb.jpg" width="628" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;Summary&lt;/h4&gt;

&lt;p&gt;I created the user interface for the Forums application that I describe in this blog entry as an experiment to test the new client templates functionality coming with the next version of the Microsoft AJAX Library. I discovered that client templates work very well in the context of an ASP.NET MVC application.&lt;/p&gt;

&lt;p&gt;ASP.NET MVC enables you to easily build actions that return JSON results. Client-templates enable you to easily format JSON results. ASP.NET MVC and ASP.NET AJAX client templates are the perfect combination for Ajax fanatics like me.&lt;/p&gt;

&lt;p&gt;I recommend that you download and play with the MVC Forums application included at the end of this blog entry. I think that you will be surprised by the responsiveness of the application. &lt;/p&gt;

&lt;p&gt;&lt;a href="http://weblogs.asp.net/blogs/stephenwalther/Downloads/AppBuildingForums6/AppForums6.zip"&gt;&lt;strong&gt;Download the Code&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://weblogs.asp.net/aggbug.aspx?PostID=6637675" width="1" height="1"&gt;</description><category domain="http://weblogs.asp.net/stephenwalther/archive/tags/AJAX/default.aspx">AJAX</category><category domain="http://weblogs.asp.net/stephenwalther/archive/tags/ASP.NET+MVC/default.aspx">ASP.NET MVC</category><category domain="http://weblogs.asp.net/stephenwalther/archive/tags/ASP.NET/default.aspx">ASP.NET</category><category domain="http://weblogs.asp.net/stephenwalther/archive/tags/Application+Building/default.aspx">Application Building</category></item><item><title>ASP.NET MVC Application Building: Family Video Website #4 – Paging, Silverlight and Flip</title><link>http://weblogs.asp.net/stephenwalther/archive/2008/09/18/asp-net-mvc-application-building-family-video-website-4-paging-silverlight-and-flip.aspx</link><pubDate>Thu, 18 Sep 2008 19:30:37 GMT</pubDate><guid isPermaLink="false">c06e2b9d-981a-45b4-a55f-ab0d8bbfdc1c:6632941</guid><dc:creator>swalther</dc:creator><slash:comments>7</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://weblogs.asp.net/stephenwalther/rsscomments.aspx?PostID=6632941</wfw:commentRss><comments>http://weblogs.asp.net/stephenwalther/archive/2008/09/18/asp-net-mvc-application-building-family-video-website-4-paging-silverlight-and-flip.aspx#comments</comments><description>&lt;blockquote&gt;   &lt;p&gt;&lt;i&gt;(ASP.NET MVC + Silverlight + Flip Video) is a wonderful combination! In this blog entry, I continue building the Family Video Website. I integrate a new Silverlight video player, demonstrate how to upload Flip videos, and add paging.&lt;/i&gt;&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;This blog entry is part of a series of entries on using ASP.NET MVC to build an entire Family Video Website from scratch. Before reading this entry, you might want to read the previous three entries:&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;&lt;a href="http://weblogs.asp.net/stephenwalther/archive/2008/08/01/asp-net-mvc-application-building-1-family-video-website-upload-the-videos.aspx"&gt;Family Video Website #1 &amp;#8211; Upload the Videos&lt;/a&gt; &amp;#8211; In this entry, I create an MVC controller that accepts large file uploads.&lt;/p&gt;    &lt;p&gt;&lt;a href="http://weblogs.asp.net/stephenwalther/archive/2008/08/15/asp-net-mvc-application-building-1-family-video-website-add-the-database.aspx"&gt;Family Video Website #2 &amp;#8211;Add the Database&lt;/a&gt; &amp;#8211; In this entry, I added a database so that I could associate additional information, such as a title and description, with each video.&lt;/p&gt;    &lt;p&gt;&lt;a href="http://weblogs.asp.net/stephenwalther/archive/2008/09/03/asp-net-mvc-application-building-family-video-website-3-play-videos-with-silverlight.aspx"&gt;Family Video Website #3 &amp;#8211; Play Videos with Silverlight&lt;/a&gt; &amp;#8211; In this entry, I added the ASP.NET MediaPlayer Silverlight player to the application.&lt;/p&gt; &lt;/blockquote&gt;  &lt;h4&gt;Using the Silverlight 2 Video Player&lt;/h4&gt;  &lt;p&gt;In the previous entry in this series, I integrated the ASP.NET Media Player into the Family Video Website. Because you cannot use standard ASP.NET controls in an MVC view, I integrated the Media Player by adding the JavaScript files used by the control and not the control itself.&lt;/p&gt;  &lt;p&gt;I got everything to work &amp;#8211; but the process seemed a little messy. Fortunately, Tim Heuer stopped by my office at Microsoft recently and explained that there was a better way to show Silverlight videos. &lt;a href="http://timheuer.com/blog/Default.aspx"&gt;Tim Heuer&lt;/a&gt; and &lt;a href="http://silverlight.net/blogs/jesseliberty/"&gt;Jesse Liberty&lt;/a&gt; are the two Silverlight experts on my team. I&amp;#8217;m really fortunate to be able to get feedback from some really smart people.&lt;/p&gt;  &lt;p&gt;Tim explained that I could use the Silverlight 2 Video Player from the CodePlex website to play videos in my application. The Silverlight 2 Video Player can be downloaded from the following URL:&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;&lt;a href="http://www.codeplex.com/sl2videoplayer"&gt;http://www.codeplex.com/sl2videoplayer&lt;/a&gt;&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;There are two versions of this video player &amp;#8211; a full version and a light version. Both versions work with Internet Explorer and Firefox. The light version is only around 16K in size. That means that Grandma can watch videos without waiting a long time for the player to download.&lt;/p&gt;  &lt;p&gt;Integrating the Silverlight 2 Player was very easy. You just need to add the right &amp;lt;object&amp;gt; tag to a view. The new version of the RenderVideo partial is contained in Listing 1.&lt;/p&gt;  &lt;p&gt;&lt;b&gt;Listing 1 &amp;#8211; Partials\RenderVideo.ascx&lt;/b&gt;&lt;/p&gt;  &lt;pre class="xml" name="code"&gt;&amp;lt;%@ Control Language=&amp;quot;VB&amp;quot; AutoEventWireup=&amp;quot;false&amp;quot; CodeBehind=&amp;quot;RenderVideo.ascx.vb&amp;quot; Inherits=&amp;quot;FamilyVideos.RenderVideo&amp;quot; %&amp;gt;

&amp;lt;div id=&amp;quot;silverlightControlHost&amp;quot;&amp;gt;
    &amp;lt;object data=&amp;quot;data:application/x-silverlight,&amp;quot; type=&amp;quot;application/x-silverlight-2-b2&amp;quot; width=&amp;quot;640&amp;quot; height=&amp;quot;480&amp;quot;&amp;gt;
        &amp;lt;param name=&amp;quot;source&amp;quot; value=&amp;quot;/VideoPlayerM.xap&amp;quot;/&amp;gt;
        &amp;lt;param name=&amp;quot;background&amp;quot; value=&amp;quot;white&amp;quot; /&amp;gt;
        &amp;lt;param name=&amp;quot;initParams&amp;quot; value=&amp;quot;m=&amp;lt;%= ResolveUrl(ViewData.Model.MediaPath) %&amp;gt;&amp;quot; /&amp;gt;
        &amp;lt;a href=&amp;quot;http://go.microsoft.com/fwlink/?LinkID=115261&amp;quot; style=&amp;quot;text-decoration: none;&amp;quot;&amp;gt;
             &amp;lt;img src=&amp;quot;http://go.microsoft.com/fwlink/?LinkId=108181&amp;quot; alt=&amp;quot;Get Microsoft Silverlight&amp;quot; style=&amp;quot;border-style: none&amp;quot;/&amp;gt;
        &amp;lt;/a&amp;gt;
    &amp;lt;/object&amp;gt;
&amp;lt;/div&amp;gt;&lt;/pre&gt;

&lt;p&gt;There are two parameters that you need to set for the &amp;lt;object&amp;gt; tag. First, you need to make sure that the source parameter points to the VideoPlayerM.xap file that you download from the CodePlex website. I located the VideoPlayerM.xap file in the root of my application.&lt;/p&gt;

&lt;p&gt;Second, the initParams parameter contains an m parameter that represents the path to the video file that you want to play. In Listing 1, the path to the video file is retrieved from View Data. Notice that I use the ResolveUrl() method to convert an application relative path (~/MediaFiles/MyVideo.wmv) into a proper path (/MyVideoApp/MediaFiles/MyVideo.wmv). &lt;/p&gt;

&lt;p&gt;There is a complete list of initParams located at the following address:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;a href="http://www.codeplex.com/sl2videoplayer/Wiki/View.aspx?title=initparams&amp;amp;referringTitle=installation%20instructions"&gt;http://www.codeplex.com/sl2videoplayer/Wiki/View.aspx?title=initparams&amp;amp;referringTitle=installation%20instructions&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For example, you can use additional parameters to control the video thumbnail image and to auto-start the video.&lt;/p&gt;

&lt;p&gt;After I integrated the new Silverlight 2 Video Player, the Index view renders the page in Figure 1 (Yes, that is my dog). It is a little hard to notice on the player, but the player does include a full-screen button located at its bottom right.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Figure 1 &amp;#8211; The Silverlight 2 Video Player&lt;/b&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://weblogs.asp.net/blogs/stephenwalther/WindowsLiveWriter/ASP.NETMVCApplicationBuildingFamilyVideo_AFE1/clip_image002_2.jpg"&gt;&lt;img style="border-right: 0px; border-top: 0px; border-left: 0px; border-bottom: 0px" height="616" alt="clip_image002" src="http://weblogs.asp.net/blogs/stephenwalther/WindowsLiveWriter/ASP.NETMVCApplicationBuildingFamilyVideo_AFE1/clip_image002_thumb.jpg" width="628" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;Uploading Flip Videos&lt;/h4&gt;

&lt;p&gt;I bought a Flip video camera to make it easier to record family videos. I love this device (see Figure 2). It holds up to 60 minutes of video on 2 Gigabytes of memory. It is the size of a pack of cigarettes. It has a built-in USB &amp;#8220;switchblade&amp;#8221; that enables you to transfer videos from the camera to a computer easily.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Figure 2 &amp;#8211; Flip Video Camera&lt;/b&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://weblogs.asp.net/blogs/stephenwalther/WindowsLiveWriter/ASP.NETMVCApplicationBuildingFamilyVideo_AFE1/clip_image004_2.jpg"&gt;&lt;img style="border-right: 0px; border-top: 0px; border-left: 0px; border-bottom: 0px" height="139" alt="clip_image004" src="http://weblogs.asp.net/blogs/stephenwalther/WindowsLiveWriter/ASP.NETMVCApplicationBuildingFamilyVideo_AFE1/clip_image004_thumb.jpg" width="79" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I first learned about the Flip video camera by reading a review by another member of my team, Scott Hanselman. You can read his full review of the camera here:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;a href="http://www.hanselman.com/blog/PureDigitalFlipVideoUltraReview.aspx"&gt;http://www.hanselman.com/blog/PureDigitalFlipVideoUltraReview.aspx&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;(I got the Flip Mini which is a much smaller and therefore better version of the Flip camera than Hanselman bought).&lt;/p&gt;

&lt;p&gt;After you record a Flip video, you can plug the Flip into your computer and transfer the AVI file. Next, launch Microsoft Expression Encoder to compress the video (see Figure 3). Click the &lt;b&gt;Import&lt;/b&gt; button to the import the AVI file, select a &lt;b&gt;Video Profile&lt;/b&gt; setting, and click the &lt;b&gt;Encode&lt;/b&gt; button. It is amazing how much you can compress a video with Expression Encoder.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Figure 3 &amp;#8211; Compressing with Expression Encoder&lt;/b&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://weblogs.asp.net/blogs/stephenwalther/WindowsLiveWriter/ASP.NETMVCApplicationBuildingFamilyVideo_AFE1/clip_image006_2.jpg"&gt;&lt;img style="border-right: 0px; border-top: 0px; border-left: 0px; border-bottom: 0px" height="433" alt="clip_image006" src="http://weblogs.asp.net/blogs/stephenwalther/WindowsLiveWriter/ASP.NETMVCApplicationBuildingFamilyVideo_AFE1/clip_image006_thumb.jpg" width="628" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The video that is included with the application download at the end of this blog entry was compressed using the Web Server 512K DSL Setting. The 8 second Flip AVI video compressed down to 520KB. The video looks fine even when you play it in full-screen mode with the Silverlight 2 Video Player. &lt;/p&gt;

&lt;p&gt;Expression Encoder produces a WMV file. By default, Expression Encoder outputs to the following folder:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;\Documents\Expression\Expression Encoder\Output\&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You can upload the WMV file to the Family Video Website MVC application in order to view it.&lt;/p&gt;

&lt;h4&gt;Adding Paging&lt;/h4&gt;

&lt;p&gt;I made one additional modification to the Family Video Website. I added support for paging. Only one video is displayed on the home page at a time. To see another video, you must either click a particular page number or click the Next page link.&lt;/p&gt;

&lt;p&gt;In order to implement paging, I took advantage of the MvcPaging project that I describe in the following tip:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;a href="http://weblogs.asp.net/stephenwalther/archive/2008/09/18/asp-net-mvc-tip-44-create-a-pager-html-helper.aspx"&gt;ASP.NET MVC Tip #44 &amp;#8211; Create a Pager HTML Helper&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Here&amp;#8217;s the steps that I followed to implement paging. First, I modified the Index view so that it uses the Html.Pager() Helper. The modified Index view is contained in Listing 2.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Listing 2 &amp;#8211; Views\Home\Index.aspx&lt;/b&gt;&lt;/p&gt;

&lt;pre class="xml" name="code"&gt;&amp;lt;%@ Page Language=&amp;quot;VB&amp;quot; AutoEventWireup=&amp;quot;false&amp;quot; CodeBehind=&amp;quot;Index.aspx.vb&amp;quot; Inherits=&amp;quot;FamilyVideos.Index&amp;quot; %&amp;gt;
&amp;lt;%@ Import Namespace=&amp;quot;MvcPaging&amp;quot; %&amp;gt;
&amp;lt;!DOCTYPE html PUBLIC &amp;quot;-//W3C//DTD XHTML 1.0 Transitional//EN&amp;quot; &amp;quot;http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd&amp;quot;&amp;gt;
&amp;lt;html xmlns=&amp;quot;http://www.w3.org/1999/xhtml&amp;quot; &amp;gt;
&amp;lt;head id=&amp;quot;Head1&amp;quot; runat=&amp;quot;server&amp;quot;&amp;gt;
    &amp;lt;title&amp;gt;Family Videos Home&amp;lt;/title&amp;gt;
    &amp;lt;style type=&amp;quot;text/css&amp;quot;&amp;gt;
    
    html
    {
        font-family: Verdana;
    }
    
    h1
    {
        font: bold 14px Verdana;
    }
    
    div.media
    {
        border-bottom: 1px solid black;
        padding: 10px;
    }

    .pageNumbers
    {
        display: inline;
        margin: 0px;
        padding: 0px;
    }
    
    .pageNumbers li
    {
        display: inline;
        padding: 4px;
        border: solid 2px blue;
        margin: 3px;
    }
    
    .pageNumbers a
    {
        text-decoration: none;
        color: blue;
    }
    
    .selectedPageNumber
    {
        background-color: blue;
        color: white;
        font-weight: bold;
    }
    
    &amp;lt;/style&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
    &amp;lt;div&amp;gt;

    &amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;
    &amp;lt;%=Html.Pager(ViewData.Model)%&amp;gt;   
    &amp;lt;br /&amp;gt;

    &amp;lt;% For Each m In ViewData.Model%&amp;gt;

    &amp;lt;div class=&amp;quot;media&amp;quot;&amp;gt;
        &amp;lt;h1&amp;gt;&amp;lt;%=Html.Encode(m.Title)%&amp;gt;&amp;lt;/h1&amp;gt;
        &amp;lt;p&amp;gt;
        &amp;lt;%=Html.Encode(m.Description)%&amp;gt;
        &amp;lt;/p&amp;gt;
        
        &amp;lt;% Html.RenderPartial(FamilyVideos.RendererMap.GetRenderer(m.FileName), m)%&amp;gt;        
    &amp;lt;/div&amp;gt;
    
    &amp;lt;% Next%&amp;gt;
  
    &amp;lt;br /&amp;gt;
    &amp;lt;%=Html.Pager(ViewData.Model)%&amp;gt;   

    &amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;
    &amp;lt;%=Html.ActionLink(&amp;quot;Add New Media&amp;quot;, &amp;quot;Create&amp;quot;, &amp;quot;Media&amp;quot;)%&amp;gt;
   
    &amp;lt;/div&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;&lt;/pre&gt;

&lt;p&gt;The Html.Pager() method is called twice. The paging user interface is displayed at both the top and bottom of the rendered page (see Figure 4).&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Figure 4 &amp;#8211; Index view&lt;/b&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://weblogs.asp.net/blogs/stephenwalther/WindowsLiveWriter/ASP.NETMVCApplicationBuildingFamilyVideo_AFE1/clip_image008_2.jpg"&gt;&lt;img style="border-right: 0px; border-top: 0px; border-left: 0px; border-bottom: 0px" height="661" alt="clip_image008" src="http://weblogs.asp.net/blogs/stephenwalther/WindowsLiveWriter/ASP.NETMVCApplicationBuildingFamilyVideo_AFE1/clip_image008_thumb.jpg" width="628" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In Figure 1, you see a link to the first page, second page, and Next page. These links appear at both the top and bottom of the page. The appearance of the paging user interface is styled using the CSS classes that appear in the Index view header.&lt;/p&gt;

&lt;p&gt;The Index view is a typed view. The ViewData.Model property is cast to an instance of the IPageOfList class with the code-behind class in Listing 3. You can open the code-behind file for a view by right-clicking the view in the Solution Explorer window and selecting the menu option &lt;b&gt;View Code&lt;/b&gt;.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Listing 3 &amp;#8211; Views\Home\Index.aspx.vb&lt;/b&gt;&lt;/p&gt;

&lt;pre class="vb" name="code"&gt;Imports MvcPaging

Partial Public Class Index
    Inherits System.Web.Mvc.ViewPage(Of PageOfList(Of Media))

End Class&lt;/pre&gt;

&lt;p&gt;In order to return database records in pages, I also needed to modify the Home controller. The updated Home controller is contained in Listing 4.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Listing 4 &amp;#8211; Controllers\HomeController.vb&lt;/b&gt;&lt;/p&gt;

&lt;pre class="vb" name="code"&gt;&amp;lt;HandleError()&amp;gt; _
Public Class HomeController
    Inherits ControllerBase

    Function Index(ByVal id As Integer?)
        Dim pageIndex = 0
        If Not IsNothing(id) Then
            pageIndex = id
        End If

        Dim media = Repository.SelectRange(pageIndex, 1)
        Return View(&amp;quot;Index&amp;quot;, media)
    End Function

End Class&lt;/pre&gt;

&lt;p&gt;The modified Index() action now accepts an Id parameter. The Id parameter is used to represent the current page of records. The Id parameter can be an integer or it can be null (the ? mark indicates that the Id parameter accepts null values).&lt;/p&gt;

&lt;p&gt;A range of database records is retrieved by calling the Repository.SelectRange() method. The modified MediaRepository class is contained in Listing 5.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Listing 5 &amp;#8211; Models\MediaRepository.vb&lt;/b&gt;&lt;/p&gt;

&lt;pre class="vb" name="code"&gt;Imports System.Data.Linq
Imports System.Web.Configuration
Imports System.Data.Linq.Mapping
Imports System.Web.Hosting
Imports MvcPaging

Public Class MediaRepository

    Private _dataContext As DataContext
    Private _media As Table(Of Media)

    Sub New()
        ' Get connection string from web config
        Dim conSettings As ConnectionStringSettings
        conSettings = WebConfigurationManager.ConnectionStrings(&amp;quot;con1&amp;quot;)
        If IsNothing(conSettings) Then
            Throw New ConfigurationErrorsException(&amp;quot;Missing con1 database connection string.&amp;quot;)
        End If

        ' Get XML mapping file
        Dim map = XmlMappingSource.FromUrl(HostingEnvironment.MapPath(&amp;quot;~/Models/Media.xml&amp;quot;))

        ' Create DataContext
        _dataContext = New DataContext(conSettings.ConnectionString, map)

        ' Create media table
        _media = _dataContext.GetTable(Of Media)()
    End Sub

    Sub Insert(ByVal mediaToAdd As Media)
        _media.InsertOnSubmit(mediaToAdd)
        _dataContext.SubmitChanges()
    End Sub

    Function SelectAll() As List(Of Media)
        Return _media.ToList()
    End Function

    Function SelectRange(ByVal pageIndex As Integer, ByVal pageSize As Integer) As PageOfList(Of Media)
        Return _media.ToPageOfList(pageIndex, pageSize)
    End Function

End Class&lt;/pre&gt;

&lt;p&gt;The SelectRange() method takes advantage of the ToPageOfList() extension method defined in the MvcPaging project. This extension method generates an instance of a PageOfList class from a LINQ to SQL query.&lt;/p&gt;

&lt;h4&gt;Summary&lt;/h4&gt;

&lt;p&gt;In this blog entry, I modified the video player used by the Family Videos Website. Integrating the new Silverlight 2 Video Player that Tim Heuer recommended was much easier than integrating the ASP.NET Media Player. I also added a paging user interface to the application by taking advantage of the Html.Pager() helper.&lt;/p&gt;

&lt;p&gt;There is still more work to do! For example, I still need to implement security (authentication and authorization) for the website. Also, it would be nice if I could find some way to display a progress bar when uploading large video files.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://weblogs.asp.net/blogs/stephenwalther/Downloads/AppBuildingFamily4/FamilyVideos4.zip"&gt;&lt;strong&gt;Download the Code&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://weblogs.asp.net/aggbug.aspx?PostID=6632941" width="1" height="1"&gt;</description><category domain="http://weblogs.asp.net/stephenwalther/archive/tags/ASP.NET+MVC/default.aspx">ASP.NET MVC</category><category domain="http://weblogs.asp.net/stephenwalther/archive/tags/ASP.NET/default.aspx">ASP.NET</category><category domain="http://weblogs.asp.net/stephenwalther/archive/tags/Application+Building/default.aspx">Application Building</category></item><item><title>ASP.NET MVC Tip #44 – Create a Pager HTML Helper</title><link>http://weblogs.asp.net/stephenwalther/archive/2008/09/18/asp-net-mvc-tip-44-create-a-pager-html-helper.aspx</link><pubDate>Thu, 18 Sep 2008 07:25:58 GMT</pubDate><guid isPermaLink="false">c06e2b9d-981a-45b4-a55f-ab0d8bbfdc1c:6631471</guid><dc:creator>swalther</dc:creator><slash:comments>12</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://weblogs.asp.net/stephenwalther/rsscomments.aspx?PostID=6631471</wfw:commentRss><comments>http://weblogs.asp.net/stephenwalther/archive/2008/09/18/asp-net-mvc-tip-44-create-a-pager-html-helper.aspx#comments</comments><description>&lt;blockquote&gt;   &lt;p&gt;&lt;i&gt;In this tip, I demonstrate how you can create a custom HTML Helper that you can use to generate a user interface for paging through a set of database records. I build on the work of Troy Goode and Martijn Boland. I also demonstrate how you can build unit tests for HTML Helpers by faking the HtmlHelper class.&lt;/i&gt;&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;This week, I discovered that I desperately needed a way of paging through the database records in two of the sample MVC applications that I am building. I needed a clean, flexible, and testable way to generate the user interface for paging through a set of database records.&lt;/p&gt;  &lt;p&gt;There are several really good solutions to this problem that already exist. I recommend that you take a look at Troy Goode&amp;#8217;s discussion of his PagedList class at the following URL:&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;&lt;a href="http://www.squaredroot.com/post/2008/07/08/PagedList-Strikes-Back.aspx" target="_blank"&gt;http://www.squaredroot.com/post/2008/07/08/PagedList-Strikes-Back.aspx&lt;/a&gt;&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;Also, see Martijn Boland&amp;#8217;s Pager HTML Helper at the following URL:&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;&lt;a href="http://blogs.taiga.nl/martijn/Default.aspx" target="_blank"&gt;http://blogs.taiga.nl/martijn/Default.aspx&lt;/a&gt;&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;I used both of these solutions as a starting point. However, there were some additional features that I wanted that forced me to extend these existing solutions:&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;&amp;#183; I wanted complete flexibility in the way in which I formatted my pager.&lt;/p&gt;    &lt;p&gt;&amp;#183; I wanted a way to easily build unit tests for my pager.&lt;/p&gt; &lt;/blockquote&gt;  &lt;h4&gt;Walkthrough of the Pager HTML Helper&lt;/h4&gt;  &lt;p&gt;Let me provide a quick walkthrough of my Pager Helper solution. Let&amp;#8217;s start by creating a controller that returns a set of database records. The Home controller in Listing 1 uses a MovieRepository class to return a particular range of Movie database records.&lt;/p&gt;  &lt;p&gt;&lt;b&gt;Listing 1 &amp;#8211; Controllers\HomeController.cs&lt;/b&gt;&lt;/p&gt;  &lt;pre class="c#" name="code"&gt;using System.Web.Mvc;
using Tip44.Models;

namespace Tip44.Controllers
{
    [HandleError]
    public class HomeController : Controller
    {
        private MovieRepository _repository;

        public HomeController()
        {
            _repository = new MovieRepository();
        }

        public ActionResult Index(int? id)
        {
            var pageIndex = id ?? 0;

            var range = _repository.SelectRange(pageIndex, 2); 
            return View(range);
        }
    }
}&lt;/pre&gt;

&lt;p&gt;The Home controller in Listing 1 has one action named Index(). The Index() action accepts an Id parameter that represents a page index. The Index() action returns a set of database records that correspond to the page index.&lt;/p&gt;

&lt;p&gt;The Home controller takes advantage of the MovieRepository in Listing 2 to retrieve the database records.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Listing 2 &amp;#8211; Models\MovieRepository.cs&lt;/b&gt;&lt;/p&gt;

&lt;pre class="c#" name="code"&gt;using MvcPaging;

namespace Tip44.Models
{
    public class MovieRepository
    {
        private MovieDataContext _dataContext;

        public MovieRepository()
        {
            _dataContext = new MovieDataContext();
        }

        public IPageOfList&amp;lt;Movie&amp;gt; SelectRange(int pageIndex, int pageSize)
        {
            return _dataContext.Movies.ToPageOfList(pageIndex, pageSize);
        }    
    }

}&lt;/pre&gt;

&lt;p&gt;A range of movie records is returned by the SelectRange() method. The ToPageOfList() extension method is called to generate an instance of the PageOfList class. The PageOfList class represents one page of database records. The code for this class is contained in Listing 3.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Listing 3 &amp;#8211; Models\PageOfList.cs&lt;/b&gt;&lt;/p&gt;

&lt;pre class="c#" name="code"&gt;using System;
using System.Collections.Generic;

namespace MvcPaging
{
    public class PageOfList&amp;lt;T&amp;gt; : List&amp;lt;T&amp;gt;, IPageOfList&amp;lt;T&amp;gt;
    {
        public PageOfList(IEnumerable&amp;lt;T&amp;gt; items, int pageIndex, int pageSize, int totalItemCount)
        {
            this.AddRange(items);
            this.PageIndex = pageIndex;
            this.PageSize = pageSize;
            this.TotalItemCount = totalItemCount;
            this.TotalPageCount = (int)Math.Ceiling(totalItemCount / (double)pageSize);
        }

        public int PageIndex { get; set; }
        public int PageSize { get; set; }
        public int TotalItemCount { get; set; }
        public int TotalPageCount { get; private set; }

    }
}&lt;/pre&gt;

&lt;p&gt;Finally, the movie database records are displayed in the view in Listing 4. The view in Listing 4 is a strongly typed view in which the ViewData.Model property is typed to an instance of the IPageOfList interface.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Listing 4 &amp;#8211; Views\Home\Index.aspx&lt;/b&gt;&lt;/p&gt;

&lt;pre class="xml" name="code"&gt;&amp;lt;%@ Page Language=&amp;quot;C#&amp;quot; AutoEventWireup=&amp;quot;true&amp;quot; CodeBehind=&amp;quot;Index.aspx.cs&amp;quot; Inherits=&amp;quot;Tip44.Views.Home.Index&amp;quot; %&amp;gt;
&amp;lt;%@ Import Namespace=&amp;quot;MvcPaging&amp;quot; %&amp;gt;
&amp;lt;!DOCTYPE html PUBLIC &amp;quot;-//W3C//DTD XHTML 1.0 Transitional//EN&amp;quot; &amp;quot;http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd&amp;quot;&amp;gt;
&amp;lt;html xmlns=&amp;quot;http://www.w3.org/1999/xhtml&amp;quot; &amp;gt;
&amp;lt;head runat=&amp;quot;server&amp;quot;&amp;gt;
    &amp;lt;title&amp;gt;Index&amp;lt;/title&amp;gt;
    &amp;lt;style type=&amp;quot;text/css&amp;quot;&amp;gt;
    
    .pageNumbers
    {
        display:inline;
        margin:0px;
    }
    
    .pageNumbers li
    {
        display: inline;
        padding:3px;
    }
    
    .selectedPageNumber
    {
        font-weight: bold;
        text-decoration: none;
    }
        
    &amp;lt;/style&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
    &amp;lt;div&amp;gt;
    
    &amp;lt;ul&amp;gt;
    &amp;lt;% foreach (var m in ViewData.Model)
       { %&amp;gt;
    
        &amp;lt;li&amp;gt;&amp;lt;%= m.Title %&amp;gt;&amp;lt;/li&amp;gt;
    
    &amp;lt;% } %&amp;gt;
    &amp;lt;/ul&amp;gt;
            
    &amp;lt;%= Html.Pager(ViewData.Model)%&amp;gt;

    
 &amp;lt;/div&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;&lt;/pre&gt;

&lt;p&gt;When the view in Listing 4 is displayed in a web browser, you see the paging user interface in Figure 1.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Figure 1 &amp;#8211; Paging through movie records&lt;/b&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://weblogs.asp.net/blogs/stephenwalther/WindowsLiveWriter/ASP.NETMVCTip44CreateaPagerHTMLHelper_60D/clip_image002_2.jpg"&gt;&lt;img style="border-top-width: 0px; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="213" alt="clip_image002" src="http://weblogs.asp.net/blogs/stephenwalther/WindowsLiveWriter/ASP.NETMVCTip44CreateaPagerHTMLHelper_60D/clip_image002_thumb.jpg" width="244" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Notice that the view in Listing 4 includes a Cascading Style Sheet. By default, the Html.Pager() renders the list of page numbers in an unordered bulleted list (an XHTML &amp;lt;ul&amp;gt; tag). The CSS classes are used to format this list so that the list of page numbers appears in a single horizontal line.&lt;/p&gt;

&lt;p&gt;By default, the Html.Pager() renders three CSS classes: pageNumbers, pageNumber, and selectedPageNumber. For example, the bulleted list displayed in Figure 1 is rendered with the following XHTML:&lt;/p&gt;

&lt;p&gt;&amp;lt;ul class='pageNumbers'&amp;gt;&lt;/p&gt;

&lt;p&gt;&amp;lt;li class='pageNumber'&amp;gt;&amp;lt;a href='/Home/Index/2'&amp;gt;&amp;amp;lt;&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;&lt;/p&gt;

&lt;p&gt;&amp;lt;li class='pageNumber'&amp;gt;&amp;lt;a href='/Home/Index/0'&amp;gt;1&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;&lt;/p&gt;

&lt;p&gt;&amp;lt;li class='pageNumber'&amp;gt;&amp;lt;a href='/Home/Index/1'&amp;gt;2&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;&lt;/p&gt;

&lt;p&gt;&amp;lt;li class='pageNumber'&amp;gt;&amp;lt;a href='/Home/Index/2'&amp;gt;3&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;&lt;/p&gt;

&lt;p&gt;&amp;lt;li class='selectedPageNumber'&amp;gt;4&amp;lt;/li&amp;gt;&lt;/p&gt;

&lt;p&gt;&amp;lt;li class='pageNumber'&amp;gt;&amp;lt;a href='/Home/Index/4'&amp;gt;5&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;&lt;/p&gt;

&lt;p&gt;&amp;lt;li class='pageNumber'&amp;gt;&amp;lt;a href='/Home/Index/4'&amp;gt;&amp;amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;&lt;/p&gt;

&lt;p&gt;&amp;lt;/ul&amp;gt;&lt;/p&gt;

&lt;p&gt;Notice that the 4&lt;sup&gt;th&lt;/sup&gt; page number is rendered with the selectedPageNumber CSS class. &lt;/p&gt;

&lt;h4&gt;Setting Pager Options&lt;/h4&gt;

&lt;p&gt;There are several options that you can set when using the Pager HTML Helper. All of these options are represented by the PagerOptions class that you can pass as an additional parameter to the Html.Pager() method. The PagerOptions class supports the following properties:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&amp;#183; IndexParameterName &amp;#8211; The name of the parameter used to pass the page index. This property defaults to the value Id.&lt;/p&gt;

  &lt;p&gt;&amp;#183; MaximumPageNumbers &amp;#8211; The maximum number of page numbers to display. This property defaults to the value 5.&lt;/p&gt;

  &lt;p&gt;&amp;#183; PageNumberFormatString &amp;#8211; A format string that you can apply to each unselected page number.&lt;/p&gt;

  &lt;p&gt;&amp;#183; SelectedPageNumberFormatString &amp;#8211; A format string that you can apply to the selected page number.&lt;/p&gt;

  &lt;p&gt;&amp;#183; ShowPrevious &amp;#8211; When true, a previous link is displayed.&lt;/p&gt;

  &lt;p&gt;&amp;#183; PreviousText &amp;#8211; The text displayed for the previous link. Defaults to &amp;lt;.&lt;/p&gt;

  &lt;p&gt;&amp;#183; ShowNext &amp;#8211; When true, a next link is displayed.&lt;/p&gt;

  &lt;p&gt;&amp;#183; NextText &amp;#8211; The text displayed for the next link. Defaults to &amp;gt;.&lt;/p&gt;

  &lt;p&gt;&amp;#183; ShowNumbers &amp;#8211; When true, page number links are displayed.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;Completely Customizing the Pager User Interface&lt;/h4&gt;

&lt;p&gt;If you want to completely customize the appearance of the pager user interface then you can use the Html.PagerList() method instead of the Html.Pager() method. The Html.PagerList() method returns a list of PagerItem classes. You can render the list of PagerItems in a loop.&lt;/p&gt;

&lt;p&gt;For example, the revised Index view in Listing 5 uses the Html.PagerList() method to display the page numbers.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Listing 5 &amp;#8211; Views\Home\Index.aspx&lt;/b&gt;&lt;/p&gt;

&lt;pre class="xml" name="code"&gt;&amp;lt;%@ Page Language=&amp;quot;C#&amp;quot; AutoEventWireup=&amp;quot;true&amp;quot; CodeBehind=&amp;quot;Index.aspx.cs&amp;quot; Inherits=&amp;quot;Tip44.Views.Home.Index&amp;quot; %&amp;gt;
&amp;lt;%@ Import Namespace=&amp;quot;MvcPaging&amp;quot; %&amp;gt;
&amp;lt;!DOCTYPE html PUBLIC &amp;quot;-//W3C//DTD XHTML 1.0 Transitional//EN&amp;quot; &amp;quot;http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd&amp;quot;&amp;gt;
&amp;lt;html xmlns=&amp;quot;http://www.w3.org/1999/xhtml&amp;quot; &amp;gt;
&amp;lt;head runat=&amp;quot;server&amp;quot;&amp;gt;
    &amp;lt;title&amp;gt;Index&amp;lt;/title&amp;gt;
    &amp;lt;style type=&amp;quot;text/css&amp;quot;&amp;gt;
    
    .pageNumbers
    {
        display:inline;
        margin:0px;
    }
    
    .pageNumbers li
    {
        display: inline;
        padding:3px;
    }
    
    .selectedPageNumber
    {
        font-weight: bold;
        text-decoration: none;
    }
        
    &amp;lt;/style&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
    &amp;lt;div&amp;gt;
    
    &amp;lt;ul&amp;gt;
    &amp;lt;% foreach (var m in ViewData.Model)
       { %&amp;gt;
    
        &amp;lt;li&amp;gt;&amp;lt;%= m.Title %&amp;gt;&amp;lt;/li&amp;gt;
    
    &amp;lt;% } %&amp;gt;
    &amp;lt;/ul&amp;gt;
            
    &amp;lt;%-- Show Page Numbers --%&amp;gt;

    &amp;lt;% foreach (PagerItem item in Html.PagerList(ViewData.Model))
       { %&amp;gt;    
       &amp;lt;a href='&amp;lt;%= item.Url %&amp;gt;' class='&amp;lt;%=item.IsSelected ? &amp;quot;selectedPageNumber&amp;quot; : &amp;quot;pageNumber&amp;quot; %&amp;gt;'&amp;gt;&amp;lt;%= item.Text%&amp;gt;&amp;lt;/a&amp;gt; &amp;amp;nbsp;
    &amp;lt;% } %&amp;gt;

    
 &amp;lt;/div&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;&lt;/pre&gt;

&lt;p&gt;&lt;b&gt;Figure 2 &amp;#8211; Custom pager user interface&lt;/b&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://weblogs.asp.net/blogs/stephenwalther/WindowsLiveWriter/ASP.NETMVCTip44CreateaPagerHTMLHelper_60D/clip_image004_2.jpg"&gt;&lt;img style="border-right: 0px; border-top: 0px; border-left: 0px; border-bottom: 0px" height="286" alt="clip_image004" src="http://weblogs.asp.net/blogs/stephenwalther/WindowsLiveWriter/ASP.NETMVCTip44CreateaPagerHTMLHelper_60D/clip_image004_thumb.jpg" width="286" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The main reason that I added the Html.PagerList() method to the PagerHelper class is to improve the testability of the class. Testing the collection of PageItems returned by Html.PagerList() is easier than testing the single gigantic string returned by Html.Pager(). Behind the scenes, the Html.Pager() method simply calls the Html.PagerList() method. Therefore, building unit tests for the Html.PagerList() method enables me to test the Html.Pager() method as well.&lt;/p&gt;

&lt;h4&gt;Testing the Pager HTML Helper&lt;/h4&gt;

&lt;p&gt;I created a separate test project for unit testing the PagerHelper class. The test project has one test class named PagerHelperTests that contains 10 unit tests.&lt;/p&gt;

&lt;p&gt;One issue that I ran into almost immediately when testing the PagerHelper class was the problem of faking the MVC HtmlHelper class. In order to unit test an extension method on the HtmlHelper class, you need a way of faking of the HtmlHelper class.&lt;/p&gt;

&lt;p&gt;I decided to extend the MvcFakes project with a FakeHtmlHelper class. I used the FakeHtmlHelper class in all of the PagerHelper unit tests. The FakeHtmlHelper is created in the following test class Initialize method:&lt;/p&gt;

&lt;pre class="c#" name="code"&gt;[TestInitialize]
public void Initialize()
{
    // Create fake Html Helper
    var controller = new TestController();
    var routeData = new RouteData();
    routeData.Values[&amp;quot;controller&amp;quot;] = &amp;quot;home&amp;quot;;
    routeData.Values[&amp;quot;action&amp;quot;] = &amp;quot;index&amp;quot;;
    _helper = new FakeHtmlHelper(controller, routeData);

    // Create fake items to page through
    _items = new List&amp;lt;string&amp;gt;();
    for (var i = 0; i &amp;lt; 99; i++)
        _items.Add(String.Format(&amp;quot;item{0}&amp;quot;, i));
}&lt;/pre&gt;

&lt;p&gt;Notice that I need to pass both a controller and an instance of the RouteData class to the FakeHtmlHelper class. The RouteData class represents the controller and action that generated the current view. These values are used to generate the page number links.&lt;/p&gt;

&lt;p&gt;The Intialize() method also creates a set of 100 fake data records. These data records are used in the unit tests as a proxy for actual database records.&lt;/p&gt;

&lt;p&gt;Here&amp;#8217;s a sample of one of the test methods from the PagerHelperTests class:&lt;/p&gt;

&lt;pre class="c#" name="code"&gt;[TestMethod]
public void NoShowNext()
{
    // Arrange
    var page = _items.AsQueryable().ToPageOfList(0, 5);

    // Act
    var options = new PagerOptions { ShowNext = false };
    var results = PagerHelper.PagerList(_helper, page, options);

    // Assert
    foreach (PagerItem item in results)
    { 
        Assert.AreNotEqual(item.Text, options.NextText);
    }
}&lt;/pre&gt;

&lt;p&gt;This test method verifies that setting the ShowNext PagerOption property to the value false causes the Next link to not be displayed.&lt;/p&gt;

&lt;p&gt;In the Arrange section, an instance of the PageOfList class is created that represents a range of records from the fake database records. &lt;/p&gt;

&lt;p&gt;Next, in the Act section, the PagerOptions class is created and the PagerHelper.PagerList() method is called. The PagerHelper.PagerList() method returns a collection of PagerItem objects.&lt;/p&gt;

&lt;p&gt;Finally, in the Assert section, a loop is used to iterate through each PagerItem object to verify that the Next link does not appear in the PagerItems. If the text for the Next link is found then the test fails.&lt;/p&gt;

&lt;h4&gt;Using the PagerHelper in Your Projects&lt;/h4&gt;

&lt;p&gt;At the end of this blog entry, there is a link to download all of the code for the PagerHelper. All of the support classes for the PagerHelper are located in the MvcPaging project. If you want to use the PagerHelper in your MVC projects, you need to add a reference to the MvcPaging assembly.&lt;/p&gt;

&lt;h4&gt;Summary&lt;/h4&gt;

&lt;p&gt;Creating a good Pager HTML Helper is difficult. There are many ways that you might want to customize the user interface for paging through a set of database records. Attempting to accommodate all of the different ways that you might want to customize a paging user interface is close to impossible.&lt;/p&gt;

&lt;p&gt;Working on this project gave me a great deal of respect for the work that Troy Goode and Martijn Boland performed on their implementations of pagers. I hope that this tip can provide you with a useful starting point, and save you some time, when you need to implement a user interface for paging through database records.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://weblogs.asp.net/blogs/stephenwalther/Downloads/Tip44/Tip44.zip"&gt;&lt;strong&gt;Download the Code&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://weblogs.asp.net/aggbug.aspx?PostID=6631471" width="1" height="1"&gt;</description><category domain="http://weblogs.asp.net/stephenwalther/archive/tags/ASP.NET+MVC/default.aspx">ASP.NET MVC</category><category domain="http://weblogs.asp.net/stephenwalther/archive/tags/ASP.NET/default.aspx">ASP.NET</category><category domain="http://weblogs.asp.net/stephenwalther/archive/tags/Tips/default.aspx">Tips</category></item><item><title>ASP.NET MVC Application Building: Forums #5 – Membership</title><link>http://weblogs.asp.net/stephenwalther/archive/2008/09/12/asp-net-mvc-application-building-forums-5-membership.aspx</link><pubDate>Fri, 12 Sep 2008 16:22:40 GMT</pubDate><guid isPermaLink="false">c06e2b9d-981a-45b4-a55f-ab0d8bbfdc1c:6620881</guid><dc:creator>swalther</dc:creator><slash:comments>15</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://weblogs.asp.net/stephenwalther/rsscomments.aspx?PostID=6620881</wfw:commentRss><comments>http://weblogs.asp.net/stephenwalther/archive/2008/09/12/asp-net-mvc-application-building-forums-5-membership.aspx#comments</comments><description>&lt;blockquote&gt;   &lt;p&gt;&lt;i&gt;In this series of blog posts, I build an entire ASP.NET MVC Forums application from start to finish. In this post, I explain how to test and implement authentication and authorization for the Forums application.&lt;/i&gt;&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;Before you read this blog post, you should read the previous posts in this series:&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;&lt;a href="http://weblogs.asp.net/stephenwalther/archive/2008/09/05/asp-net-mvc-application-building-forums-1-create-the-perfect-application.aspx"&gt;ASP.NET MVC Application Building: Forums #1 &amp;#8211; Create the Perfect Application&lt;/a&gt; &amp;#8211; In this first entry, I explain the overall goals of the ASP.NET MVC Forums application. I emphasize the importance of Software Design Principles and justify my choice to use test-driven development.&lt;/p&gt;    &lt;p&gt;&lt;a href="http://weblogs.asp.net/stephenwalther/archive/2008/09/05/asp-net-mvc-application-building-forums-2-create-the-first-unit-test.aspx"&gt;ASP.NET MVC Application Building: Forums #2 &amp;#8211; Create the First Unit Test&lt;/a&gt; &amp;#8211; In the second entry, I build the first unit test and create the Index() action which returns a collection of messages.&lt;/p&gt;    &lt;p&gt;&lt;a href="http://weblogs.asp.net/stephenwalther/archive/2008/09/05/asp-net-mvc-application-building-forums-3-post-messages.aspx"&gt;ASP.NET MVC Application Building: Forums #3 &amp;#8211; Post Messages&lt;/a&gt; &amp;#8211; In the third entry, I add the unit tests and functionality required to post new messages and replies.&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;What about the fourth entry in this series? The fourth (and fourth &amp;#189; posts) were devoted to the subject of validation. I keep revisiting this topic because it is so important to get it right. In the first half of this post, I revisit the issue of validation (yet again). In the second half of this post, I address the issue of authenticating and authorizing users.&lt;/p&gt;  &lt;h4&gt;Validation Revisited, Again&lt;/h4&gt;  &lt;p&gt;I decided to modify the Forums application so that it uses the validation attributes from the System.ComponentModel.DataAnnotations namespace. These are the same set of validation attributes that are used in ASP.NET Dynamic Data applications. In order to use these attributes, you must have Visual Studio Service Pack 1 installed and you must add a reference to the System.ComponentModel.DataAnnotations assembly (located in the Global Assembly Cache).&lt;/p&gt;  &lt;p&gt;If you want to learn more about using these attributes, please read the following blog post:&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;&lt;a href="http://weblogs.asp.net/stephenwalther/archive/2008/09/10/asp-net-mvc-tip-43-use-data-annotation-validators.aspx"&gt;ASP.NET MVC Tip #43 &amp;#8211; Use Data Annotation Validators&lt;/a&gt;&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;Right now, my validation needs are minor. I need to validate that when a user posts a new message, the user supplies both a message subject and message body. In order to perform this type of validation, I can take advantage of the Data Annotations Required attribute in my Message class. The modified Message class in Listing 1 uses the Required attribute.&lt;/p&gt;  &lt;p&gt;&lt;b&gt;Listing 1 &amp;#8211; Message.cs&lt;/b&gt;&lt;/p&gt;  &lt;pre class="c#" name="code"&gt;using System;
using MvcValidation;
using System.Collections.Generic;
using System.Data.Linq;
using System.Web.Mvc;
using System.ComponentModel.DataAnnotations;

namespace MvcForums.Models.Entities
{
    public class Message 
    {
        private DateTime _entryDate = DateTime.Now;

        public Message()
        { }

        public Message(int id, int? parentThreadId, int? parentMessageId, string author, string subject, string body)
        {
            this.Id = id;
            this.ParentThreadId = parentThreadId;
            this.ParentMessageId = parentMessageId;
            this.Author = author;
            this.Subject = subject;
            this.Body = body;
        }

        public int Id { get; set; }
        public int? ParentThreadId { get; set; }
        public int? ParentMessageId { get; set; }
        public string Author { get; set; }

        [Required(ErrorMessage=&amp;quot;You must enter a message subject.&amp;quot;)]
        public string Subject { get; set; }

        [Required(ErrorMessage=&amp;quot;You must enter a message body.&amp;quot;)]        
        public string Body { get; set; }
        
        public DateTime EntryDate 
        {
            get { return _entryDate; }
            set { _entryDate = value; }  
        }

    }
}&lt;/pre&gt;

&lt;p&gt;Notice that I have supplied an ErrorMessage for both of the Required attributes.&lt;/p&gt;

&lt;p&gt;I modified the ForumRepository class so that it accepts a class that implements the IValidation interface in its constructor. In other words, a particular validation framework is injected into the ForumRepository class using dependency injection.&lt;/p&gt;

&lt;p&gt;I modified the AddMessage() method in my ForumsRepository class so that it calls _validation.Validate() before inserting a new Forums message into the database. The new version of the ForumRepository class is contained in Listing 2.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Listing 2 &amp;#8211; Models\ForumRepository.cs&lt;/b&gt;&lt;/p&gt;

&lt;pre class="c#" name="code"&gt;using MvcFakes;
using System.Linq;
using System.Data.Linq;
using LinqToSqlExtensions;
using Microsoft.Web.Mvc;
using System.Collections.Generic;
using MvcForums.Models.Entities;
using MvcValidation;

namespace MvcForums.Models
{
    public class ForumRepository : IForumRepository
    {
        private IDataContext _dataContext;
        private IValidation _validation; 

        public ForumRepository()
            : this(new DataContextWrapper(&amp;quot;conForumsDB&amp;quot;, &amp;quot;~/Models/ForumsDB.xml&amp;quot;), new Validation())
        { }
        
        public ForumRepository(IDataContext dataContext, IValidation validation)
        {
            _dataContext = dataContext;
            _validation = validation;
        }

        public IList&amp;lt;Message&amp;gt; SelectThreads()
        {
            var messages = _dataContext.GetTable&amp;lt;Message&amp;gt;();
            var threads = from m in messages
                          where m.ParentThreadId == null
                          select m;
            return threads.ToList();
        }

        public IList&amp;lt;Message&amp;gt; SelectMessages(int threadId)
        {
            var messages = _dataContext.GetTable&amp;lt;Message&amp;gt;();
            var threads = from m in messages
                          where (m.Id == threadId || m.ParentThreadId == threadId)
                          select m;
            return threads.ToList();
        }


        public Message AddMessage(Message messageToAdd)
        {
            _validation.Validate(messageToAdd);
            _dataContext.Insert(messageToAdd);
            return messageToAdd;
        }
    }
}&lt;/pre&gt;

&lt;p&gt;If any of the Data Annotations validation attributes are invalid (the subject or body is missing) then the Validation.Validate() method throws a ValidationIssueException. &lt;/p&gt;

&lt;p&gt;Finally, I changed the ForumController class so that it catches a ValidationIssueException. The modified ForumController class is contained in Listing 3.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Listing 3 &amp;#8211; Controllers\ForumController.cs&lt;/b&gt;&lt;/p&gt;

&lt;pre class="c#" name="code"&gt;using System;
using System.Web.Mvc;
using MvcForums.Models;
using Microsoft.Web.Mvc;
using MvcForums.Models.Entities;
using MvcValidation;

namespace MvcForums.Controllers
{
    public class ForumController : Controller
    {
        private IForumRepository _repository;

        public ForumController()
            : this(new ForumRepository())
        { }

        public ForumController(IForumRepository repository)
        {
            _repository = repository;
        }

        public ActionResult Index()
        {
            ViewData.Model = _repository.SelectThreads();
            return View(&amp;quot;Index&amp;quot;);
        }

        [AcceptVerbs(&amp;quot;GET&amp;quot;)]
        public ActionResult Create()
        {
            return View(&amp;quot;Create&amp;quot;);
        }

        [AcceptVerbs(&amp;quot;Post&amp;quot;)]
        public ActionResult Create(FormCollection form)
        {
            var messageToCreate = new Message();

            try
            {
                UpdateModel(messageToCreate, new[] { &amp;quot;Author&amp;quot;, &amp;quot;ParentThreadId&amp;quot;, &amp;quot;ParentMessageId&amp;quot;, &amp;quot;Subject&amp;quot;, &amp;quot;Body&amp;quot; });
                _repository.AddMessage(messageToCreate);
            }
            catch (ValidationIssueException vex)
            {
                ViewData.ModelState.CopyValidationIssues(vex);
                return View(&amp;quot;Create&amp;quot;, messageToCreate);
            }
            catch 
            {
                return View(&amp;quot;Create&amp;quot;, messageToCreate);
            }

            // Redirect
            return RedirectToAction(&amp;quot;Index&amp;quot;);
        }

        public ActionResult Thread(int threadId)
        {
            ViewData.Model = _repository.SelectMessages(threadId);
            return View(&amp;quot;Thread&amp;quot;);
        }

    }
}&lt;/pre&gt;

&lt;p&gt;Let&amp;#8217;s examine the Create(FormCollection form) method in more detail. This method starts by creating a new instance of the Message class. Next, it calls the UpdateModel() method to copy the fields from the XHTML form to the instance of the Message class. Notice that the UpdateModel() method uses a whitelist of form field names to copy (You wouldn't want a sneaky hacker overriding the UserName property). If the UpdateModel() method encounters issues then it updates the ModelState and throws an InvalidOperationException.&lt;/p&gt;

&lt;p&gt;Next, the ForumRespository.AddMessage() method is called. Remember that this method calls Validation.Validate() internally. If the Validate() method raises an exception, the exception is caught by the Catch clause for the ValidationIssueException. &lt;/p&gt;

&lt;p&gt;The ValidationIssueException Catch clause uses the CopyValidationIssues() extension method on the ViewData.Model class to copy all of the validation issues represented by the ValidationIssueException class into ModelState. When the Create view is redisplayed, the error messages from the Data Annotations attributes are displayed (see Figure 1).&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Figure 1 &amp;#8211; Validation issues bubble up from the Required validation attributes&lt;/b&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://weblogs.asp.net/blogs/stephenwalther/WindowsLiveWriter/ASP.NETMVCApplicationBuildingForums5Memb_839F/clip_image002_2.jpg"&gt;&lt;img style="border-top-width: 0px; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="300" alt="clip_image002" src="http://weblogs.asp.net/blogs/stephenwalther/WindowsLiveWriter/ASP.NETMVCApplicationBuildingForums5Memb_839F/clip_image002_thumb.jpg" width="508" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I had to create several support classes to get the Data Annotations validation attributes to work with the Forums application. These classes are all contained in a separate project (included with the download) named MvcValidation. The MvcValidation project includes the following classes:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&amp;#183; IValidation &amp;#8211; Represents the contract that all validation providers must support.&lt;/p&gt;

  &lt;p&gt;&amp;#183; Validation &amp;#8211; Implements the Validate() method that executes all of the Data Annotations validation attributes on a class.&lt;/p&gt;

  &lt;p&gt;&amp;#183; ValidationIssue &amp;#8211; Represents one validation issue.&lt;/p&gt;

  &lt;p&gt;&amp;#183; ValidationIssueException &amp;#8211; Represents the exception that is thrown when there is at least one validation issue.&lt;/p&gt;

  &lt;p&gt;&amp;#183; ModelStateDictionaryExtensions &amp;#8211; Adds the CopyValidationIssues() method to the ViewData.ModelState class.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;After I made all of these modifications, my original unit tests for validation continued to run successfully. These two tests are contained in the Controllers\ForumsControllerTest.cs class and look like this:&lt;/p&gt;

&lt;pre class="c#" name="code"&gt;[TestMethod]
public void EmptySubjectFailsValidation()
{
    // Arrange
    var controller = new ForumController(_repository);

    // Act
    var form = new NameValueCollection();
    form.Add(&amp;quot;author&amp;quot;, &amp;quot;Stephen&amp;quot;);
    form.Add(&amp;quot;subject&amp;quot;, String.Empty);
    form.Add(&amp;quot;body&amp;quot;, &amp;quot;Body of new thread&amp;quot;);
    controller.ControllerContext = new FakeControllerContext(controller, form);
    var result = (ViewResult)controller.Create(new FormCollection());

    // Assert
    var modelState = result.ViewData.ModelState;
    Assert.IsFalse(result.ViewData.ModelState.IsValid);
    Assert.AreEqual(&amp;quot;You must enter a message subject.&amp;quot;,
        modelState[&amp;quot;subject&amp;quot;].Errors[0].ErrorMessage);
}


[TestMethod]
public void EmptyBodyFailsValidation()
{
    // Arrange
    var controller = new ForumController(_repository);

    // Act
    var form = new NameValueCollection();
    form.Add(&amp;quot;author&amp;quot;, &amp;quot;Stephen&amp;quot;);
    form.Add(&amp;quot;subject&amp;quot;, &amp;quot;New Message&amp;quot;);
    form.Add(&amp;quot;body&amp;quot;, String.Empty);
    controller.ControllerContext = new FakeControllerContext(controller, form);
    var result = (ViewResult)controller.Create(new FormCollection());

    // Assert
    var modelState = result.ViewData.ModelState;
    Assert.IsFalse(modelState.IsValid);
    Assert.AreEqual(&amp;quot;You must enter a message body.&amp;quot;, 
        modelState[&amp;quot;body&amp;quot;].Errors[0].ErrorMessage);
}&lt;/pre&gt;

&lt;p&gt;The first test verifies that an empty subject form field causes ModelState to contain the error message &amp;#8220;You must enter a message subject.&amp;#8221;. The second test verifies that an empty body form field causes ModelState to contain the error message &amp;quot;You must enter a message body.&amp;quot;.&lt;/p&gt;

&lt;h4&gt;Requiring Authentication&lt;/h4&gt;

&lt;p&gt;We want to make sure that only authenticated users can post a message. We require that people register and login before posting to the forums.&lt;/p&gt;

&lt;p&gt;Since we are being virtuous about test-driven development, we should express this intention with a test. The test in Listing 4 verifies that when an anonymous user executes the Index() action, an HTTP 401 Status Code is returned. The HTTP 401 Status Code means that the user is unauthorized (see &lt;a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html"&gt;http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html&lt;/a&gt;). &lt;/p&gt;

&lt;p&gt;&lt;b&gt;Listing 4 &amp;#8211; AnonymousUserIsRedirectedTest&lt;/b&gt;&lt;/p&gt;

&lt;pre class="c#" name="code"&gt;[TestMethod]
public void AnonymousUserIsRedirected()
{
    // Arrange
    var controller = new ForumController(_repository);
    var fakeContext = new FakeControllerContext(controller);

    // Act            
    controller.ActionInvoker.InvokeAction(fakeContext, &amp;quot;Index&amp;quot;);
    int statusCode = fakeContext.HttpContext.Response.StatusCode;

    // Assert
    Assert.AreEqual(401, statusCode);
}&lt;/pre&gt;

&lt;p&gt;Notice that the test in Listing 4 calls the Controller.ActionInvoker.InvokeAction() method to execute the Index() action. If you want the attributes associated with a controller action to execute then you need to call InvokeAction(). If you just call controller.Index() then any attributes applied to the Index() action are ignored.&lt;/p&gt;

&lt;p&gt;In order to call ActionInvoker.InvokeAction(), we needed to fake the ControllerContext. I faked the ControllerContext by taking advantage of my MvcFakes project. &lt;/p&gt;

&lt;p&gt;When you first run the test, it will fail (see Figure 2). You want the test to fail because only a failing tests provides you with permission to modify your application code.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Figure 2 &amp;#8211; Failing test&lt;/b&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://weblogs.asp.net/blogs/stephenwalther/WindowsLiveWriter/ASP.NETMVCApplicationBuildingForums5Memb_839F/clip_image004_2.jpg"&gt;&lt;img style="border-top-width: 0px; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="203" alt="clip_image004" src="http://weblogs.asp.net/blogs/stephenwalther/WindowsLiveWriter/ASP.NETMVCApplicationBuildingForums5Memb_839F/clip_image004_thumb.jpg" width="513" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We can satisfy the unit test by adding an [Authorize] attribute to the ForumsController. When you add an [Authorize] attribute to a controller class, the attribute is applied to all of the controller actions automatically. The modified ForumController class is declared like this:&lt;/p&gt;

&lt;pre class="c#" name="code"&gt;[Authorize]

public class ForumController : Controller

{

&amp;#8230;.

}&lt;/pre&gt;

&lt;p&gt;After we add the [Authorize] attribute, our test passes and we can breathe a sigh of relief.&lt;/p&gt;

&lt;p&gt;We next need to make sure that a user&amp;#8217;s user name is added to the database when the user submits a new forum post. Therefore, we need another test. The test in Listing 5 verifies when a user named Kermit posts a new message then the Message.Author property has the value Kermit.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Listing 5 &amp;#8211; PostHasUserName Test&lt;/b&gt;&lt;/p&gt;

&lt;pre class="c#" name="code"&gt;[TestMethod]
public void PostHasUserName()
{
    // Arrange
    var controller = new ForumController(_repository);
    
    // Act            
    var form = new NameValueCollection();
    form.Add(&amp;quot;subject&amp;quot;, &amp;quot;New Thread!&amp;quot;);
    form.Add(&amp;quot;body&amp;quot;, &amp;quot;Body of new thread&amp;quot;);
    var fakeContext = new FakeControllerContext(controller, &amp;quot;Kermit&amp;quot;, form);            
    controller.ControllerContext = fakeContext;
    controller.Create(new FormCollection());

    // Assert
    var threads = _repository.SelectThreads();
    var lastThread = threads.Last();
    Assert.AreEqual(&amp;quot;Kermit&amp;quot;, lastThread.Author); 
}&lt;/pre&gt;

&lt;p&gt;The test in Listing 5, once again, takes advantage of the MvcFakes project to create a fake ControllerContext. This ControllerContext represents the user name Kermit and a set of form parameters. The test verifies that when the Create() action is invoked and a new Forums message is created that the new Message.Author property is equal to the value Kermit.&lt;/p&gt;

&lt;p&gt;In order to pass this new test, we need to make two simple modifications to the Create() action in the FormController class. The modified Create() action is contained in Listing 6.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Listing 6 &amp;#8211; CreateAction() (with user name)&lt;/b&gt;&lt;/p&gt;

&lt;pre class="c#" name="code"&gt;[AcceptVerbs(&amp;quot;Post&amp;quot;)]
public ActionResult Create(FormCollection form)
{
    var messageToCreate = new Message();
    messageToCreate.Author = User.Identity.Name;

    try
    {
        UpdateModel(messageToCreate, new[] { &amp;quot;ParentThreadId&amp;quot;, &amp;quot;ParentMessageId&amp;quot;, &amp;quot;Subject&amp;quot;, &amp;quot;Body&amp;quot; });
        _repository.AddMessage(messageToCreate);
    }
    catch (ValidationIssueException vex)
    {
        ViewData.ModelState.CopyValidationIssues(vex);
        return View(&amp;quot;Create&amp;quot;, messageToCreate);
    }
    catch 
    {
        return View(&amp;quot;Create&amp;quot;, messageToCreate);
    }

    // Redirect
    return RedirectToAction(&amp;quot;Index&amp;quot;);
}&lt;/pre&gt;

&lt;p&gt;In Listing 6, I removed &lt;i&gt;Author&lt;/i&gt; from the whitelist of form parameter names used by the UpdateModel() method. We don&amp;#8217;t want to retrieve the message author from an XHTML form parameter. Instead, the value of the Author property is retrieved from User.Identity.Name.&lt;/p&gt;

&lt;p&gt;I debated about exactly where to assign the user name to the Message.Author property. I was tempted to do it in the constructor for the Message class. However, I don&amp;#8217;t want to couple my Message class to the HttpContext. I want to make sure that I can use my model classes with any front end rendering technology including Silverlight, Windows Forms, or a WCF service. Therefore, I assigned the user name to the Message.UserName property in the controller action.&lt;/p&gt;

&lt;p&gt;After I made these changes to the Create() action, all of the tests pass (see Figure 4). I&amp;#8217;ve now implemented basic authentication. In the future, if I modify my code, I can be reassured that I know when authentication is working and when it is not.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Figure 4 &amp;#8211; Success!&lt;/b&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://weblogs.asp.net/blogs/stephenwalther/WindowsLiveWriter/ASP.NETMVCApplicationBuildingForums5Memb_839F/clip_image006_2.jpg"&gt;&lt;img style="border-top-width: 0px; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="345" alt="clip_image006" src="http://weblogs.asp.net/blogs/stephenwalther/WindowsLiveWriter/ASP.NETMVCApplicationBuildingForums5Memb_839F/clip_image006_thumb.jpg" width="513" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As a sanity check, I like to actually run my MVC application occasionally. Fortunately, we don&amp;#8217;t need to create a Login and Register view because the Visual Studio MVC project supplies an AccountController controller, Login view, and Register view for us.&lt;/p&gt;

&lt;p&gt;When we run the MVC Forums application, we get the view in Figure 5. &lt;/p&gt;

&lt;p&gt;&lt;b&gt;Figure 5 &amp;#8211; The Login page&lt;/b&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://weblogs.asp.net/blogs/stephenwalther/WindowsLiveWriter/ASP.NETMVCApplicationBuildingForums5Memb_839F/clip_image008_2.jpg"&gt;&lt;img style="border-top-width: 0px; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="451" alt="clip_image008" src="http://weblogs.asp.net/blogs/stephenwalther/WindowsLiveWriter/ASP.NETMVCApplicationBuildingForums5Memb_839F/clip_image008_thumb.jpg" width="628" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Notice that the Login view includes a link to register. If you click this link, you get the view in Figure 4.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Figure 4 &amp;#8211; Registration page&lt;/b&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://weblogs.asp.net/blogs/stephenwalther/WindowsLiveWriter/ASP.NETMVCApplicationBuildingForums5Memb_839F/clip_image010_2.jpg"&gt;&lt;img style="border-top-width: 0px; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="496" alt="clip_image010" src="http://weblogs.asp.net/blogs/stephenwalther/WindowsLiveWriter/ASP.NETMVCApplicationBuildingForums5Memb_839F/clip_image010_thumb.jpg" width="628" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can complete the form in Figure 4 to create a new account. The Account controller takes advantage of a database named AspNetDB to store user names and passwords. This database is located in the App_Data folder. You can, of course, configure the ASP.NET Membership provider to store the account information in some other database by modifying settings in the Web configuration (web.config) file.&lt;/p&gt;

&lt;p&gt;After you register or login, you are redirected to the URL /Home/Index. Our Forums application does not have a HomeController. Therefore, we need to modify the AccountController so that it redirects to the Index action of the ForumController. You can do a quick search and replace in the Account controller to correct his behavior.&lt;/p&gt;

&lt;h4&gt;Summary&lt;/h4&gt;

&lt;p&gt;This blog entry had two parts. In the first part, I demonstrated how you can perform form validation in an MVC application by taking advantage of the Data Annotations validation attribute classes. We added attributes to our Message class to mark both the Subject and Body properties as required.&lt;/p&gt;

&lt;p&gt;Next, I demonstrated how you can create unit tests to test authentication. We created a unit test that verifies that unauthorized users cannot invoke a controller action. We also created a unit test that verifies that a person&amp;#8217;s user name is added to the database when the person submits a new message.&lt;/p&gt;

&lt;p&gt;We still have more work to do! In the next blog entry, I need to clean up the views for our forums application. I want to make sure that you can see the list of messages, start a new thread, and reply to an existing message. We also should introduce master pages and partials to make easier to maintain our views.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://weblogs.asp.net/blogs/stephenwalther/Downloads/AppBuildingForums5/Forums5.zip"&gt;&lt;strong&gt;Download the Code&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://weblogs.asp.net/aggbug.aspx?PostID=6620881" width="1" height="1"&gt;</description><category domain="http://weblogs.asp.net/stephenwalther/archive/tags/ASP.NET+MVC/default.aspx">ASP.NET MVC</category><category domain="http://weblogs.asp.net/stephenwalther/archive/tags/ASP.NET/default.aspx">ASP.NET</category><category domain="http://weblogs.asp.net/stephenwalther/archive/tags/TDD/default.aspx">TDD</category><category domain="http://weblogs.asp.net/stephenwalther/archive/tags/Application+Building/default.aspx">Application Building</category></item><item><title>ASP.NET MVC Tip #43 – Use Data Annotation Validators</title><link>http://weblogs.asp.net/stephenwalther/archive/2008/09/10/asp-net-mvc-tip-43-use-data-annotation-validators.aspx</link><pubDate>Wed, 10 Sep 2008 23:20:03 GMT</pubDate><guid isPermaLink="false">c06e2b9d-981a-45b4-a55f-ab0d8bbfdc1c:6617450</guid><dc:creator>swalther</dc:creator><slash:comments>15</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://weblogs.asp.net/stephenwalther/rsscomments.aspx?PostID=6617450</wfw:commentRss><comments>http://weblogs.asp.net/stephenwalther/archive/2008/09/10/asp-net-mvc-tip-43-use-data-annotation-validators.aspx#comments</comments><description>&lt;blockquote&gt;   &lt;p&gt;&lt;i&gt;In this tip, I demonstrate how to take advantage of the validators from the System.ComponentModel.DataAnnotations namespace in an MVC application. You can take advantage of these validators to validate form data before submitting the form data into a database.&lt;/i&gt;&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;In my previous tip, I explained how you can take advantage of the validators included with the Microsoft Enterprise Library Validation Application Block to validate an entity before submitting the entity to a database. The Validation Application Block supplies you with a very rich set of validators that support advanced validation scenarios. You can read about using the Validation Application Block in an MVC application here:&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;&lt;a href="http://weblogs.asp.net/stephenwalther/archive/2008/09/09/asp-net-mvc-tip-42-use-the-validation-application-block.aspx" target="_blank"&gt;ASP.NET MVC Tip #42 &amp;#8211; Use the Validation Application Block&lt;/a&gt;&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;In this tip, I explore an alternative set of validators. In this tip, I discuss the validators included in the System.ComponentModel.DataAnnotations namespace. These are the very same validators as you use in a Dynamic Data application. You get these validators when you install Visual Studio Service Pack 1.&lt;/p&gt;  &lt;p&gt;I found the Data Annotation validators to be easier to use in simple validation scenarios than the validators included with the Validaiton Application Block. If you want to perform simple validation tasks such as verifying required fields, verifying email addresses, and checking the length of form fields values then the Data Annotation validators are a great option. &lt;/p&gt;  &lt;h4&gt;Overview of the Data Annotation Validators&lt;/h4&gt;  &lt;p&gt;The Data Annotation validators are contained in the System.ComponentModel.DataAnnotations namespace. In order to use these validators in an MVC application, you must have Visual Studio Service Pack 1 installed. Furthermore, you must add a reference to the System.ComponentModel.DataAnnotations assembly (located in the Global Assembly Cache).&lt;/p&gt;  &lt;p&gt;There are 5 validation attributes in the DataAnnotations namespace:&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;&amp;#183; DataType &amp;#8211; Enables you to verify that a property matches a certain type. Valid types are Custom, DateTime, Date, Time, Duration, PhoneNumber, Currency, Text, Html, MultiLineText, EmailAddress, Password, and URL. You also can specify a custom type.&lt;/p&gt;    &lt;p&gt;&amp;#183; Range &amp;#8211; Enables you to validate whether a property value falls between a specified minimum and maximum value.&lt;/p&gt;    &lt;p&gt;&amp;#183; RegularExpression &amp;#8211; Enables you to validate whether a property value matches a regular expression pattern.&lt;/p&gt;    &lt;p&gt;&amp;#183; Required &amp;#8211; Enables you to validate whether a property has a value. A property value that consists only of whitespace fails validation.&lt;/p&gt;    &lt;p&gt;&amp;#183; StringLength &amp;#8211; Enables you to validate whether a string is less than a specified maximum length.&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;This is a very basic set of validators. They cover the most common validation tasks. Of course, you can create new custom validators by inheriting from the base ValidationAttribute class.&lt;/p&gt;  &lt;h4&gt;Walkthrough: Using the Data Annotation Validators in an MVC Application&lt;/h4&gt;  &lt;p&gt;In this section, I show how you can use the Data Annotation validators in the context of an MVC application. I&amp;#8217;ll show you how to use the validators when building a Movie database application (see Figure 1).&lt;/p&gt;  &lt;p&gt;&lt;b&gt;Figure 1 &amp;#8211; Movie database application&lt;/b&gt;&lt;/p&gt;  &lt;p&gt;&lt;a href="http://weblogs.asp.net/blogs/stephenwalther/WindowsLiveWriter/ASP.NETMVCTip43UseDataAnnotationValidato_E5A2/clip_image002_3.jpg"&gt;&lt;img style="border-top-width: 0px; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="518" alt="clip_image002" src="http://weblogs.asp.net/blogs/stephenwalther/WindowsLiveWriter/ASP.NETMVCTip43UseDataAnnotationValidato_E5A2/clip_image002_thumb.jpg" width="413" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Let&amp;#8217;s start with the model class that we want to validate. The Movie class represents a particular movie. The Movie class is contained in Listing 1.&lt;/p&gt;  &lt;p&gt;&lt;b&gt;Listing 1 &amp;#8211; Movie.cs&lt;/b&gt;&lt;/p&gt;  &lt;pre class="c#" name="code"&gt;using System.ComponentModel.DataAnnotations;

namespace Tip43.Models
{
    public class Movie
    {
        public int Id { get; set; }
        
        [Required(ErrorMessage=&amp;quot;Movie title is required.&amp;quot;)]
        [StringLength(8, ErrorMessage=&amp;quot;Movie title cannot be longer than 8 characters&amp;quot;)]
        public string Title { get; set; }

        [Required(ErrorMessage=&amp;quot;Movie director is required&amp;quot;)]
        public string Director { get; set; }

        [Required(ErrorMessage=&amp;quot;Box office totals is required&amp;quot;)]
        public decimal? BoxOfficeTotals { get; set; }
    }
}&lt;/pre&gt;

&lt;p&gt;The Movie class in Listing 1 represents four properties of a movie: Id, Title, Director, and BoxOfficeTotals. The Data Annotations Required attribute is applied to the Title, Director, and BoxOfficeTotals properties. The StringLength validator is applied to the Title property.&lt;/p&gt;

&lt;p&gt;The controller in Listing 2 exposes three actions: Index(), Create(), and Create(FormCollection form). The Index() action displays all of the existing movies in the database within the Index view. The Create() method displays the form for creating a new movie. Finally, the Create(FormCollection form) method is responsible for validating the new movie and adding the movie to the database.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Listing 2 &amp;#8211; Controllers\HomeController.cs&lt;/b&gt;&lt;/p&gt;

&lt;pre class="c#" name="code"&gt;using System.Web.Mvc;
using Microsoft.Web.Mvc;
using Tip43.Models;
using System.ComponentModel.DataAnnotations;
using Tip43.MvcValidation;

namespace Tip43.Controllers
{
    [HandleError]
    public class HomeController : Controller
    {

        private MovieRepository _repository;

        public HomeController()
        {
            _repository = new MovieRepository();
        }

        public ActionResult Index()
        {
            var movies = _repository.SelectAll();
            return View(&amp;quot;Index&amp;quot;, movies);
        }

        [AcceptVerbs(&amp;quot;GET&amp;quot;)]
        public ActionResult Create()
        {
            return View(&amp;quot;Create&amp;quot;);
        }

        [AcceptVerbs(&amp;quot;POST&amp;quot;)]
        public ActionResult Create(FormCollection form)
        {
            var movieToCreate = new Movie();

            try
            {
                UpdateModel(movieToCreate, new[] { &amp;quot;Title&amp;quot;, &amp;quot;Director&amp;quot;, &amp;quot;BoxOfficeTotals&amp;quot; });
                _repository.Add(movieToCreate);
            }
            catch (ValidationIssueException vex)
            {
                ViewData.ModelState.CopyValidationIssues(vex);
                return View(&amp;quot;Create&amp;quot;, movieToCreate);
            }
            catch
            {
                return View(&amp;quot;Create&amp;quot;, movieToCreate);
            }

            return RedirectToAction(&amp;quot;Index&amp;quot;);
        }
    }
}&lt;/pre&gt;

&lt;p&gt;Let&amp;#8217;s examine the second Create() action in more detail. This action first creates a new instance of the Movie class. Next, the form fields submitted to the Create() action are applied to the new Movie class with the help of the UpdateModel() method (The UpdateModel() method is a standard method of the MVC controller class).&lt;/p&gt;

&lt;p&gt;The controller uses a repository class to interact with the Movie database table. The Add() method is called on the repository class to add the new movie to the database. If the repository class raises a validation error when the Add() method is called then the Catch clause for the ValidationIssueException is executed. This Catch clause copies all of the validation issues from the ValidationIssueException to ModelState and the form for creating a new movie is displayed once again so the validation issues can be fixed. &lt;/p&gt;

&lt;p&gt;The MovieRepository class is contained in Listing 3. &lt;/p&gt;

&lt;p&gt;&lt;b&gt;Listing 3 &amp;#8211; Models\MovieRepository.cs&lt;/b&gt;&lt;/p&gt;

&lt;pre class="c#" name="code"&gt;using System.Collections.Generic;
using System.Linq;
using LinqToSqlExtensions;
using MvcFakes;
using Tip43.MvcValidation;

namespace Tip43.Models
{
    public class MovieRepository
    {
        private IDataContext _dataContext;

        public MovieRepository()
        {
            _dataContext = new DataContextWrapper(&amp;quot;conMovieDB&amp;quot;, &amp;quot;~/Models/MovieDB.xml&amp;quot;);
        }

        public List&amp;lt;Movie&amp;gt; SelectAll()
        {
            return _dataContext.Select&amp;lt;Movie&amp;gt;().ToList();
        }


        public Movie Add(Movie movieToAdd)
        {
            Validation.ValidateAttributes(movieToAdd);
            _dataContext.Insert(movieToAdd);
            return movieToAdd;
        }

    }
}&lt;/pre&gt;

&lt;p&gt;The MovieRepository class exposes two methods: SelectAll() and Add(). The SelectAll() method is called by the Index() action to display all of the existing movies in the database. The Add() method validates the movie being added and inserts the new movie into the database with the help of the LINQ to SQL DataContext class.&lt;/p&gt;

&lt;p&gt;The new movie is validated by calling the Validation.ValidateAttributes() method. This method executes each of the validators on the Movie class. For example, the method executes the Required validators on the Movie class Title, Director, and BoxOfficeTotals properties. If any of these properties do not have a value, then the Validate() method throws a ValidationIssueException.&lt;/p&gt;

&lt;p&gt;You don&amp;#8217;t need to do anything special in an MVC view when taking advantage of the Data Annotation validators. The view in Listing 4 uses the Html.ValidationSummary() helper to display the validation error messages. The Html.TextBox() helper adds a CSS class named input-validation-error to an input field automatically when the input field is associated with a property that has a validation error. The view in Listing 4 highlights input fields associated with a validation error with a red border (see Figure 2).&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Listing 4 &amp;#8211; Views\Home\Create.aspx&lt;/b&gt;&lt;/p&gt;

&lt;pre class="xml" name="code"&gt;&amp;lt;%@ Page Language=&amp;quot;C#&amp;quot; AutoEventWireup=&amp;quot;true&amp;quot; CodeBehind=&amp;quot;Create.aspx.cs&amp;quot; Inherits=&amp;quot;Tip43.Views.Home.Create&amp;quot; %&amp;gt;
&amp;lt;!DOCTYPE html PUBLIC &amp;quot;-//W3C//DTD XHTML 1.0 Transitional//EN&amp;quot; &amp;quot;http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd&amp;quot;&amp;gt;
&amp;lt;html xmlns=&amp;quot;http://www.w3.org/1999/xhtml&amp;quot; &amp;gt;
&amp;lt;head runat=&amp;quot;server&amp;quot;&amp;gt;
    &amp;lt;title&amp;gt;Create Movie&amp;lt;/title&amp;gt;
    &amp;lt;style type=&amp;quot;text/css&amp;quot;&amp;gt;
    
    .input-validation-error
    {
        border: solid 2px red;
        background-color:yellow;
    }
    
    &amp;lt;/style&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
    &amp;lt;div&amp;gt;
    
    &amp;lt;h1&amp;gt;Add Movie&amp;lt;/h1&amp;gt;
    
    &amp;lt;form method=&amp;quot;post&amp;quot; action=&amp;quot;/Home/Create&amp;quot;&amp;gt;
    
    &amp;lt;%= Html.ValidationSummary() %&amp;gt;
    
    
    &amp;lt;label for=&amp;quot;Title&amp;quot;&amp;gt;Movie Title:&amp;lt;/label&amp;gt;
    &amp;lt;br /&amp;gt;
    &amp;lt;%= Html.TextBox(&amp;quot;Title&amp;quot;) %&amp;gt;
    
    &amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;
    &amp;lt;label for=&amp;quot;Director&amp;quot;&amp;gt;Movie Director:&amp;lt;/label&amp;gt;
    &amp;lt;br /&amp;gt;
    &amp;lt;%= Html.TextBox(&amp;quot;Director&amp;quot;) %&amp;gt;
    
    &amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;
    &amp;lt;label for=&amp;quot;BoxOfficeTotals&amp;quot;&amp;gt;Box Office Totals:&amp;lt;/label&amp;gt;
    &amp;lt;br /&amp;gt;
    &amp;lt;%= Html.TextBox(&amp;quot;BoxOfficeTotals&amp;quot;) %&amp;gt;
    
    &amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;
    &amp;lt;input type=&amp;quot;submit&amp;quot; value=&amp;quot;Add Movie&amp;quot; /&amp;gt;
    
    &amp;lt;/form&amp;gt;
    
    
    &amp;lt;/div&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;&lt;/pre&gt;

&lt;p&gt;&lt;b&gt;Figure 2 &amp;#8211; CSS applied to form fields with validation errors&lt;/b&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://weblogs.asp.net/blogs/stephenwalther/WindowsLiveWriter/ASP.NETMVCTip43UseDataAnnotationValidato_E5A2/clip_image002%5B1%5D.jpg"&gt;&lt;img style="border-top-width: 0px; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="518" alt="clip_image002[1]" src="http://weblogs.asp.net/blogs/stephenwalther/WindowsLiveWriter/ASP.NETMVCTip43UseDataAnnotationValidato_E5A2/clip_image002%5B1%5D_thumb.jpg" width="413" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;Summary&lt;/h4&gt;

&lt;p&gt;The Data Annotations validation attributes provide you with a very easy method of performing model-driven validation in an MVC application. For complex validation scenarios, I would recommend taking advantage of the Microsoft Enterprise Validation Application Block. For simple validation scenarios, such as a Movie database application, the Data Annotation validation attributes provide an easier alternative.&lt;/p&gt;

&lt;p&gt;One other advantage of the Data Annotation validators is that using these validators makes it easier to transition a standard MVC application to a Dynamic Data MVC application. It is worth remembering that the very same set of validators is used in ASP.NET Dynamic Data applications.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://weblogs.asp.net/blogs/stephenwalther/Downloads/Tip43/Tip43.zip"&gt;&lt;strong&gt;Download the Code&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://weblogs.asp.net/aggbug.aspx?PostID=6617450" width="1" height="1"&gt;</description><category domain="http://weblogs.asp.net/stephenwalther/archive/tags/ASP.NET+MVC/default.aspx">ASP.NET MVC</category><category domain="http://weblogs.asp.net/stephenwalther/archive/tags/ASP.NET/default.aspx">ASP.NET</category><category domain="http://weblogs.asp.net/stephenwalther/archive/tags/Tips/default.aspx">Tips</category></item><item><title>ASP.NET MVC Tip #42 – Use the Validation Application Block</title><link>http://weblogs.asp.net/stephenwalther/archive/2008/09/09/asp-net-mvc-tip-42-use-the-validation-application-block.aspx</link><pubDate>Wed, 10 Sep 2008 02:50:40 GMT</pubDate><guid isPermaLink="false">c06e2b9d-981a-45b4-a55f-ab0d8bbfdc1c:6614999</guid><dc:creator>swalther</dc:creator><slash:comments>19</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://weblogs.asp.net/stephenwalther/rsscomments.aspx?PostID=6614999</wfw:commentRss><comments>http://weblogs.asp.net/stephenwalther/archive/2008/09/09/asp-net-mvc-tip-42-use-the-validation-application-block.aspx#comments</comments><description>&lt;blockquote&gt;   &lt;p&gt;&lt;i&gt;In this tip, I demonstrate how you can use the Microsoft Enterprise Validation Application Block within an MVC application to perform both basic and advanced form validation. The Validation Application Block supports a rich set of validators that you can begin using in an ASP.NET MVC application.&lt;/i&gt;&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;In this tip, I show how you can use the Microsoft Validation Application Block to perform form validation within an ASP.NET MVC application. Using the Validation Application Block was surprisingly easy. You can download and start using the Validation Application Block in minutes.&lt;/p&gt;  &lt;p&gt;The Validation Application Block uses a model-driven approach to validation. You specify validation rules for your model entities. For example, if you have a Product class then you create validation rules that require the Product.Name property to contain at least 3 characters and the Product.Price property to have a value less than $10.00.&lt;/p&gt;  &lt;p&gt;When a validation rule is broken, you can bubble up a validation error message to an ASP.NET MVC view. That way, the user has a chance to fix the error and submit the form again. For example, the view in Figure 1 illustrates an MVC view that displays validation errors bubbled up from the Validation Application Block.&lt;/p&gt;  &lt;p&gt;&lt;b&gt;Figure 1 &amp;#8211; Validation errors&lt;/b&gt;&lt;/p&gt;  &lt;p&gt;&lt;a href="http://weblogs.asp.net/blogs/stephenwalther/WindowsLiveWriter/ASP.NETMVCTip42UsetheValidationApplicati_116FF/clip_image002_3.jpg"&gt;&lt;img style="border-top-width: 0px; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="546" alt="clip_image002" src="http://weblogs.asp.net/blogs/stephenwalther/WindowsLiveWriter/ASP.NETMVCTip42UsetheValidationApplicati_116FF/clip_image002_thumb.jpg" width="516" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;h4&gt;Using the Validation Application Block in an ASP.NET MVC Application&lt;/h4&gt;  &lt;p&gt;In order to use the Validation Application Block, you need to download the Microsoft Enterprise Library 4.0 from the Microsoft website:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://www.microsoft.com/downloads/details.aspx?FamilyId=90DE37E0-7B42-4044-99BE-F8ECFBBC5B65&amp;amp;displaylang=en&amp;amp;hash=NdKYzXa4U%2beZdzZa%2buGPu%2flKE%2fPfL0GjN6Z1dqDOZX6rPWcMMhAC0c9v5ayzqms35yOrCB%2fAbXBCXbL1z%2f4bhA%3d%3d"&gt;http://www.microsoft.com/downloads/details.aspx?FamilyId=90DE37E0-7B42-4044-99BE-F8ECFBBC5B65&amp;amp;displaylang=en&amp;amp;hash=NdKYzXa4U%2beZdzZa%2buGPu%2flKE%2fPfL0GjN6Z1dqDOZX6rPWcMMhAC0c9v5ayzqms35yOrCB%2fAbXBCXbL1z%2f4bhA%3d%3d&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;After you install the Enterprise Library, the Enterprise Library assemblies can be found in the following folder on your computer:&lt;/p&gt;  &lt;p&gt;C:\Program Files\Microsoft Enterprise Library 4.0 - May 2008\Bin\&lt;/p&gt;  &lt;p&gt;In order to use the Validation Application Block in an MVC application, you need to add a reference to the Microsoft.Practices.EnterpriseLibrary.Validation.dll assembly. Within Visual Studio, select the menu option &lt;b&gt;Project, Add Reference&lt;/b&gt;, select the Browse tab, and browse to the folder that contains the Enterprise Library assemblies (see Figure 2). Click the OK button to add a reference to the assembly.&lt;/p&gt;  &lt;p&gt;&lt;b&gt;Figure 2 &amp;#8211; Adding a reference to the Validation Application Block&lt;/b&gt;&lt;/p&gt;  &lt;p&gt;&lt;a href="http://weblogs.asp.net/blogs/stephenwalther/WindowsLiveWriter/ASP.NETMVCTip42UsetheValidationApplicati_116FF/clip_image004_2.jpg"&gt;&lt;img style="border-top-width: 0px; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="487" alt="clip_image004" src="http://weblogs.asp.net/blogs/stephenwalther/WindowsLiveWriter/ASP.NETMVCTip42UsetheValidationApplicati_116FF/clip_image004_thumb.jpg" width="628" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;h4&gt;Specifying Validation Rules&lt;/h4&gt;  &lt;p&gt;The Validation Application Block supports two methods of specifying validation rules. You can use an attribute approach or you can use a configuration approach. &lt;/p&gt;  &lt;p&gt;When you use an attribute approach, you decorate properties of a class with validator attributes. The Validation Application Block supports a rich set of validators (too many to list here). Here is a list of some of the more useful ones:&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;&amp;#183; NotNullValidator &amp;#8211; Validates that a property does not have the value Null.&lt;/p&gt;    &lt;p&gt;&amp;#183; RangeValidator &amp;#8211; Validates that the value of a property falls between a specified minimum and maximum range.&lt;/p&gt;    &lt;p&gt;&amp;#183; RegexValidator &amp;#8211; Validates that a property value matches a regular expression.&lt;/p&gt;    &lt;p&gt;&amp;#183; StringLengthValidator &amp;#8211; Validates that the value of a string property has a specified minimum and maximum number of characters.&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;Of course, you also can create custom validators. The Validation Application Quickstart project included with the Enterprise Library download includes sample custom validators such as a USStateValidator.&lt;/p&gt;  &lt;p&gt;The class in Listing 1 illustrates how you can use these validators to specify validation rules on a class.&lt;/p&gt;  &lt;p&gt;&lt;b&gt;Listing 1 &amp;#8211; Models\Movie.cs&lt;/b&gt;&lt;/p&gt;  &lt;pre class="c#" name="code"&gt;using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Microsoft.Practices.EnterpriseLibrary.Validation.Validators;
using MvcValidation;

namespace Tip33.Models
{
    public class Movie : IValidated
    {
        public int Id { get; set; }
        
        [StringLengthValidator(3, 50, MessageTemplate=&amp;quot;You must supply a title between 3 and 50 characters.&amp;quot;)]
        public string Title { get; set; }

        [StringLengthValidator(3, 50, MessageTemplate=&amp;quot;You must supply a director between 2 and 50 characters.&amp;quot;)]
        public string Director { get; set; }

        public decimal BoxOfficeTotals { get; set; }
    }
}&lt;/pre&gt;

&lt;p&gt;In Listing 1, the StringLengthValidator is applied to both the Title and Director property. This validator is used to ensure that these properties get a value assigned to them.&lt;/p&gt;

&lt;p&gt;The Validation Application Block supports an alternative method of specifying validation rules. Instead of using attributes, you can specify the validation rules in an XML configuration file. I won&amp;#8217;t explore this option in this tip, but this is an attractive option when you want to keep your entity classes pure.&lt;/p&gt;

&lt;h4&gt;Performing Validation with the Validation Application Block&lt;/h4&gt;

&lt;p&gt;After you specify your validation rules, you can check whether or not a particular class violates the rules by calling the Validation.Validate() method. For example, you can validate an instance of the Movie class named newMovie by calling the Validate() method like this:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;var results = Validation.Validate(newMovie);&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The Validate() method returns a ValidationResult object. The ValidationResult object supports a property named IsValid that indicates whether or not the class being validated failed validation. &lt;/p&gt;

&lt;p&gt;The ValidationResult class is a collection. You can determine exactly which validation rules a class violated by iterating through the collection of broken validation rules represented by the ValidationResult class.&lt;/p&gt;

&lt;h4&gt;Using the Validation Application Block in an ASP.NET MVC Application&lt;/h4&gt;

&lt;p&gt;Let&amp;#8217;s walk through a complete sample MVC application that uses the Validation Application Block. We&amp;#8217;ll use the Validation Application Block with a simple movie database application to validate new movies before the movies are submitted to the database.&lt;/p&gt;

&lt;p&gt;The controller for the Movie database application is contained in Listing 2.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Listing 2 &amp;#8211; Controllers\HomeController.cs&lt;/b&gt;&lt;/p&gt;

&lt;pre class="c#" name="code"&gt;using System.Web.Mvc;
using Microsoft.Web.Mvc;
using MvcValidation;
using Tip33.Models;
using Tip42.Models;

namespace Tip42.Controllers
{
    [HandleError]
    public class HomeController : Controller
    {

        private MovieRepository _repository;

        public HomeController()
        {
            _repository = new MovieRepository();
        }

        public ActionResult Index()
        {
            var movies = _repository.SelectAll();
            return View(&amp;quot;Index&amp;quot;, movies);
        }

        [AcceptVerbs(&amp;quot;GET&amp;quot;)]
        public ActionResult Create()
        {
            return View(&amp;quot;Create&amp;quot;);
        }

        [AcceptVerbs(&amp;quot;POST&amp;quot;)]
        public ActionResult Create(FormCollection form)
        {
            var movieToCreate = new Movie();

            try
            {
                UpdateModel(movieToCreate, new[] { &amp;quot;Title&amp;quot;, &amp;quot;Director&amp;quot;, &amp;quot;BoxOfficeTotals&amp;quot; });
                _repository.Add(movieToCreate);
            }
            catch (ValidationException vex)
            {
                ViewData.ModelState.CopyValidationExceptions(vex);
                return View(&amp;quot;Create&amp;quot;, movieToCreate);
            }
            catch
            {
                return View(&amp;quot;Create&amp;quot;, movieToCreate);
            }

            return RedirectToAction(&amp;quot;Index&amp;quot;);
        }
    }
}&lt;/pre&gt;

&lt;p&gt;The Home controller exposes three actions: Index(), Create(), and Create(FormCollection form). The Index() action displays a list of existing movies from the database. The first Create() method displays a form for inserting a new movie. The second Create() method actually performs the validation and adds the movie to the database.&lt;/p&gt;

&lt;p&gt;Notice that the Home controller class uses a MovieRepository to perform all of its database access operations. The MovieRepository class is contained in Listing 3.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Listing 3 &amp;#8211; Models\MovieRepository.cs&lt;/b&gt;&lt;/p&gt;

&lt;pre class="c#" name="code"&gt;using LinqToSqlExtensions;
using System.Linq;
using MvcFakes;
using MvcValidation;
using Tip33.Models;
using System.Collections.Generic;

namespace Tip42.Models
{
    public class MovieRepository
    {
        private IDataContext _dataContext;

        public MovieRepository()
        {
            _dataContext = new DataContextWrapper(&amp;quot;conMovieDB&amp;quot;, &amp;quot;~/Models/MovieDB.xml&amp;quot;);
        }

        public List&amp;lt;Movie&amp;gt; SelectAll()
        {
            return _dataContext.Select&amp;lt;Movie&amp;gt;().ToList();
        }


        public Movie Add(Movie movieToAdd)
        {
            movieToAdd.Validate&amp;lt;Movie&amp;gt;();
            _dataContext.Insert(movieToAdd);
            return movieToAdd;
        }

    }
}&lt;/pre&gt;

&lt;p&gt;The MovieRepository.Add() method adds a new Movie to the database. Notice that before the Movie is inserted into the database, the Validate() method is called on the Movie object. If the Movie object violates any of the validation rules then calling the Validate() method throws a ValidationException.&lt;/p&gt;

&lt;p&gt;Where does the Validate() method on the Movie class come from? I created a Validate() extension method. This extension method is added to any class that implements the IValidated interface (like the Movie class). The code for the Validate() extension method is contained in Listing 4.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Listing 4 &amp;#8211; MvcValidation\ValidationExtensions.cs&lt;/b&gt;&lt;/p&gt;

&lt;pre class="c#" name="code"&gt;using Microsoft.Practices.EnterpriseLibrary.Validation;

namespace MvcValidation
{
    public static class ValidationExtensions
    {
        public static void Validate&amp;lt;TEntity&amp;gt;(this IValidated entity)
        {
            var results = Validation.Validate&amp;lt;TEntity&amp;gt;((TEntity)entity);
            if (!results.IsValid)
                throw new ValidationException(results);
        }


    }
}&lt;/pre&gt;

&lt;p&gt;The extension method in Listing 4 simply calls the Validation Application Block Validation.Validate() method to retrieve a ValidationResult. If there are validation violations, a ValidationException that represents the validation violations is thrown.&lt;/p&gt;

&lt;p&gt;I created one more extension method that I use in the Home controller: the CopyValidationExceptions() extension method. This extension method copies validation errors from a ValidationException to a ModelState object. The code for the CopyValidationExceptions() method is contained in Listing 4.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Listing 4 &amp;#8211; MvcValidation\ModelStateDictionaryExtensions.cs&lt;/b&gt;&lt;/p&gt;

&lt;pre class="c#" name="code"&gt;using System.Web.Mvc;

namespace MvcValidation
{
    public static class ModelStateDictionaryExtensions
    {
        public static void CopyValidationExceptions(this ModelStateDictionary modelState, ValidationException validationException)
        {
            foreach (var vex in validationException.ValidationResults)
            {
                modelState.AddModelError(vex.Key, null, vex.Message);
            }
        }

    }
}&lt;/pre&gt;

&lt;p&gt;The CopyValidationExceptions() extension method is used within the Create() action in the Home controller like this:&lt;/p&gt;

&lt;pre class="c#" name="code"&gt;[AcceptVerbs(&amp;quot;POST&amp;quot;)]
public ActionResult Create(FormCollection form)
{
    var movieToCreate = new Movie();

    try
    {
        UpdateModel(movieToCreate, new[] { &amp;quot;Title&amp;quot;, &amp;quot;Director&amp;quot;, &amp;quot;BoxOfficeTotals&amp;quot; });
        _repository.Add(movieToCreate);
    }
    catch (ValidationException vex)
    {
        ViewData.ModelState.CopyValidationExceptions(vex);
        return View(&amp;quot;Create&amp;quot;, movieToCreate);
    }
    catch
    {
        return View(&amp;quot;Create&amp;quot;, movieToCreate);
    }

    return RedirectToAction(&amp;quot;Index&amp;quot;);
}&lt;/pre&gt;

&lt;p&gt;If there is a ValdationException, the validation violations are copied to ModelState so that the validation errors can be rendered in the MVC view.&lt;/p&gt;

&lt;p&gt;I didn&amp;#8217;t have to do anything special to display the validation errors in an MVC view. The Create view is contained in Listing 5.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Listing 5 --- Views\Home\Create.aspx&lt;/b&gt;&lt;/p&gt;

&lt;pre class="xml" name="code"&gt;&amp;lt;%@ Page Language=&amp;quot;C#&amp;quot; AutoEventWireup=&amp;quot;true&amp;quot; CodeBehind=&amp;quot;Create.aspx.cs&amp;quot; Inherits=&amp;quot;Tip42.Views.Home.Create&amp;quot; %&amp;gt;
&amp;lt;!DOCTYPE html PUBLIC &amp;quot;-//W3C//DTD XHTML 1.0 Transitional//EN&amp;quot; &amp;quot;http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd&amp;quot;&amp;gt;
&amp;lt;html xmlns=&amp;quot;http://www.w3.org/1999/xhtml&amp;quot; &amp;gt;
&amp;lt;head runat=&amp;quot;server&amp;quot;&amp;gt;
    &amp;lt;title&amp;gt;Create Movie&amp;lt;/title&amp;gt;
    &amp;lt;style type=&amp;quot;text/css&amp;quot;&amp;gt;
    
    .input-validation-error
    {
        border: solid 2px red;
        background-color:yellow;
    }
    
    &amp;lt;/style&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
    &amp;lt;div&amp;gt;
    
    &amp;lt;h1&amp;gt;Add Movie&amp;lt;/h1&amp;gt;
    
    &amp;lt;form method=&amp;quot;post&amp;quot; action=&amp;quot;/Home/Create&amp;quot;&amp;gt;
    
    &amp;lt;%= Html.ValidationSummary() %&amp;gt;
    
    
    &amp;lt;label for=&amp;quot;Title&amp;quot;&amp;gt;Movie Title:&amp;lt;/label&amp;gt;
    &amp;lt;br /&amp;gt;
    &amp;lt;%= Html.TextBox(&amp;quot;Title&amp;quot;) %&amp;gt;
    
    &amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;
    &amp;lt;label for=&amp;quot;Director&amp;quot;&amp;gt;Movie Director:&amp;lt;/label&amp;gt;
    &amp;lt;br /&amp;gt;
    &amp;lt;%= Html.TextBox(&amp;quot;Director&amp;quot;) %&amp;gt;
    
    &amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;
    &amp;lt;label for=&amp;quot;BoxOfficeTotals&amp;quot;&amp;gt;Box Office Totals:&amp;lt;/label&amp;gt;
    &amp;lt;br /&amp;gt;
    &amp;lt;%= Html.TextBox(&amp;quot;BoxOfficeTotals&amp;quot;) %&amp;gt;
    
    &amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;
    &amp;lt;input type=&amp;quot;submit&amp;quot; value=&amp;quot;Add Movie&amp;quot; /&amp;gt;
    
    &amp;lt;/form&amp;gt;
    
    
    &amp;lt;/div&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;&lt;/pre&gt;

&lt;p&gt;Notice that the Create view takes advantage of the Html.ValidationSummary() HTML helper method to display the errors. Furthermore, when there is a validation error associated with a particular TextBox, the Html.TextBox helper renders an input-validation-error CSS class automatically. The style sheet defined at the top of the view causes input fields associated with a validation error to appear with a thick red border and a yellow background (see Figure 3).&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Figure 3 &amp;#8211; Validation errors&lt;/b&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://weblogs.asp.net/blogs/stephenwalther/WindowsLiveWriter/ASP.NETMVCTip42UsetheValidationApplicati_116FF/clip_image002%5B1%5D.jpg"&gt;&lt;img style="border-top-width: 0px; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="546" alt="clip_image002[1]" src="http://weblogs.asp.net/blogs/stephenwalther/WindowsLiveWriter/ASP.NETMVCTip42UsetheValidationApplicati_116FF/clip_image002%5B1%5D_thumb.jpg" width="516" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;Summary&lt;/h4&gt;

&lt;p&gt;To be honest, I assumed that getting the Validation Application Block to work with an ASP.NET MVC application would take a lot of effort. Instead, I was able to fully integrate the Validation Block with my Movie database application within a couple of hours. The Validation Application Block is an excellent option for providing validation for an ASP.NET MVC application.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://weblogs.asp.net/blogs/stephenwalther/Downloads/Tip42/Tip42.zip"&gt;&lt;strong&gt;Download the Code&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://weblogs.asp.net/aggbug.aspx?PostID=6614999" width="1" height="1"&gt;</description><category domain="http://weblogs.asp.net/stephenwalther/archive/tags/ASP.NET+MVC/default.aspx">ASP.NET MVC</category><category domain="http://weblogs.asp.net/stephenwalther/archive/tags/ASP.NET/default.aspx">ASP.NET</category><category domain="http://weblogs.asp.net/stephenwalther/archive/tags/Tips/default.aspx">Tips</category></item><item><title>ASP.NET MVC Application Building: Forums #4 1/2– Validation Revisited</title><link>http://weblogs.asp.net/stephenwalther/archive/2008/09/09/asp-net-mvc-application-building-forums-4-1-2-validation-revisited.aspx</link><pubDate>Tue, 09 Sep 2008 08:17:34 GMT</pubDate><guid isPermaLink="false">c06e2b9d-981a-45b4-a55f-ab0d8bbfdc1c:6612238</guid><dc:creator>swalther</dc:creator><slash:comments>18</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://weblogs.asp.net/stephenwalther/rsscomments.aspx?PostID=6612238</wfw:commentRss><comments>http://weblogs.asp.net/stephenwalther/archive/2008/09/09/asp-net-mvc-application-building-forums-4-1-2-validation-revisited.aspx#comments</comments><description>&lt;p&gt;I received a lot of feedback on my last blog entry on implementing server-side form validation for the MVC Forums application. Thank you everyone for the feedback! &lt;/p&gt;  &lt;p&gt;In my previous blog entry, I described how you can perform validation in an MVC application by taking advantage of custom validation attributes. By decorating your model with attributes, such as the RequiredValidator and RegularExpressionValidator attributes, you can enforce validation rules and display custom validation error messages.&lt;/p&gt;  &lt;p&gt;In order to perform more complex validation, I suggested using a CustomValidator that could be applied to the entity class. The CustomValidator class points to another class that executes custom validation rules.&lt;/p&gt;  &lt;p&gt;One piece of feedback that I consistently received was that this approach for performing custom validation was too complex for most scenarios. Most people liked the attribute approach to validation, but also wanted an easy way to do custom validation.&lt;/p&gt;  &lt;p&gt;Therefore, in this blog entry, I take a slightly different approach to handling the problem of form validation. In this blog entry, I combine the approach described by &lt;a href="http://weblogs.asp.net/scottgu/archive/2008/09/02/asp-net-mvc-preview-5-and-form-posting-scenarios.aspx"&gt;Scott Guthrie&lt;/a&gt; with an attribute approach. In other words, I mix an imperative and declarative approach to validation. I call this approach a &lt;i&gt;hybrid approach&lt;/i&gt; to form validation.&lt;/p&gt;  &lt;p&gt;In this blog entry, I describe the hybrid approach. Before we get into the details, I should mention that I just came across an excellent blog entry by Steve Sanderson that describes almost exactly the same approach to validation described in this blog entry (however -- his blog entry has much nicer diagrams than mine). You can view his post here:&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;&lt;a href="http://blog.codeville.net/2008/09/08/thoughts-on-validation-in-aspnet-mvc-applications/"&gt;http://blog.codeville.net/2008/09/08/thoughts-on-validation-in-aspnet-mvc-applications/&lt;/a&gt;&lt;/p&gt; &lt;/blockquote&gt;  &lt;h4&gt;An Imperative Approach to Form Validation&lt;/h4&gt;  &lt;p&gt;Let&amp;#8217;s start with the approach described by Scott Guthrie. When following this approach to form validation, you must create the following classes: &lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;&amp;#183; IRuleEntity &amp;#8211; an interface that each entity must implement in order to be validated.&lt;/p&gt;    &lt;p&gt;&amp;#183; RuleViolation &amp;#8211; a class that represents a validation error.&lt;/p&gt;    &lt;p&gt;&amp;#183; RuleViolationException &amp;#8211; an exception that is raised when an entity fails validation.&lt;/p&gt;    &lt;p&gt;&amp;#183; Validation &amp;#8211; a class that includes a method for copying rule violations to ModelState.&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;The IRuleEntity interface is really simple. It consists of two methods named Validate() and GetRuleViolations(). This interface is contained in Listing 1.&lt;/p&gt;  &lt;p&gt;&lt;b&gt;Listing 1 &amp;#8211; IRuleEntity.cs&lt;/b&gt;&lt;/p&gt;  &lt;pre class="c#" name="code"&gt;using System.Collections.Generic;

namespace MvcValidation
{
    public interface IRuleEntity
    {
        List&amp;lt;RuleViolation&amp;gt; GetRuleViolations();
        void Validate();
    }
}&lt;/pre&gt;

&lt;p&gt;The RuleViolation class represents a particular validation error. A collection of RuleViolations is used to represent all of the validation errors that result when submitting a form. The RuleViolation class is contained in Listing 2.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Listing 2 &amp;#8211; RuleViolation.cs&lt;/b&gt;&lt;/p&gt;

&lt;pre class="c#" name="code"&gt;using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace MvcValidation
{
    public class RuleViolation
    {
        public string PropertyName { get; private set; }

        public object PropertyValue { get; private set; }

        public string ErrorMessage { get; private set; }

        public RuleViolation(string propertyName, object propertyValue, string errorMessage)
        {
            this.PropertyName = propertyName;
            this.PropertyValue = propertyValue;
            this.ErrorMessage = errorMessage;
        }

    }
}&lt;/pre&gt;

&lt;p&gt;When there is at least 1 rule violation, a RuleViolationException is raised. This special exception class is contained in Listing 3.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Listing 3 &amp;#8211; RuleViolationException.cs&lt;/b&gt;&lt;/p&gt;

&lt;pre class="c#" name="code"&gt;using System;
using System.Collections.Generic;

namespace MvcValidation
{
    public class RuleViolationException : Exception
    {
        public RuleViolationException(string message, List&amp;lt;RuleViolation&amp;gt; validationIssues)
            : base(message)
        {
            this.ValidationIssues = validationIssues;
        }

        public List&amp;lt;RuleViolation&amp;gt; ValidationIssues { get; private set; }
    }
}&lt;/pre&gt;

&lt;p&gt;Finally, the Validation class is a utility class that exposes one method named UpdateModelStateWithViolations(). This method copies all of the rule violations to a controller class&amp;#8217;s ModelState. The Validation class is contained in Listing 4.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Listing 4 &amp;#8211; Validation.cs&lt;/b&gt;&lt;/p&gt;

&lt;pre class="c#" name="code"&gt;using System.Web.Mvc;

namespace MvcValidation
{
    public class Validation
    {
        public static void UpdateModelStateWithViolations(RuleViolationException ruleViolationException, ModelStateDictionary modelState)
        {
            foreach (var issue in ruleViolationException.ValidationIssues)
            {
                var value = issue.PropertyValue ?? string.Empty;
                modelState.AddModelError(issue.PropertyName, value.ToString(), issue.ErrorMessage);
            }
        }


    }
}&lt;/pre&gt;

&lt;p&gt;Let&amp;#8217;s take a look at how all of these classes work together. The Message class in Listing 5 represents a Forum message. Notice that this class implements the IRuleEntity interface by implementing both the Validate() and the GetRuleViolations() methods.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Listing 5 &amp;#8211; Message.cs&lt;/b&gt;&lt;/p&gt;

&lt;pre class="c#" name="code"&gt;using System;
using MvcValidation;
using System.Collections.Generic;
using System.Data.Linq;
using MvcValidation.Attributes;
using System.Web.Mvc;

namespace MvcForums.Models.Entities
{
    public class Message :IRuleEntity
    {
        private DateTime _entryDate = DateTime.Now;

        public Message()
        { }

        public Message(int id, int? parentThreadId, int? parentMessageId, string author, string subject, string body)
        {
            this.Id = id;
            this.ParentThreadId = parentThreadId;
            this.ParentMessageId = parentMessageId;
            this.Author = author;
            this.Subject = subject;
            this.Body = body;
        }

        public int Id { get; set; }
        public int? ParentThreadId { get; set; }
        public int? ParentMessageId { get; set; }
        public string Author { get; set; }
        public string Subject { get; set; }
        public string Body { get; set; }

        
        public DateTime EntryDate 
        {
            get { return _entryDate; }
            set { _entryDate = value; }  
        }

        #region IRuleEntity Members

        public List&amp;lt;RuleViolation&amp;gt; GetRuleViolations()
        {
            var validationIssues = new List&amp;lt;RuleViolation&amp;gt;();

            // Validate Subject
            if (String.IsNullOrEmpty(this.Subject))
                validationIssues.Add(new RuleViolation(&amp;quot;subject&amp;quot;, this.Subject, &amp;quot;You must enter a message subject.&amp;quot;));

            // Validate Body
            if (String.IsNullOrEmpty(this.Body))
                validationIssues.Add(new RuleViolation(&amp;quot;body&amp;quot;, this.Body, &amp;quot;You must enter a message body.&amp;quot;)); 

            return validationIssues;
        }

        public void Validate()
        {
            var validationIssues = GetRuleViolations();
            if (validationIssues.Count &amp;gt; 0)
                throw new RuleViolationException(&amp;quot;Validation Issues&amp;quot;, validationIssues);
        }

        #endregion

    }
}&lt;/pre&gt;

&lt;p&gt;The Validate() method calls the GetRuleViolations() method. If there are any validation rule violations then the Validate() method throws a RuleViolationException.&lt;/p&gt;

&lt;p&gt;In the Forums application, the Validate() method is called within the ForumRepository. Before a new Message is added to the database, the Validate() method is called on the Message class like this:&lt;/p&gt;

&lt;pre class="c#" name="code"&gt;public Message AddMessage(Message messageToAdd)

{

  messageToAdd.Validate();

  _dataContext.Insert(messageToAdd);

  return messageToAdd;

}&lt;/pre&gt;

&lt;p&gt;If there is a validation error then a RuleViolationException is thrown and the message never gets inserted into the database.&lt;/p&gt;

&lt;p&gt;The ForumRepository is used within the Forum controller. When you submit a form for creating a new forum message, the ForumController.Create(FormCollection collecction) method is called. The Forum controller is contained in Listing 6.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Listing 6 &amp;#8211; ForumController.cs&lt;/b&gt;&lt;/p&gt;

&lt;pre class="c#" name="code"&gt;using System;
using System.Web.Mvc;
using MvcForums.Models;
using Microsoft.Web.Mvc;
using MvcForums.Models.Entities;
using MvcValidation;

namespace MvcForums.Controllers
{
    public class ForumController : Controller
    {
        private IForumRepository _repository;

        public ForumController()
            : this(new ForumRepository())
        { }

        public ForumController(IForumRepository repository)
        {
            _repository = repository;
        }

        public ActionResult Index()
        {
            ViewData.Model = _repository.SelectThreads();
            return View(&amp;quot;Index&amp;quot;);
        }

        [AcceptVerbs(&amp;quot;GET&amp;quot;)]
        public ActionResult Create()
        {
            return View(&amp;quot;Create&amp;quot;);
        }

        [AcceptVerbs(&amp;quot;Post&amp;quot;)]
        public ActionResult Create(FormCollection form)
        {
            var messageToCreate = new Message();

            try
            {
                UpdateModel(messageToCreate, new[] { &amp;quot;Author&amp;quot;, &amp;quot;ParentThreadId&amp;quot;, &amp;quot;ParentMessageId&amp;quot;, &amp;quot;Subject&amp;quot;, &amp;quot;Body&amp;quot; });
                _repository.AddMessage(messageToCreate);
            }
            catch (RuleViolationException rex)
            {
                Validation.UpdateModelStateWithViolations(rex, ViewData.ModelState);
                return View(&amp;quot;Create&amp;quot;, messageToCreate);
            }
            catch (Exception ex)
            {
                ViewData.ModelState.AddModelError(&amp;quot;message&amp;quot;, null, &amp;quot;Could not save message.&amp;quot;);
                return View(&amp;quot;Create&amp;quot;, messageToCreate);
            }

            // Redirect
            return RedirectToAction(&amp;quot;Index&amp;quot;);
        }

        public ActionResult Thread(int threadId)
        {
            ViewData.Model = _repository.SelectMessages(threadId);
            return View(&amp;quot;Thread&amp;quot;);
        }

    }
}&lt;/pre&gt;

&lt;p&gt;Notice that there are two Create() methods. The first Create() method is invoked only for an HTTP GET operation. This action returns the form for creating a new Message (see Figure 1).&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Figure 1 &amp;#8211; Form for adding a Forums message&lt;/b&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://weblogs.asp.net/blogs/stephenwalther/WindowsLiveWriter/ASP.NETMVCApplicationBuildingForums412Va_1228/clip_image002_2.jpg"&gt;&lt;img style="border-top-width: 0px; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="295" alt="clip_image002" src="http://weblogs.asp.net/blogs/stephenwalther/WindowsLiveWriter/ASP.NETMVCApplicationBuildingForums412Va_1228/clip_image002_thumb.jpg" width="514" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The second Create() method is invoked when an XHTML form is posted to the server. When the form data is posted, the Create() method calls the Controller.UpdateModel() method to generate a Product class that has all of the values submitted in the form. Next, the ForumRepository.Add() method is called to add the new instance of the Product class to the database.&lt;/p&gt;

&lt;p&gt;Both statements are wrapped in a Try..Catch block. If either the Controller.UpdateModel() or the ForumRepository.Add() methods fail then the Catch clause of the Try&amp;#8230;Catch statement is executed. The first Catch clause updates the ViewData.ModelState property with information about the validation errors by copying the validation errors from the RuleViolationException to ModelState. Next, the Create view is redisplayed.&lt;/p&gt;

&lt;p&gt;Notice that there are two Catch clauses. The second Catch clause captures a generic Exception. This Catch clause is invoked when the UpdateModel() method encounters a validation issue (the wrong type of value is being assigned to a property). This clause will be invoked when there are network problems communicating with your database server or your database server crashes. Notice that a error key named message is updated.&lt;/p&gt;

&lt;p&gt;The Create view is contained in Listing 7.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Listing 7 -- Create.aspx&lt;/strong&gt;&lt;/p&gt;

&lt;pre class="xml" name="code"&gt;&amp;lt;%@ Page Language=&amp;quot;C#&amp;quot; AutoEventWireup=&amp;quot;true&amp;quot; CodeBehind=&amp;quot;Create.aspx.cs&amp;quot; Inherits=&amp;quot;MvcForums.Views.Forum.Create&amp;quot; %&amp;gt;
&amp;lt;!DOCTYPE html PUBLIC &amp;quot;-//W3C//DTD XHTML 1.0 Transitional//EN&amp;quot; &amp;quot;http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd&amp;quot;&amp;gt;
&amp;lt;html xmlns=&amp;quot;http://www.w3.org/1999/xhtml&amp;quot; &amp;gt;
&amp;lt;head runat=&amp;quot;server&amp;quot;&amp;gt;
    &amp;lt;title&amp;gt;Index&amp;lt;/title&amp;gt;
    &amp;lt;style type=&amp;quot;text/css&amp;quot;&amp;gt;
    
    .input-validation-error
    {
        border: solid 2px red;
    }
    
    #message
    {
        padding: 5px;
        color:red;   
    }
    
    &amp;lt;/style&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
    &amp;lt;div&amp;gt;
    
    &amp;lt;form method=&amp;quot;post&amp;quot; action=&amp;quot;/Forum/Create&amp;quot;&amp;gt;
    
    &amp;lt;div id=&amp;quot;message&amp;quot;&amp;gt;
    &amp;lt;%= Html.ValidationMessage(&amp;quot;message&amp;quot;) %&amp;gt;
    &amp;lt;/div&amp;gt;
    
    &amp;lt;input name=&amp;quot;author&amp;quot; type=&amp;quot;hidden&amp;quot; value=&amp;quot;Stephen&amp;quot; /&amp;gt;
    
    &amp;lt;label for=&amp;quot;subject&amp;quot;&amp;gt;Subject:&amp;lt;/label&amp;gt;
    &amp;lt;%= Html.TextBox(&amp;quot;subject&amp;quot;) %&amp;gt;
    &amp;lt;%= Html.ValidationMessage(&amp;quot;subject&amp;quot;) %&amp;gt;
    
    &amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;
    &amp;lt;label for=&amp;quot;body&amp;quot;&amp;gt;Body:&amp;lt;/label&amp;gt;
    &amp;lt;%= Html.TextArea(&amp;quot;body&amp;quot;) %&amp;gt;
    &amp;lt;%= Html.ValidationMessage(&amp;quot;body&amp;quot;) %&amp;gt;

    &amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;
    &amp;lt;input type=&amp;quot;submit&amp;quot; value=&amp;quot;Post&amp;quot; /&amp;gt;
    
    &amp;lt;/form&amp;gt;
    
    &amp;lt;/div&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;&lt;/pre&gt;

&lt;p&gt;&amp;#160;&lt;/p&gt;

&lt;p&gt;Notice that the view includes a generic error message (named message).&lt;/p&gt;

&lt;p&gt;&amp;#160;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Figure 2 -- Generic error message&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://weblogs.asp.net/blogs/stephenwalther/WindowsLiveWriter/ASP.NETMVCApplicationBuildingForums412Va_1228/image_2.png"&gt;&lt;img style="border-right: 0px; border-top: 0px; border-left: 0px; border-bottom: 0px" height="342" alt="image" src="http://weblogs.asp.net/blogs/stephenwalther/WindowsLiveWriter/ASP.NETMVCApplicationBuildingForums412Va_1228/image_thumb.png" width="285" border="0" /&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;&amp;#160;&lt;/p&gt;

&lt;p&gt;This approach to performing validation is an imperative approach. The validation rules are expressed by imperative code in the GetRuleViolations() method like this:&lt;/p&gt;

&lt;pre class="c#" name="code"&gt;public List&amp;lt;RuleViolation&amp;gt; GetRuleViolations()
{
    var validationIssues = new List&amp;lt;RuleViolation&amp;gt;();

    // Validate Subject
    if (String.IsNullOrEmpty(this.Subject))
        validationIssues.Add(new RuleViolation(&amp;quot;subject&amp;quot;, this.Subject, &amp;quot;You must enter a message subject.&amp;quot;));

    // Validate Body
    if (String.IsNullOrEmpty(this.Body))
        validationIssues.Add(new RuleViolation(&amp;quot;body&amp;quot;, this.Body, &amp;quot;You must enter a message body.&amp;quot;)); 

    return validationIssues;
}&lt;/pre&gt;

&lt;p&gt;Using an imperative approach you can perform any type of validation that you need. For example, you can perform a database lookup to make sure that the Message subject and body are unique in the database before inserting the Message into the database.&lt;/p&gt;

&lt;h4&gt;A Declarative Approach to Form Validation&lt;/h4&gt;

&lt;p&gt;I am a lazy person. I like a declarative approach to performing form validation. A declarative approach enables you to validate form data without writing any code.&lt;/p&gt;

&lt;p&gt;Another advantage of a declarative approach is that a declarative approach enables you to easily implement client-side validation. We want to generate client-side validators from our server-side validators. That way, we can validate a form without posting the form back to the server.&lt;/p&gt;

&lt;p&gt;The modified Message class in Listing 8 uses a declarative approach to validation.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Listing 8 &amp;#8211; Message.cs (with attributes)&lt;/b&gt;&lt;/p&gt;

&lt;pre class="c#" name="code"&gt;using System;
using MvcValidation;
using System.Collections.Generic;
using System.Data.Linq;
using MvcValidation.Attributes;
using System.Web.Mvc;

namespace MvcForums.Models.Entities
{
    public class Message :IRuleEntity
    {
        private DateTime _entryDate = DateTime.Now;

        public Message()
        { }

        public Message(int id, int? parentThreadId, int? parentMessageId, string author, string subject, string body)
        {
            this.Id = id;
            this.ParentThreadId = parentThreadId;
            this.ParentMessageId = parentMessageId;
            this.Author = author;
            this.Subject = subject;
            this.Body = body;
        }

        public int Id { get; set; }
        public int? ParentThreadId { get; set; }
        public int? ParentMessageId { get; set; }
        public string Author { get; set; }

        [RequiredValidator(&amp;quot;You must enter a message subject.&amp;quot;)]
        public string Subject { get; set; }

        [RequiredValidator(&amp;quot;You must enter a message body.&amp;quot;)]        
        public string Body { get; set; }

        
        public DateTime EntryDate 
        {
            get { return _entryDate; }
            set { _entryDate = value; }  
        }

        #region IRuleEntity Members

        public List&amp;lt;RuleViolation&amp;gt; GetRuleViolations()
        {
            var validationIssues = new List&amp;lt;RuleViolation&amp;gt;();

        // Validate attributes
            AttributeValidation.Validate(this, validationIssues);

            return validationIssues;
        }

        public void Validate()
        {
            var validationIssues = GetRuleViolations();
            if (validationIssues.Count &amp;gt; 0)
                throw new RuleViolationException(&amp;quot;Validation Issues&amp;quot;, validationIssues);
        }

        #endregion

    }
}&lt;/pre&gt;

&lt;p&gt;Notice that both the Subject and Body properties in the modified Message class are decorated with the RequiredValidator attribute. The RequiredValidator attribute marks both of these properties as required.&lt;/p&gt;

&lt;p&gt;Notice, furthermore, that the imperative code has been removed from the GetRuleViolations() method. Instead, the AttributeValidation.Validate() method is called. This method calls the Validate() method on each validator attribute applied to the Message class. If at least one validator fails then the Message class fails validation and a RuleViolationException is thrown.&lt;/p&gt;

&lt;p&gt;Clearly, you can mix both an attribute and non-attribute approach to validation. For common validation tasks, you can use standard validators such as the RequiredValidator , LengthValidator, or RegularExpressionValidator. For more complicated types of validation, you can write imperative code to perform the validation.&lt;/p&gt;

&lt;h4&gt;Summary&lt;/h4&gt;

&lt;p&gt;In this blog entry, I&amp;#8217;ve described an approach to validation that combines imperative and declarative validation. I believe that the easiest approach for common validation tasks is a declarative approach. For more complex or specialized validation tasks, write imperative validation code. In the case of the Forums application, my plan is to take a hybrid approach to validation.&lt;/p&gt;&lt;img src="http://weblogs.asp.net/aggbug.aspx?PostID=6612238" width="1" height="1"&gt;</description><category domain="http://weblogs.asp.net/stephenwalther/archive/tags/ASP.NET+MVC/default.aspx">ASP.NET MVC</category><category domain="http://weblogs.asp.net/stephenwalther/archive/tags/ASP.NET/default.aspx">ASP.NET</category><category domain="http://weblogs.asp.net/stephenwalther/archive/tags/Application+Building/default.aspx">Application Building</category></item><item><title>ASP.NET MVC Application Building: Forums #4 – Server-Side Form Validation</title><link>http://weblogs.asp.net/stephenwalther/archive/2008/09/08/asp-net-mvc-application-building-forums-4-server-side-form-validation.aspx</link><pubDate>Mon, 08 Sep 2008 10:22:24 GMT</pubDate><guid isPermaLink="false">c06e2b9d-981a-45b4-a55f-ab0d8bbfdc1c:6608776</guid><dc:creator>swalther</dc:creator><slash:comments>14</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://weblogs.asp.net/stephenwalther/rsscomments.aspx?PostID=6608776</wfw:commentRss><comments>http://weblogs.asp.net/stephenwalther/archive/2008/09/08/asp-net-mvc-application-building-forums-4-server-side-form-validation.aspx#comments</comments><description>&lt;p&gt;In this series of blog entries, I build an entire ASP.NET MVC forums application from start to finish. In this blog entry, I add server-side form validation to the forums application.&lt;/p&gt;  &lt;div style="border-right: red 5px solid; padding-right: 10px; border-top: red 5px solid; padding-left: 10px; padding-bottom: 10px; font: 16pt bold arial; border-left: red 5px solid; padding-top: 10px; border-bottom: red 5px solid; background-color: yellow"&gt;This post has been updated. Please read the updated post here:    &lt;blockquote&gt;&lt;a href="http://weblogs.asp.net/stephenwalther/archive/2008/09/09/asp-net-mvc-application-building-forums-4-1-2-validation-revisited.aspx"&gt;Forums #4 1/2 - Validation Revisited&lt;/a&gt; &lt;/blockquote&gt; &lt;/div&gt;  &lt;p&gt;Before reading this blog entry, you should read the previous three blog entries in this series:&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;&lt;a href="http://weblogs.asp.net/stephenwalther/archive/2008/09/05/asp-net-mvc-application-building-forums-1-create-the-perfect-application.aspx"&gt;ASP.NET MVC Application Building: Forums #1 &amp;#8211; Create the Perfect Application&lt;/a&gt; &amp;#8211; In this first entry, I explain the overall goals of the ASP.NET MVC Forums application. I emphasize the importance of Software Design Principles and justify my choice to use test-driven development.&lt;/p&gt;    &lt;p&gt;&lt;a href="http://weblogs.asp.net/stephenwalther/archive/2008/09/05/asp-net-mvc-application-building-forums-2-create-the-first-unit-test.aspx"&gt;ASP.NET MVC Application Building: Forums #2 &amp;#8211; Create the First Unit Test&lt;/a&gt; &amp;#8211; In the second entry, I build the first unit test and create the Index() action which returns a collection of messages.&lt;/p&gt;    &lt;p&gt;&lt;a href="http://weblogs.asp.net/stephenwalther/archive/2008/09/05/asp-net-mvc-application-building-forums-3-post-messages.aspx"&gt;ASP.NET MVC Application Building: Forums #3 &amp;#8211; Post Messages&lt;/a&gt; &amp;#8211; In the third entry, I add the unit tests and functionality required to post new messages and replies.&lt;/p&gt; &lt;/blockquote&gt;  &lt;h4&gt;Creating a Form Validation Framework&lt;/h4&gt;  &lt;p&gt;The ASP.NET MVC framework does not include built-in support for performing form validation. If you want to validate form data before submitting the data to a database then you must write the validation logic yourself. In this section, I describe how you can create a custom form validation framework.&lt;/p&gt;  &lt;p&gt;The validation framework that I describe in this section was developed by Brian Henderson and me way back during the Preview 1 state of the ASP.NET MVC framework. I&amp;#8217;ve updated the original framework that Brian and I developed so that it integrates more smoothly with the new ViewData.ModelState property introduced with the ASP.NET MVC framework Preview 5.&lt;/p&gt;  &lt;p&gt;The validation framework takes advantage of attributes. You decorate the properties that you want to validate with validator attributes. The validation framework includes the following attributes:&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;&amp;#183; RequiredValidator &amp;#8211; Use this attribute to display a validation error message when a property does not have a value.&lt;/p&gt;    &lt;p&gt;&amp;#183; TypeValidator &amp;#8211; Use this attribute to display a validation error message when a property is not the right type.&lt;/p&gt;    &lt;p&gt;&amp;#183; RegularExpressionValidator &amp;#8211; Use this attribute to display a validation error message when a property does not match a regular expression.&lt;/p&gt;    &lt;p&gt;&amp;#183; LengthValidator &amp;#8211; Use this attribute to display a validation error message when the value of an attribute is too long.&lt;/p&gt;    &lt;p&gt;&amp;#183; EmailValidator &amp;#8211; Use this attribute to display a validation error message when a property does not represent a valid email address.&lt;/p&gt;    &lt;p&gt;&amp;#183; USCurrencyValidator &amp;#8211; Use this attribute to display a validation error message when a property does not represent a valid U.S. currency amount.&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;Let&amp;#8217;s look at an example of how you can use these attributes. Imagine that you are creating a product catalog application and you want to validate the form that enables you to enter product information (see Figure 1).&lt;/p&gt;  &lt;p&gt;&lt;b&gt;Figure 1 &amp;#8211; Creating a new product&lt;/b&gt;&lt;/p&gt;  &lt;p&gt;&lt;a href="http://weblogs.asp.net/blogs/stephenwalther/WindowsLiveWriter/ASP.NETMVCApplicationBuildingForums4Serv_2F51/clip_image002_2.jpg"&gt;&lt;img style="border-top-width: 0px; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="514" alt="clip_image002" src="http://weblogs.asp.net/blogs/stephenwalther/WindowsLiveWriter/ASP.NETMVCApplicationBuildingForums4Serv_2F51/clip_image002_thumb.jpg" width="456" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;The view for creating a new product is contained in Listing 1.&lt;/p&gt;  &lt;p&gt;&lt;b&gt;Listing 1 &amp;#8211; Views\Home\Create.aspx&lt;/b&gt;&lt;/p&gt;  &lt;pre class="xml" name="code"&gt;&amp;lt;%@ Page Language=&amp;quot;C#&amp;quot; AutoEventWireup=&amp;quot;true&amp;quot; CodeBehind=&amp;quot;Create.aspx.cs&amp;quot; Inherits=&amp;quot;MvcValidationWebsite.Views.Home.Create&amp;quot; %&amp;gt;
&amp;lt;!DOCTYPE html PUBLIC &amp;quot;-//W3C//DTD XHTML 1.0 Transitional//EN&amp;quot; &amp;quot;http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd&amp;quot;&amp;gt;
&amp;lt;html xmlns=&amp;quot;http://www.w3.org/1999/xhtml&amp;quot; &amp;gt;
&amp;lt;head runat=&amp;quot;server&amp;quot;&amp;gt;
    &amp;lt;title&amp;gt;Create New Product&amp;lt;/title&amp;gt;
    
    &amp;lt;style type=&amp;quot;text/css&amp;quot;&amp;gt;
    
    .input-validation-error
    {
        border: solid 2px red;
    }
    
    &amp;lt;/style&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
    &amp;lt;div&amp;gt;
    
    &amp;lt;form method=&amp;quot;post&amp;quot; action=&amp;quot;/Home/Create&amp;quot;&amp;gt;
    
    &amp;lt;label for=&amp;quot;name&amp;quot;&amp;gt;Product Name:&amp;lt;/label&amp;gt;
    &amp;lt;br /&amp;gt;
    &amp;lt;%= Html.TextBox(&amp;quot;name&amp;quot;) %&amp;gt;
    &amp;lt;%= Html.ValidationMessage(&amp;quot;name&amp;quot;) %&amp;gt;
    
    &amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;
    &amp;lt;label for=&amp;quot;price&amp;quot;&amp;gt;Product Price:&amp;lt;/label&amp;gt;
    &amp;lt;br /&amp;gt;
    &amp;lt;%= Html.TextBox(&amp;quot;price&amp;quot;) %&amp;gt;
    &amp;lt;%= Html.ValidationMessage(&amp;quot;price&amp;quot;) %&amp;gt;

    &amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;
    &amp;lt;label for=&amp;quot;description&amp;quot;&amp;gt;Product Description:&amp;lt;/label&amp;gt;
    &amp;lt;br /&amp;gt;
    &amp;lt;%= Html.TextArea(&amp;quot;description&amp;quot;) %&amp;gt;
    &amp;lt;%= Html.ValidationMessage(&amp;quot;description&amp;quot;) %&amp;gt;

    &amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;
    &amp;lt;label for=&amp;quot;saleStartDate&amp;quot;&amp;gt;Sale Start Date:&amp;lt;/label&amp;gt;
    &amp;lt;br /&amp;gt;
    &amp;lt;%= Html.TextBox(&amp;quot;saleStartDate&amp;quot;) %&amp;gt;
    &amp;lt;%= Html.ValidationMessage(&amp;quot;saleStartDate&amp;quot;) %&amp;gt;

    &amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;
    &amp;lt;label for=&amp;quot;saleEndDate&amp;quot;&amp;gt;Sale End Date:&amp;lt;/label&amp;gt;
    &amp;lt;br /&amp;gt;
    &amp;lt;%= Html.TextBox(&amp;quot;saleEndDate&amp;quot;) %&amp;gt;
    &amp;lt;%= Html.ValidationMessage(&amp;quot;saleEndDate&amp;quot;) %&amp;gt;

    &amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;
    &amp;lt;input type=&amp;quot;submit&amp;quot; value=&amp;quot;Add Product&amp;quot; /&amp;gt;
    
    &amp;lt;/form&amp;gt;
    
    &amp;lt;/div&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;&lt;/pre&gt;

&lt;p&gt;There are two special things that you should notice about the view in Listing 1. First, notice that there is a Html.ValidationMessage() helper associated with each input field. This helper is included in the ASP.NET MVC framework. It displays a validation error message.&lt;/p&gt;

&lt;p&gt;Second, notice that the view includes a cascading style sheet that defines a class named input-validation-error. When the Html.TextBox() helper renders a input field that has an invalid value, this CSS class is added to the input field automatically. In the view in Listing 1, Input fields associated with validation errors get a red border.&lt;/p&gt;

&lt;p&gt;The view for creating a new product is returned from the Home controller. The Home controller is contained in Listing 2.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Listing 2 &amp;#8211; Controllers\HomeController.cs&lt;/b&gt;&lt;/p&gt;

&lt;pre class="c#" name="code"&gt;using System;
using System.Globalization;
using System.Linq;
using System.Web.Mvc;
using LinqToSqlExtensions;
using Microsoft.Web.Mvc;
using MvcFakes;
using MvcValidation;
using MvcValidationWebsite.Models;

namespace MvcValidationWebsite.Controllers
{
    [HandleError]
    public class HomeController : Controller
    {
        private IDataContext _dataContext;
        private ITable&amp;lt;Product&amp;gt; _products;

        public HomeController()
        {
            _dataContext = new DataContextWrapper(&amp;quot;conProductsDB&amp;quot;, &amp;quot;~/Models/ProductsDB.xml&amp;quot;);
            _products = _dataContext.GetTable&amp;lt;Product&amp;gt;();
        }

        public ActionResult Index()
        {
            return View(&amp;quot;Index&amp;quot;, _products.ToList());
        }


        [AcceptVerbs(&amp;quot;GET&amp;quot;)]
        public ActionResult Create()
        {
            return View(&amp;quot;Create&amp;quot;);
        }
        
        [AcceptVerbs(&amp;quot;POST&amp;quot;)]
        public ActionResult Create(FormCollection form)
        {
            // Perform validation
            Validation.Validate&amp;lt;Product&amp;gt;(ViewData.ModelState, form);
            if (!ViewData.ModelState.IsValid)
                return View(&amp;quot;Create&amp;quot;, form);

            // Create product
            var product = new Product();
            product.Name = form[&amp;quot;name&amp;quot;];
            product.Price = Decimal.Parse(form[&amp;quot;price&amp;quot;], NumberStyles.Currency);
            product.Description = form[&amp;quot;description&amp;quot;];
            if (!String.IsNullOrEmpty(form[&amp;quot;saleStartDate&amp;quot;]))
                product.SaleStartDate = DateTime.Parse(form[&amp;quot;saleStartDate&amp;quot;]);
            if (!String.IsNullOrEmpty(form[&amp;quot;saleEndDate&amp;quot;]))          
                product.SaleEndDate = DateTime.Parse(form[&amp;quot;saleEndDate&amp;quot;]);

            // Insert product into database
            _dataContext.Insert(product);

            // Redirect
            return RedirectToAction(&amp;quot;Index&amp;quot;);
        }

    }
}&lt;/pre&gt;

&lt;p&gt;Notice that the Home controller has two Create() action. One Create() action is invoked when performing an HTTP GET operation. In other words, the first Create() action is called when the form for creating a new product is first displayed.&lt;/p&gt;

&lt;p&gt;The second Create() action is invoked only when performing an HTTP POST operation. This Create() action is called when the product form is posted back to the server. This second Create() method validates the form data and, if there are no validation errors, adds the new product to the database.&lt;/p&gt;

&lt;p&gt;Validation is performed with the following three lines of code:&lt;/p&gt;

&lt;pre class="c#:nocontrols" name="code"&gt;Validation.Validate&amp;lt;Product&amp;gt;(ViewData.ModelState, form);
if (!ViewData.ModelState.IsValid)
   return View(&amp;quot;Create&amp;quot;, form);&lt;/pre&gt;

&lt;p&gt;The Validation.Validate() method validates the form parameters passed to the controller action against the Product class. When validation errors are encountered, the errors are added to ModelState. If there are any validation errors, the ModelState.IsValid property returns the value False and the Create view is displayed again. When the Create view is redisplayed, all of the values that the user entered are redisplayed (Notice that the form variable is passed as ViewData to the Create view).&lt;/p&gt;

&lt;p&gt;You specify how form fields get validated in the model (validation is model driven). The Product class is contained in Listing 3.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Listing 3 &amp;#8211; Models\Product.cs&lt;/b&gt;&lt;/p&gt;

&lt;pre class="c#:nocontrols" name="code"&gt;using System;
using MvcValidation;
using System.Web.Mvc;
using Microsoft.Web.Mvc;
using MvcValidationWebsite.Validators;

namespace MvcValidationWebsite.Models
{
    [CustomValidator(typeof(ProductValidator))]
    public class Product
    {
        public int Id { get; set; }

        [RequiredValidator(&amp;quot;Product name is required.&amp;quot;)]
        public string Name { get; set; }

        [RequiredValidator(&amp;quot;Product price is required.&amp;quot;)]
        [USCurrencyValidator(&amp;quot;Product price is not a valid currency amount.&amp;quot;)]
        public decimal Price { get; set; }

        [RequiredValidator(&amp;quot;Product description is required&amp;quot;)]
        [LengthValidator(50, &amp;quot;Description too long.&amp;quot;)]
        public string Description { get; set; }

        [TypeValidator(typeof(DateTime), &amp;quot;Sale start date must be a valid date.&amp;quot;)]
        public DateTime? SaleStartDate { get; set; }

        [TypeValidator(typeof(DateTime), &amp;quot;Sale end date must be a valid date.&amp;quot;)]
        public DateTime? SaleEndDate { get; set; }
    }
}&lt;/pre&gt;

&lt;p&gt;Notice that the validator attributes are applied to the Product properties. For example, the Name property is marked as required with the RequiredValidator attribute. The error message specified by the validator bubbles up to be displayed in the view.&lt;/p&gt;

&lt;p&gt;There is one validator attribute that requires additional explanation. The CustomValidator is applied to the class instead of to a class property. The CustomValidator enables you to perform more complicated types of validation logic that might involve several properties.&lt;/p&gt;

&lt;p&gt;For example, the Product class has both a StartSaleDate and EndSaleDate property that represent the time period when the product is on sale. The EndSaleDate should always be after the StartSaleDate. The CustomValidator in Listing 3 is used to enforce this validation rule.&lt;/p&gt;

&lt;p&gt;Notice that when you add a CustomValidator to a class, you specify a type of object. In Listing 3, the CustomValidator points to a ProductValidator class. This class contains the logic for performing the custom validation. The ProductValidator class is contained in Listing 4.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Listing 4 &amp;#8211; Validators\ProductValidator.cs&lt;/b&gt;&lt;/p&gt;

&lt;pre class="c#:nocontrols" name="code"&gt;using System;
using System.Web.Mvc;
using Microsoft.Web.Mvc;
using MvcValidation;

namespace MvcValidationWebsite.Validators
{
    public class ProductValidator : ICustomValidator
    {
        #region ICustomValidator Members

        public void Validate(ModelStateDictionary modelState, FormCollection form)
        {
            // Don't bother with custom validation when attribute validation failed
            if (modelState.IsValid)
            {
                string strSaleStartDate = form[&amp;quot;saleStartDate&amp;quot;];
                string strSaleEndDate = form[&amp;quot;saleEndDate&amp;quot;];

                // Verify that either both or neither saleStartDate and saleEndDate have values
                if (!String.IsNullOrEmpty(strSaleStartDate) &amp;amp;&amp;amp; String.IsNullOrEmpty(strSaleEndDate))
                    modelState.AddModelError(&amp;quot;saleEndDate&amp;quot;, strSaleEndDate, &amp;quot;sale end date must have a value when sale start date has a value.&amp;quot;);
                if (String.IsNullOrEmpty(strSaleStartDate) &amp;amp;&amp;amp; !String.IsNullOrEmpty(strSaleEndDate))
                    modelState.AddModelError(&amp;quot;saleStartDate&amp;quot;, strSaleStartDate, &amp;quot;sale start date must have a value when sale end date has a value.&amp;quot;);

                // Verify that saleEndDate &amp;gt; saleStartDate
                if (!String.IsNullOrEmpty(strSaleStartDate) &amp;amp;&amp;amp; !String.IsNullOrEmpty(strSaleEndDate))
                {
                    DateTime saleStartDate = DateTime.Parse(strSaleStartDate);
                    DateTime saleEndDate = DateTime.Parse(strSaleEndDate);
                    if (saleEndDate &amp;lt;= saleStartDate)
                    {
                        modelState.AddModelError(&amp;quot;saleStartDate&amp;quot;, strSaleStartDate, &amp;quot;sale start date must be before end date.&amp;quot;);
                        modelState.AddModelError(&amp;quot;saleEndDate&amp;quot;, strSaleEndDate, &amp;quot;sale start date must be before end date.&amp;quot;);
                    }
                }
            }

        }

        #endregion
    }
}&lt;/pre&gt;

&lt;p&gt;Notice that the ProductValidator implements the ICustomValidator interface. This interface has one required method that you must implement named Validate().&lt;/p&gt;

&lt;p&gt;The ProductValidator class in Listing 4 first checks that when a date is supplied for either StartSaleDate or EndSaleDate, a date is supplied for both. It wouldn&amp;#8217;t make much sense to specify a start date for a sale without specifying an end date.&lt;/p&gt;

&lt;p&gt;Next, the ProductValidator confirms that the EndSaleDate is greater than the StartSaleDate. If there is an error, a new error message is added to ModelState to represent the error. Just as long as the view includes an HTML.ValidationMessage() call that includes the validation error key, the validation error message will be displayed. &lt;/p&gt;

&lt;p&gt;You can take advantage of a custom validator to perform any complicated validation task. For example, if you need to perform a database lookup to ensure a unique value then you can perform the database lookup in the custom validator class.&lt;/p&gt;

&lt;h4&gt;Testing the Validation Framework&lt;/h4&gt;

&lt;p&gt;Because the validation framework is used in the Forums application, it needs unit tests just like any other part of the Forums application. At the end of this blog entry, you can download the Visual Studio solution that includes the validators. This solution also includes a test project for the validators. The test project includes 33 tests that verify how the validators work with different form field values (see Figure 2).&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Figure 2 &amp;#8211; Unit tests for the validators&lt;/b&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://weblogs.asp.net/blogs/stephenwalther/WindowsLiveWriter/ASP.NETMVCApplicationBuildingForums4Serv_2F51/clip_image004_2.jpg"&gt;&lt;img style="border-top-width: 0px; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="459" alt="clip_image004" src="http://weblogs.asp.net/blogs/stephenwalther/WindowsLiveWriter/ASP.NETMVCApplicationBuildingForums4Serv_2F51/clip_image004_thumb.jpg" width="628" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For example, the test class in Listing 5 contains all of the unit tests for the LengthValidator. The LengthValidator is tested with an empty string, a string over the maximum length, a string equal to the maximum length, and a string less than the maximum length.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Listing 5 &amp;#8211; LengthValidatorTests.cs&lt;/b&gt;&lt;/p&gt;

&lt;pre class="c#" name="code"&gt;using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using MvcValidation;

namespace MvcValidationTests
{
    [TestClass]
    public class LengthValidatorTests
    {
        [TestMethod]
        public void LengthValidatorEmptyIsValid()
        {
            // Arrange
            var validator = new LengthValidatorAttribute(2);

            // Act
            var result = validator.Validate(String.Empty);

            // Assert
            Assert.IsTrue(result);
        }

        [TestMethod]
        public void LengthValidatorOverMaximum()
        {
            // Arrange
            var validator = new LengthValidatorAttribute(2);

            // Act
            var result = validator.Validate(&amp;quot;abc&amp;quot;);

            // Assert
            Assert.IsFalse(result);

        }

        [TestMethod]
        public void LengthValidatorEqualMaximum()
        {
            // Arrange
            var validator = new LengthValidatorAttribute(2);

            // Act
            var result = validator.Validate(&amp;quot;ab&amp;quot;);

            // Assert
            Assert.IsTrue(result);

        }

        [TestMethod]
        public void LengthValidatorUnderMaximum()
        {
            // Arrange
            var validator = new LengthValidatorAttribute(2);

            // Act
            var result = validator.Validate(&amp;quot;a&amp;quot;);

            // Assert
            Assert.IsTrue(result);

        }
    
    }
}&lt;/pre&gt;

&lt;h4&gt;A Moment of Reflection&lt;/h4&gt;

&lt;p&gt;It might seem strange that we expended so much effort to avoid putting LINQ to SQL attributes on our Model classes and now we are placing validator attributes on the very same classes. Why is it okay to add validator attributes but not okay to add LINQ to SQL attributes?&lt;/p&gt;

&lt;p&gt;There are two important considerations here. First, there is a likelihood that we will need to switch data access technologies in the future. For example, I might need to swap out LINQ to SQL for the Microsoft Entity Framework or even NHibernate. I don&amp;#8217;t want to bake a particular data access technology into our Model classes when I might need to change the data access technology in the future.&lt;/p&gt;

&lt;p&gt;It is unlikely, however, that I will want to change how I handle form validation. I don&amp;#8217;t anticipate changing form validation frameworks in the future. Therefore, adding validator attributes to the Model classes is not locking us into anything that I expect to change.&lt;/p&gt;

&lt;p&gt;There are two (closely related) software design principles that we must consider here: the &lt;b&gt;Single Responsibility Principle&lt;/b&gt; and the &lt;b&gt;Encapsulate What Varies Principle&lt;/b&gt;. Both principles warn us that when we expect software to change, we should do everything we can to isolate the software that might change from the rest of our code. &lt;/p&gt;

&lt;p&gt;If we prefer, we &lt;i&gt;can&lt;/i&gt; isolate the validator attributes from the rest of our code. We could divide the Product class into two classes: the Product class and the ProductValidationModel class. We don&amp;#8217;t need to touch the Product class to add validation to the Product class. We could add all of the validators to a duplicate ProductValidationModel class. When we call the Validation.Validate() method, we can pass the ProductValidationModel class instead of the Product class to this method.&lt;/p&gt;

&lt;p&gt;I&amp;#8217;m not going to divide the Product class into two classes for the Forums application because this smells like &lt;b&gt;Needless Complexity&lt;/b&gt;. I really don&amp;#8217;t expect to change validation frameworks, so the extra effort is not worth it. &lt;/p&gt;

&lt;h4&gt;Adding Form Validation to the Forums Application&lt;/h4&gt;

&lt;p&gt;Now that we have a simple validation framework, we can modify the Forums application to take advantage of it. To begin with, we want to make sure that a user cannot post a new message to the forum without entering a message subject and a message body (see Figure 3).&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Figure 3 &amp;#8211; Validating a forum post&lt;/b&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://weblogs.asp.net/blogs/stephenwalther/WindowsLiveWriter/ASP.NETMVCApplicationBuildingForums4Serv_2F51/clip_image006_2.jpg"&gt;&lt;img style="border-top-width: 0px; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="311" alt="clip_image006" src="http://weblogs.asp.net/blogs/stephenwalther/WindowsLiveWriter/ASP.NETMVCApplicationBuildingForums4Serv_2F51/clip_image006_thumb.jpg" width="546" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Therefore, we need to create unit tests that express this intention. The two new unit tests are contained in Listing 6. The first unit test verifies that validation fails when the subject is set to an empty value. The second unit test verifies that validation fails when the body does not have a value.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Listing 6 &amp;#8211; Controllers\ForumControllerTest.cs&lt;/b&gt;&lt;/p&gt;

&lt;pre class="c#" name="code"&gt;[TestMethod]
public void EmptySubjectFailsValidation()
{
    // Arrange
    var controller = new ForumController(_repository);

    // Act
    var form = new FormCollection();
    form.Add(&amp;quot;author&amp;quot;, &amp;quot;Stephen&amp;quot;);
    form.Add(&amp;quot;subject&amp;quot;, String.Empty);
    form.Add(&amp;quot;body&amp;quot;, &amp;quot;Body of new thread&amp;quot;);
    var result = (ViewResult)controller.Create(form);

    // Assert
    Assert.IsFalse(result.ViewData.ModelState.IsValid);  
}


[TestMethod]
public void EmptyBodyFailsValidation()
{
    // Arrange
    var controller = new ForumController(_repository);

    // Act
    var form = new FormCollection();
    form.Add(&amp;quot;author&amp;quot;, &amp;quot;Stephen&amp;quot;);
    form.Add(&amp;quot;subject&amp;quot;, &amp;quot;New Message&amp;quot;);
    form.Add(&amp;quot;body&amp;quot;, String.Empty);
    var result = (ViewResult)controller.Create(form);

    // Assert
    Assert.IsFalse(result.ViewData.ModelState.IsValid);
}&lt;/pre&gt;

&lt;p&gt;In order to pass these new unit tests, we need to modify our Forum controller. The new version of the Forum controller is contained in Listing 7.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Listing 7 &amp;#8211; Controllers\ForumController.cs&lt;/b&gt;&lt;/p&gt;

&lt;pre class="c#" name="code"&gt;using System;
using System.Web.Mvc;
using MvcForums.Models;
using Microsoft.Web.Mvc;
using MvcValidation;

namespace MvcForums.Controllers
{
    public class ForumController : Controller
    {
        private IForumRepository _repository;

        public ForumController()
            : this(new ForumRepository())
        { }

        public ForumController(IForumRepository repository)
        {
            _repository = repository;
        }

        public ActionResult Index()
        {
            ViewData.Model = _repository.SelectThreads();
            return View(&amp;quot;Index&amp;quot;);
        }

        [AcceptVerbs(&amp;quot;GET&amp;quot;)]
        public ActionResult Create()
        {
            return View(&amp;quot;Create&amp;quot;);
        }

        [AcceptVerbs(&amp;quot;Post&amp;quot;)]
        public ActionResult Create(FormCollection form)
        {
            // Validate
            Validation.Validate&amp;lt;Message&amp;gt;(ViewData.ModelState, form);
            if (!ViewData.ModelState.IsValid)
                return View(&amp;quot;Create&amp;quot;, form);

            // Create new message
            var messageToCreate = new Message();
            if (!String.IsNullOrEmpty(form[&amp;quot;parentThreadId&amp;quot;]))
                messageToCreate.ParentThreadId = int.Parse(form[&amp;quot;parentThreadId&amp;quot;]);
            if (!String.IsNullOrEmpty(form[&amp;quot;parentMessageId&amp;quot;]))
                messageToCreate.ParentMessageId =  int.Parse(form[&amp;quot;parentMessageId&amp;quot;]);
            messageToCreate.Author = form[&amp;quot;author&amp;quot;];
            messageToCreate.Subject = form[&amp;quot;subject&amp;quot;];
            messageToCreate.Body = form[&amp;quot;body&amp;quot;];
            messageToCreate.EntryDate = DateTime.Now;

            // Add to database
            _repository.AddMessage(messageToCreate);

            // Redirect
            return RedirectToAction(&amp;quot;Index&amp;quot;);
        }

        public ActionResult Thread(int threadId)
        {
            ViewData.Model = _repository.SelectMessages(threadId);
            return View(&amp;quot;Thread&amp;quot;);
        }

    }
}&lt;/pre&gt;

&lt;p&gt;Notice that the Forum controller now has two Create() methods. The first Create() method displays the form for creating a new forum message. The second Create() method is called when the form data is posted to the server.&lt;/p&gt;

&lt;p&gt;This second Create() method takes advantage of the Validation class to validate the form data. If validation fails, the Create view is redisplayed.&lt;/p&gt;

&lt;p&gt;The final modification that we need to make is to the Message class. The modified Message class is contained in Listing 8.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Listing 8 &amp;#8211; Models\Message.cs&lt;/b&gt;&lt;/p&gt;

&lt;pre class="c#" name="code"&gt;using System;
using MvcValidation;

namespace MvcForums.Models
{
    public class Message
    {
        public Message()
        { }

        public Message(int id, int? parentThreadId, int? parentMessageId, string author, string subject, string body)
        {
            this.Id = id;
            this.ParentThreadId = parentThreadId;
            this.ParentMessageId = parentMessageId;
            this.Author = author;
            this.Subject = subject;
            this.Body = body;
        }

        public int Id { get; set; }
        public int? ParentThreadId { get; set; }
        public int? ParentMessageId { get; set; }
        public string Author { get; set; }
        
        [RequiredValidator(&amp;quot;You must enter a message subject.&amp;quot;)]
        public string Subject { get; set; }
        
        [RequiredValidator(&amp;quot;You must enter a message body.&amp;quot;)]
        public string Body { get; set; }
        
        public DateTime EntryDate { get; set; }
    }
}&lt;/pre&gt;

&lt;p&gt;Two RequiredValidator attributes have been added to the Message class in Listing 8. No other changes have been made to the class.&lt;/p&gt;

&lt;p&gt;After all of these modifications have been made to the Forums application, the new validation unit tests pass (see Figure 4). We have successfully added validation to the Forums application.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Figure 4 &amp;#8211; Forums validation tests pass&lt;/b&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://weblogs.asp.net/blogs/stephenwalther/WindowsLiveWriter/ASP.NETMVCApplicationBuildingForums4Serv_2F51/clip_image008_2.jpg"&gt;&lt;img style="border-top-width: 0px; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="304" alt="clip_image008" src="http://weblogs.asp.net/blogs/stephenwalther/WindowsLiveWriter/ASP.NETMVCApplicationBuildingForums4Serv_2F51/clip_image008_thumb.jpg" width="513" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;Summary&lt;/h4&gt;

&lt;p&gt;In this blog entry, we&amp;#8217;ve added support for basic form validation to the Forums application. We created a set of validation attributes that we can apply to our model classes. In this blog entry, we implemented server-side validation. In the next blog entry, I want to tackle the problem of client-side validation.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://weblogs.asp.net/blogs/stephenwalther/Downloads/AppBuildingForums4/AppForums4.zip"&gt;&lt;strong&gt;Download the Code&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://weblogs.asp.net/aggbug.aspx?PostID=6608776" width="1" height="1"&gt;</description><category domain="http://weblogs.asp.net/stephenwalther/archive/tags/ASP.NET+MVC/default.aspx">ASP.NET MVC</category><category domain="http://weblogs.asp.net/stephenwalther/archive/tags/ASP.NET/default.aspx">ASP.NET</category><category domain="http://weblogs.asp.net/stephenwalther/archive/tags/Application+Building/default.aspx">Application Building</category></item></channel></rss>