Maximum size for the query string

2006-10-13: Although the specification of the HTTP protocol does not specify any maximum length, practical limits are imposed by web browser and server software.
Microsoft Internet Explorer (Browser)
Microsoft states that the maximum length of a URL in Internet Explorer is 2,083 characters, with no more than 2,048 characters in the path portion of the URL. In my tests, attempts to use URLs longer than this produced a clear error message in Internet Explorer.
Firefox (Browser)
After 65,536 characters, the location bar no longer displays the URL in Windows Firefox 1.5.x. However, longer URLs will work. I stopped testing after 100,000 characters.
Safari (Browser)
At least 80,000 characters will work. I stopped testing after 80,000 characters.
Opera (Browser)
At least 190,000 characters will work. I stopped testing after 190,000 characters. Opera 9 for Windows continued to display a fully editable, copyable and pasteable URL in the location bar even at 190,000 characters.
Apache (Server)
My early attempts to measure the maximum URL length in web browsers bumped into a server URL length limit of approximately 4,000 characters, after which Apache produces a "413 Entity Too Large" error. I used the current up to date Apache build found in Red Hat Enterprise Linux 4. The official Apache documentation only mentions an 8,192-byte limit on an individual field in a request.
Microsoft Internet Information Server (Server)
The default limit is 16,384 characters (yes, Microsoft's web server accepts longer URLs than Microsoft's web browser). This is configurable.

Working with hierarchical data in SQL Server databases

Working with hierarchical data in SQL Server databases
Note: Information & code samples from this article are tested on SQL Server 2005 RTM (Yukon) and found to be working. Will update the article in case of any compatibility issues.

In this article, I will show you a simple example that traverses a hierarchy and displays the output in indented format. I will also provide you with links and pointers for more advanced and in-depth information on processing hierarchies.

Representing data in the form of a hierarchy or a tree is a common requirement. The most common example I can think of, is an organizational chart that contains all the employees in a given organization and each employee is linked to his/her manager by his/her manager's ID. Since managers are also employees, their details also get stored in the same employees table.

A few more examples:

- Implementation of a permissions hierarchy.

- Implementation of a product catalog for an online store.

All the above examples need data to be stored in the form of hierarchies or trees (also loosely referred to as parent child relationships). But SQL Server is a relational database, not a hierarchical database. So, we have to store the data in normalized, relational tables, but come up with programming techniques to process this data in a hierarchical manner.

Unfortunately, there is no built-in support for processing hierarchies and tree structures in SQL Server's T-SQL implementation. (Oracle's PL/SQL implementation has a CONNECT BY syntax that makes it easier to work with hierarchical data. Let's hope we will see something similar in the next version of SQL Server, Yukon).

C programmers use recursive programming techniques for traversing tree structures. The same can be implemented in T-SQL using recursive stored procedure calls. I will demonstrate this in the following example.

Consider the employee table of an organization, that stores all the employee records. Each employee is linked to his/her manager by a manger ID. This table can be represented using the following CREATE TABLE statement:


CREATE TABLE dbo.Emp
(
 EmpID  int PRIMARY KEY,
 EmpName  varchar(30),
 MgrID  int FOREIGN KEY REFERENCES Emp(EmpID)
)
GO

Notice that, EmpID is decalred as a primary key, and the MgrID column is declared as a foreign key constraint, that references the EmpID column of the same table, that is, a self referencing table. This is so, because all employees and managers are stored in the same table.

Since EmpID is declared as a primary key, by default it will be implemented as a unique clustered index. Now, let's create a non-clustered index on MgrID column, to improve the query performance:

CREATE NONCLUSTERED INDEX NC_NU_Emp_MgrID ON dbo.Emp(MgrID)
GO

Let's populate the employee table with some data:

INSERT dbo.Emp SELECT 1, 'President', NULL
INSERT dbo.Emp SELECT 2, 'Vice President', 1
INSERT dbo.Emp SELECT 3, 'CEO', 2
INSERT dbo.Emp SELECT 4, 'CTO', 2
INSERT dbo.Emp SELECT 5, 'Group Project Manager', 4
INSERT dbo.Emp SELECT 6, 'Project Manager 1', 5
INSERT dbo.Emp SELECT 7, 'Project Manager 2', 5
INSERT dbo.Emp SELECT 8, 'Team Leader 1', 6
INSERT dbo.Emp SELECT 9, 'Software Engineer 1', 8
INSERT dbo.Emp SELECT 10, 'Software Engineer 2', 8
INSERT dbo.Emp SELECT 11, 'Test Lead 1', 6
INSERT dbo.Emp SELECT 12, 'Tester 1', 11
INSERT dbo.Emp SELECT 13, 'Tester 2', 11
INSERT dbo.Emp SELECT 14, 'Team Leader 2', 7
INSERT dbo.Emp SELECT 15, 'Software Engineer 3', 14
INSERT dbo.Emp SELECT 16, 'Software Engineer 4', 14
INSERT dbo.Emp SELECT 17, 'Test Lead 2', 7
INSERT dbo.Emp SELECT 18, 'Tester 3', 17
INSERT dbo.Emp SELECT 19, 'Tester 4', 17
INSERT dbo.Emp SELECT 20, 'Tester 5', 17
GO

Notice that the MgrID of President is NULL, that means, he is the super boss and has nobody managing him. As you can see, rest of the employees are linked to their respective managers using the MgrID column.

Now, let's create a stored procedure, that traverses this employee hierarchy recursively and displays the employees in the form of an indented tree structure. The following stored procedure has an input parameter named Root, that takes the ID of the employee (equivalent to the ID of a node in a tree) and displays all the employees managed by him and his sub-ordinates.

CREATE PROC dbo.ShowHierarchy
(
 @Root int
)
AS
BEGIN
 SET NOCOUNT ON
 DECLARE @EmpID int, @EmpName varchar(30)

 SET @EmpName = (SELECT EmpName FROM dbo.Emp WHERE EmpID = @Root)
 PRINT REPLICATE('-', @@NESTLEVEL * 4) + @EmpName

 SET @EmpID = (SELECT MIN(EmpID) FROM dbo.Emp WHERE MgrID = @Root)

 WHILE @EmpID IS NOT NULL
 BEGIN
  EXEC dbo.ShowHierarchy @EmpID
  SET @EmpID = (SELECT MIN(EmpID) FROM dbo.Emp WHERE MgrID = @Root AND EmpID > @EmpID)
 END
END
GO

While creating the above stored procedure, you will receive the following warning:

Cannot add rows to sysdepends for the current stored procedure because it depends on the missing object 'ShowHierarchy'. The stored procedure will still be created.

Nothing to worry about. It's a just a warning, as SQL Server is a little confused about the recursive stored procedures.

As you can see, the above stored procedure calls itself recursively. You should be aware of a limitation imposed by SQL Server though. A stored procedure can nest itself upto a maximum of 32 levels. If you exceed this limit, you will receive the following error:

Server: Msg 217, Level 16, State 1, Procedure sdss, Line 1
Maximum stored procedure nesting level exceeded (limit 32).

Now let's run this procedure by passing the IDs of different employees and look at the output:
--Complete hierarchy

EXEC dbo.ShowHierarchy 1
GO


---President
------Vice President
---------CEO
---------CTO
------------Group Project Manager
---------------Project Manager 1
------------------Team Leader 1
---------------------Software Engineer 1
---------------------Software Engineer 2
------------------Test Lead 1
---------------------Tester 1
---------------------Tester 2
---------------Project Manager 2
------------------Team Leader 2
---------------------Software Engineer 3
---------------------Software Engineer 4
------------------Test Lead 2
---------------------Tester 3
---------------------Tester 4
---------------------Tester 5



--From Group Project Manager onwards

EXEC dbo.ShowHierarchy 5
GO


---Group Project Manager
------Project Manager 1
---------Team Leader 1
------------Software Engineer 1
------------Software Engineer 2
---------Test Lead 1
------------Tester 1
------------Tester 2
------Project Manager 2
---------Team Leader 2
------------Software Engineer 3
------------Software Engineer 4
---------Test Lead 2
------------Tester 3
------------Tester 4
------------Tester 5



--From Project Manager 1 onwards

EXEC dbo.ShowHierarchy 6
GO


---Project Manager 1
------Team Leader 1
---------Software Engineer 1
---------Software Engineer 2
------Test Lead 1
---------Tester 1
---------Tester 2

The above is just a simple example of traversing hierarchies. There's more to hierarchies than just traversing, that is, adding to, deleting from and modifying hierarchies. The following links deal with hierarchies in greater detail and propose different methodologies for managing hierarchies efficiently.

SQL Server recursion - Parent child relations

I'm sure that many of the experienced database developers in here already know about this technique, but for the rest of you I though it would be nice to have a tutorial on how you can query multi-level parent-child data, both one to many and many to many relationsships.

Examples of a one to many multi-level structure are for example organisations structures or a threaded discussion forum (like VBForums).

A many to many structure can be a Bill of Material (BOM). A BOM can have many items and an item can be part of many BOMs.

The problem with these kind of relations is how write a query that will search and return the whole tree for a given record, e.g. the whole subtree (or x levels) of a BOM or all replies to a post in a discussion forum.

I will start with an example where we look at an employee structure. An employee has one manager, and a manager can have many employees. We create a table Employee with Employee_Id, First_Name, Last_Name, and Manager_Id.


Code:
CREATE TABLE Employee (
 Employee_Id int IDENTITY (1, 1) NOT NULL PRIMARY KEY CLUSTERED,
 First_Name varchar(50) NOT NULL,
 Last_Name varchar(50) NOT NULL,
 Manager_Id int NULL constraint [FK_Manager_Employee] FOREIGN KEY
  (Manager_Id) REFERENCES Employee(Employee_Id)
) ON [PRIMARY]
GO
-- We also create an index on Manager_Id to speed things up a little bit.
CREATE NONCLUSTERED INDEX IX_Manager ON Employee
 (
 Manager_Id
 ) ON [PRIMARY]
GO


To make it even more understandable we will populate the table with "employees" from VBForums database section, and I hope nobody gets offended by this chart.



The following TSQL will insert this structure into the Employee table:

Code:
insert into Employee(First_Name, Last_Name, Manager_Id) values('si_the_geek','NA',NULL)
insert into Employee(First_Name, Last_Name, Manager_Id) values('mendhak','NA',1)
insert into Employee(First_Name, Last_Name, Manager_Id) values('szlamany','NA',1)
insert into Employee(First_Name, Last_Name, Manager_Id) values('vb_dba','NA',1)
insert into Employee(First_Name, Last_Name, Manager_Id) values('techgnome','NA',3)
insert into Employee(First_Name, Last_Name, Manager_Id) values('hack','NA',3)
insert into Employee(First_Name, Last_Name, Manager_Id) values('kaffenils','NA',5)
insert into Employee(First_Name, Last_Name, Manager_Id) values('dee-u','NA',5)
We have now the created the Employee table and populated it with test data. As you can see, the Employee structure consists of four levels.

The next step is to create a function or stored procedure that can take an Employee_Id as parameter and list all sub-levels in the employee hierarcy. This means that if we execute the query with Employee_Id=1 (si_the_geek), we will get all the records in the table returned.

The script that does the magic is here. Comments in the script explains how it works.

-- Update the first level's Path and Employee_Id_Path. Employee_Id_Path will be used to prevent the recursion from
-- going into an endless loop if for example si_the_geek is defined as both parent and child to mendhak.
update @tree set Path=str(TreeId,10,0) + '.', Employee_Id_Path=cast(Employee_Id as varchar(10)) + '\'

while @rowcount>0
begin
 -- Increase level by one.
 set @lvl=@lvl+1 

 -- This line inserts all records from the Employee table that has the previous level's
 -- employee_Id as Manager_Id. In other words, it inserts all manager's staff from the previous
 -- execution of the loop.
 insert into @tree(Employee_Id, lvl, ParentTreeId)
  select e.Employee_Id, @lvl, t.TreeId
  from Employee e inner join @tree t on e.Manager_Id=t.Employee_Id and t.lvl=@lvl-1

 -- Get number of rows affected by the previous insert. If no records were affected by the last
 -- statement, then we know that we have reached the bottom of the hierarcy.
 set @rowcount=@@ROWCOUNT
 
 -- The following SQL updates the newly inserted records' Path with the
 -- the new TreeId + previous levels TreeId. The path is used later to sort
 -- the result in a treeview look-a-like way. We also update Employee_Id_Path
 -- with the Employee_Id and previous level's Employee_Id_Path. The Employee_Id_Path
 -- is used to detect endless loops and therefore we only update Employee_Id_Path
 -- where parent Employee_Id_Path does not contain current Employee_Id. This is
 -- perhaps a little bit difficult to understand the first time you read the code.
 update t1 set t1.Path=t2.Path + str(t1.TreeId,10,0)+'.',
  t1.Employee_Id_Path=t2.Employee_Id_Path + cast(t1.Employee_Id as varchar(10))+'\'
  from @tree t1 inner join @tree t2 on t1.ParentTreeId=t2.TreeId
  where t1.lvl=@lvl and t2.Employee_Id_Path not like '%' + cast(t1.Employee_Id as varchar(10)) + '\%'
 
 -- This statement deletes andy records where Employee_Id_Path is NULL. In other
 -- words: We delete records that will cause an endless loop.
 delete from @tree where Employee_Id_Path is null
 
 -- We then get the number of records deleted...
 set @delcount=@@ROWCOUNT

 -- ... and substract this number from the records appended. If @rowcount=0
 -- then the WHILE loop will exit.
 set @rowcount=@rowcount-@delcount
end

-- Now we can choose to display the results in what way we want to. Path can, as we said before,
-- be used to sort the result in a treeview way.
select replicate(' | ',lvl)+cast(t.Employee_Id as varchar(10)) as tree_level,e.First_Name,lvl
from @tree t inner join Employee e on t.Employee_Id=e.Employee_Id order by Path


GO


 You can use this script in a stored procedure or a UDF. Using the script in a UDF will also give you the possibility to use the result as a recordset in other SELECT statements. Read here to see how the UDF header must be defined to act as a recordset.

Execute the script in QA and you will get the following result. As you can see, this is a treeview representation of the hierarcy displayed in the picture above.



This example demonstrates the basic principle, but you can make it much more advanced if you like to. We have used it to return a distinct list of all items in a BOM and the quantity of each item.

Enjoy, and feel free to add questions or comments.

Creating a MVC 3 Application with Razor and Unobtrusive JavaScript

The User List sample web application demonstrates how simple it is to create ASP.NET MVC  3  applications using the Razor view engine. The sample application shows how to use the new Razor view engine with ASP.NET MVC version 3 and Visual Studio 2010 to create a fictional  User List website that includes functionality such as creating, displaying, editing, and deleting users.
This tutorial describes the steps that were taken in order to build the User List sample ASP.NET MVC 3 application.  A Visual Studio project with C# and VB source code is available to accompany this topic: Download. If you have questions about this tutorial, please post them to the MVC forum.

Overview

The application you’ll be building is a simple user list website. Users can enter, view, and update user information.
Sample site
You can download the VB and C# completed project here.

Creating the Web Application

To start the tutorial, open Visual Studio 2010 and create a new project using the ASP.NET MVC 3 Web Application template. Name the application "Mvc3Razor".
New MVC 3 project
In the New ASP.NET MVC 3 Project dialog, select Internet Application, select the Razor view engine, and then click OK.
New ASP.NET MVC 3 Project dialog
In this tutorial you will not be using the ASP.NET membership provider, so you can delete all the files associated with logon and membership. In Solution Explorer, remove the following files and directories:
  • Controllers\AccountController
  • Models\AccountModels
  • Views\Shared\_LogOnPartial
  • Views\Account (and all the files in this directory)
Soln Exp
Edit the _Layout.cshtml file and replace the markup inside the <div> element named logindisplay with the message "Login Disabled". The following example shows the new markup:
 <div id="logindisplay">
  Login Disabled</div>

Adding the Model

In Solution Explorer, right-click the Models folder, select Add, and then click Class.
New User Mdl class
Name the class UserModel. Replace the contents of the UserModel file with the following code:
using System.ComponentModel.DataAnnotations;
using System.Collections.Generic;
namespace Mvc3Razor.Models {
    public class UserModel {

        [Required]
        [StringLength(6, MinimumLength = 3)]
        [Display(Name = "User Name")]
        [RegularExpression(@"(\S)+", ErrorMessage = "White space is not allowed")]
        [ScaffoldColumn(false)]
        public string UserName { get; set; }

        [Required]
        [StringLength(8, MinimumLength = 3)]
        [Display(Name = "First Name")]
        public string FirstName { get; set; }
        [Required]
        [StringLength(9, MinimumLength = 2)]
        [Display(Name = "Last Name")]
        public string LastName { get; set; }
        [Required()]
        public string City { get; set; }

    }

    public class Users {

        public Users() {
            _usrList.Add(new UserModel
            {
                UserName = "BenM",
                FirstName = "Ben",
                LastName = "Miller",
                City = "Seattle"
            });
            _usrList.Add(new UserModel
            {
                UserName = "AnnB",
                FirstName = "Ann",
                LastName = "Beebe",
                City = "Boston"
            });
        }

        public List<UserModel> _usrList = new List<UserModel>();

        public void Update(UserModel umToUpdate) {

            foreach (UserModel um in _usrList) {
                if (um.UserName == umToUpdate.UserName) {
                    _usrList.Remove(um);
                    _usrList.Add(umToUpdate);
                    break;
                }
            }
        }

        public void Create(UserModel umToUpdate) {
            foreach (UserModel um in _usrList) {
                if (um.UserName == umToUpdate.UserName) {
                    throw new System.InvalidOperationException("Duplicat username: " + um.UserName);
                }
            }
            _usrList.Add(umToUpdate);
        }

        public void Remove(string usrName) {

            foreach (UserModel um in _usrList) {
                if (um.UserName == usrName) {
                    _usrList.Remove(um);
                    break;
                }
            }
        }

        public  UserModel GetUser(string uid) {
            UserModel usrMdl = null;

            foreach (UserModel um in _usrList)
                if (um.UserName == uid)
                    usrMdl = um;

            return usrMdl;
        }

    }    }
The UserModel class represents users. Each member of the class is annotated with the Required attribute from the DataAnnotations namespace. The attributes in the DataAnnotations namespace provide automatic client- and server-side validation for web applications.
Open the HomeController class and add a using directive so that you can access the UserModel and Users classes:
using Mvc3Razor.Models; 
Just after the HomeController declaration, add the following comment and the reference to a Users class:
public class HomeController : Controller {

        // The __usrs class is replacement for a real data access strategy.
        private static Users _usrs = new Users();
The Users class is a simplified, in-memory data store that you'll use in this tutorial. In a real application you would use a database to store user information. The first few lines of the HomeController file are shown in the following example:
using System.Web.Mvc;
using Mvc3Razor.Models;
namespace Mvc3Razor.Controllers {
       
    public class HomeController : Controller {

        // The __usrs class is replacement for a real data access strategy.
        private static Users _usrs = new Users();
Build the application so that the user model will be available to the scaffolding wizard in the next step.

Creating the Default View

The next step is to add an action method and view to display the users.
Delete the existing Views\Home\Index file. You will create a new Index file to display the users.
In the HomeController class, replace the contents of the Index method with the following code:
return View(_usrs._usrList); 
Right-click inside the Index method and then click Add View.
Add View
Select the Create a strongly-typed view option. For View data class, select Mvc3Razor.Models.UserModel. (If you don't see Mvc3Razor.Models.UserModel in the View data class box, you need to build the project.) Make sure that the view engine is set to Razor. Set View content to List and then click Add.
Add Index View
The new view automatically scaffolds the user data that's passed to the Index view. Examine the newly generated Views\Home\Index file. The Create New, Edit, Details, and Delete links don't work, but the rest of the page is functional. Run the page. You see a list of users.
Index Page
Open the Index.cshtml file and replace the ActionLink markup for Edit, Details, and Delete with the following code:
@Html.ActionLink("Edit", "Edit", new {  id=item.UserName  }) |
@Html.ActionLink("Details", "Details", new {  id=item.UserName  }) |
@Html.ActionLink("Delete", "Delete", new {  id=item.UserName  })         
The user name is used as the ID to find the selected record in the Edit, Details, and Delete links.

Creating the Details View

The next step is to add a Details action method and view in order to display user details.
Details
Add the following Details method to the home controller:
public ViewResult Details(string id) {
    return View(_usrs.GetUser(id));
}
Right-click inside the Details method and then select Add View. Verify that the View data class box contains Mvc3Razor.Models.UserModel. Set View content to Details and then click Add.
Add details view
Run the application and select a details link. The automatic scaffolding shows each property in the model.
Details

Creating the Edit View

Add the following Edit method to the home controller.
public ViewResult Edit(string id) {
    return View(_usrs.GetUser(id));
}
[HttpPost]
public ViewResult Edit(UserModel um) {

    if (!TryUpdateModel(um)) {
        ViewBag.updateError = "Update Failure";
        return View(um);
    }

    // ToDo: add persistent to DB.
    _usrs.Update(um);
    return View("Details", um);
}
Add a view as in the previous steps, but set View content to Edit.
Add Edit view
Run the application and edit the first and last name of one of the users. If you violate any DataAnnotation constraints that have been applied to the UserModel class, when you submit the form, you will see validation errors that are produced by server code. For example, if you change the first name "Ann" to "A", when you submit the form, the following error is displayed on the form:
The field First Name must be a string with a minimum length of 3 and a maximum length of 8.
In this tutorial, you're treating the user name as the primary key. Therefore, the user name property cannot be changed. In the Edit.cshtml file, just after the Html.BeginForm statement, set the user name to be a hidden field. This causes the property to be passed in the model. The following code fragment shows the placement of the Hidden statement:
<h2>Edit</h2>
    @using (Html.BeginForm()) {
        @Html.Hidden("UserName", Model.UserName)
Replace the TextBoxFor and ValidationMessageFor markup for the user name with a DisplayFor call. The DisplayFor method displays the property as a read-only element. The following example shows the completed markup. The original TextBoxFor and ValidationMessageFor calls are commented out with the Razor begin-comment and end-comment characters (@* *@)
<div class="editor-label">
  @Html.LabelFor(model => model.UserName)</div>
<div class="editor-field">
@*
  @Html.TextBoxFor(model => model.UserName)
  @Html.ValidationMessageFor(model => model.UserName)
*@
@Html.DisplayFor(model => model.UserName)</div>

Enabling Client-Side Validation

To enable client-side validation in ASP.NET MVC 3, you must set two flags and you must include three JavaScript files.
Open the application's Web.config file. Verify that ClientValidationEnabled and UnobtrusiveJavaScriptEnabled are set to true in the application settings. The following fragment from the root Web.config file shows the correct settings:
 <appSettings>
  <add key="ClientValidationEnabled" value="true"/> 
  <add key="UnobtrusiveJavaScriptEnabled" value="true"/> 
 </appSettings>
Setting UnobtrusiveJavaScriptEnabled to true enables unobtrusive Ajax and unobtrusive client validation. When you use unobtrusive validation, the validation rules are turned into HTML5 attributes. HTML5 attribute names can consist of only lowercase letters, numbers, and dashes.
Setting ClientValidationEnabled to true enables client-side validation. By setting these keys in the application Web.config file, you enable client validation and unobtrusive JavaScript for the entire application. You can also enable or disable these settings in individual views or in controller methods using the following code:
HtmlHelper.ClientValidationEnabled = true; HtmlHelper.UnobtrusiveJavaScriptEnabled = true; 
You also need to include several JavaScript files in the rendered view. An easy way to include the JavaScript in all views is to add them to the Views\Shared\_Layout.cshtml file. Replace the <head> element of the _Layout.cshtml file with the following code:
<head>
  <title>@View.Title</title>
  <link href="@Url.Content("~/Content/Site.css")" rel="stylesheet" type="text/css" />
  <script src="http://ajax.microsoft.com/ajax/jQuery/jquery-1.4.2.min.js"></script>
  <script src="http://ajax.microsoft.com/ajax/jquery.validate/1.7/jquery.validate.min.js"></script>
  <script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>
</head>
The first two jQuery scripts are hosted by the Microsoft Ajax Content Delivery Network (CDN). By taking advantage of the Microsoft Ajax CDN, you can significantly improve the first-hit performance of your applications.
Run the application and click an edit link. View the page's source in the browser. The browser source shows many attributes of the form data-val (for data validation). When client validation and unobtrusive JavaScript is enabled, input fields with a client-validation rule contain the data-val="true" attribute to trigger unobtrusive client validation. For example, the City field in the model was decorated with the Required attribute, which results in the HTML shown in the following example:
<div class="editor-field">
  <input data-val="true" data-val-required="The City field is required." id="City" name="City" type="text" value="Seattle" />
  <span class="field-validation-valid" data-valmsg-for="City" data-valmsg-replace="true"></span>
</div>
For each client-validation rule, an attribute is added that has the form data-val-rulename="message". Using the City field example shown earlier, the required client-validation rule generates the data-val-required attribute and the message "The City field is required". Run the application, edit one of the users, and clear the City field. When you tab out of the field, you see a client-side validation error message.
City required
Similarly, for each parameter in the client-validation rule, an attribute is added that has the form data-val-rulename-paramname=paramvalue. For example, the FirstName property is annotated with the StringLength attribute and specifies a minimum length of 3 and a maximum length of 8. The data validation rule named length has the parameter name max and the parameter value 8. The following shows the HTML that is generated for the FirstName field when you edit one of the users:
<input data-val="true"         
       data-val-length="The field First Name must be a string with a minimum length of 3 and a maximum length of 8." 
       data-val-length-max="8" 
       data-val-length-min="3" 
       data-val-required="The First Name field is required." 
       id="FirstName" 
       name="FirstName" 
       type="text" 
       value="Ben" /> 
For more information about unobtrusive client validation, see the entry Unobtrusive Client Validation in ASP.NET MVC 3 in Brad Wilson's blog.
Note In ASP.NET MVC 3 Beta, you sometimes need to submit the form in order to start client-side validation. This might be changed for the final release.

Creating the Create View

The next step is to add a Create action method and view in order to enable the user to create a new user. Add the following Create method to the home controller:
 public ViewResult Create() {
            return View(new UserModel());
        }

        [HttpPost]
        public ViewResult Create(UserModel um) {

            if (!TryUpdateModel(um)) {
                ViewBag.updateError = "Create Failure";
                return View(um);
            }

            // ToDo: add persistent to DB.
            _usrs.Create(um);
            return View("Details", um);
        }
Add a view as in the previous steps, but set View content to Create.
Create View
Run the application, select the Create link, and add a new user. The Create method automatically takes advantage of client-side and server-side validation. Try to enter a user name that contains white space, such as "Ben X". When you tab out of the user name field, a client-side validation error (White space is not allowed) is displayed.

Add the Delete method

To complete the tutorial, add the following Delete method to the home controller:
public ViewResult Delete(string id) {
            return View(_usrs.GetUser(id));
        }

        [HttpPost]
        public RedirectToRouteResult Delete(string id, FormCollection collection) {
            _usrs.Remove(id);
            return RedirectToAction("Index");
        }
Add a Delete view as in the previous steps, setting View content to Delete.
Delete View

Understanding Models, Views, and Controllers (C#)

This tutorial provides you with a high-level overview of ASP.NET MVC models, views, and controllers. In other words, it explains the M', V', and C' in ASP.NET MVC.
After reading this tutorial, you should understand how the different parts of an ASP.NET MVC application work together. You should also understand how the architecture of an ASP.NET MVC application differs from an ASP.NET Web Forms application or Active Server Pages application.

The Sample ASP.NET MVC Application

The default Visual Studio template for creating ASP.NET MVC Web Applications includes an extremely simple sample application that can be used to understand the different parts of an ASP.NET MVC application. We take advantage of this simple application in this tutorial.
You create a new ASP.NET MVC application with the MVC template by launching Visual Studio 2008 and selecting the menu option File, New Project (see Figure 1). In the New Project dialog, select your favorite programming language under Project Types (Visual Basic or C#) and select ASP.NET MVC Web Application under Templates. Click the OK button.
Figure 01: New Project Dialog (Click to view full-size image)
When you create a new ASP.NET MVC application, the Create Unit Test Project dialog appears (see Figure 2). This dialog enables you to create a separate project in your solution for testing your ASP.NET MVC application. Select the option No, do not create a unit test project and click the OK button.
Figure 02: Create Unit Test Dialog (Click to view full-size image)
After the new ASP.NET MVC application is created. You will see several folders and files in the Solution Explorer window. In particular, you'll see three folders named Models, Views, and Controllers. As you might guess from the folder names, these folders contain the files for implementing models, views, and controllers.
If you expand the Controllers folder, you should see a file named AccountController.cs and a file named HomeController.cs. If you expand the Views folder, you should see three subfolders named Account, Home and Shared. If you expand the Home folder, you'll see two additional files named About.aspx and Index.aspx (see Figure 3). These files make up the sample application included with the default ASP.NET MVC template.
Figure 03: The Solution Explorer Window (Click to view full-size image)
You can run the sample application by selecting the menu option Debug, Start Debugging. Alternatively, you can press the F5 key.
When you first run an ASP.NET application, the dialog in Figure 4 appears that recommends that you enable debug mode. Click the OK button and the application will run.
Figure 04: Debugging Not Enabled dialog (Click to view full-size image)
When you run an ASP.NET MVC application, Visual Studio launches the application in your web browser. The sample application consists of only two pages: the Index page and the About page. When the application first starts, the Index page appears (see Figure 5). You can navigate to the About page by clicking the menu link at the top right of the application.
Figure 05: The Index Page (Click to view full-size image)
Notice the URLs in the address bar of your browser. For example, when you click the About menu link, the URL in the browser address bar changes to /Home/About.
If you close the browser window and return to Visual Studio, you won't be able to find a file with the path Home/About. The files don't exist. How is this possible?

A URL Does Not Equal a Page

When you build a traditional ASP.NET Web Forms application or an Active Server Pages application, there is a one-to-one correspondence between a URL and a page. If you request a page named SomePage.aspx from the server, then there had better be a page on disk named SomePage.aspx. If the SomePage.aspx file does not exist, you get an ugly 404 - Page Not Found error.
When building an ASP.NET MVC application, in contrast, there is no correspondence between the URL that you type into your browser's address bar and the files that you find in your application. In an ASP.NET MVC application, a URL corresponds to a controller action instead of a page on disk.
In a traditional ASP.NET or ASP application, browser requests are mapped to pages. In an ASP.NET MVC application, in contrast, browser requests are mapped to controller actions. An ASP.NET Web Forms application is content-centric. An ASP.NET MVC application, in contrast, is application logic centric.

Understanding ASP.NET Routing

A browser request gets mapped to a controller action through a feature of the ASP.NET framework called ASP.NET Routing. ASP.NET Routing is used by the ASP.NET MVC framework to route incoming requests to controller actions.
ASP.NET Routing uses a route table to handle incoming requests. This route table is created when your web application first starts. The route table is setup in the Global.asax file. The default MVC Global.asax file is contained in Listing 1.
Listing 1 - Global.asax
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
namespace MvcApplication1
{
    // Note: For instructions on enabling IIS6 or IIS7 classic mode, 
    // visit http://go.microsoft.com/?LinkId=9394801

    public class MvcApplication : System.Web.HttpApplication
    {
        public static void RegisterRoutes(RouteCollection routes)
        {
            routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

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

        }

        protected void Application_Start()
        {
            RegisterRoutes(RouteTable.Routes);
        }
    }
}
When an ASP.NET application first starts, the Application_Start() method is called. In Listing 1, this method calls the RegisterRoutes() method and the RegisterRoutes() method creates the default route table.
The default route table consists of one route. This default route breaks all incoming requests into three segments (a URL segment is anything between forward slashes). The first segment is mapped to a controller name, the second segment is mapped to an action name, and the final segment is mapped to a parameter passed to the action named Id.
For example, consider the following URL:
/Product/Details/3
This URL is parsed into three parameters like this:
Controller = Product
Action = Details
Id = 3
The Default route defined in the Global.asax file includes default values for all three parameters. The default Controller is Home, the default Action is Index, and the default Id is an empty string. With these defaults in mind, consider how the following URL is parsed:
/Employee
This URL is parsed into three parameters like this:
Controller = Employee
Action = Index
Id = ��
Finally, if you open an ASP.NET MVC Application without supplying any URL (for example, http://localhost) then the URL is parsed like this:
Controller = Home
Action = Index
Id = ��
The request is routed to the Index() action on the HomeController class.

Understanding Controllers

A controller is responsible for controlling the way that a user interacts with an MVC application. A controller contains the flow control logic for an ASP.NET MVC application. A controller determines what response to send back to a user when a user makes a browser request.
A controller is just a class (for example, a Visual Basic or C# class). The sample ASP.NET MVC application includes a controller named HomeController.cs located in the Controllers folder. The content of the HomeController.cs file is reproduced in Listing 2.
Listing 2 - HomeController.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace MvcApplication1.Controllers
{
    [HandleError]
    public class HomeController : Controller
    {
        public ActionResult Index()
        {
            ViewData["Title"] = "Home Page";
            ViewData["Message"] = "Welcome to ASP.NET MVC!";

            return View();
        }

        public ActionResult About()
        {
            ViewData["Title"] = "About Page";

            return View();
        }
    }
}
Notice that the HomeController has two methods named Index() and About(). These two methods correspond to the two actions exposed by the controller. The URL /Home/Index invokes the HomeController.Index() method and the URL /Home/About invokes the HomeController.About() method.
Any public method in a controller is exposed as a controller action. You need to be careful about this. This means that any public method contained in a controller can be invoked by anyone with access to the Internet by entering the right URL into a browser.

Understanding Views

The two controller actions exposed by the HomeController class, Index() and About(), both return a view. A view contains the HTML markup and content that is sent to the browser. A view is the equivalent of a page when working with an ASP.NET MVC application.
You must create your views in the right location. The HomeController.Index() action returns a view located at the following path:
\Views\Home\Index.aspx
The HomeController.About() action returns a view located at the following path:
\Views\Home\About.aspx
In general, if you want to return a view for a controller action, then you need to create a subfolder in the Views folder with the same name as your controller. Within the subfolder, you must create an .aspx file with the same name as the controller action.
The file in Listing 3 contains the About.aspx view.
Listing 3 - About.aspx
<%@ Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage" %>
<asp:Content ID="aboutContent" ContentPlaceHolderID="MainContent" runat="server">
    <h2>About</h2>
    <p>
        Put content here.
    </p>
</asp:Content>
If you ignore the first line in Listing 3, most of the rest of the view consists of standard HTML. You can modify the contents of the view by entering any HTML that you want here.
A view is very similar to a page in Active Server Pages or ASP.NET Web Forms. A view can contain HTML content and scripts. You can write the scripts in your favorite .NET programming language (for example, C# or Visual Basic .NET). You use scripts to display dynamic content such as database data.

Understanding Models

We have discussed controllers and we have discussed views. The last topic that we need to discuss is models. What is an MVC model?
An MVC model contains all of your application logic that is not contained in a view or a controller. The model should contain all of your application business logic, validation logic, and database access logic. For example, if you are using the Microsoft Entity Framework to access your database, then you would create your Entity Framework classes (your .edmx file) in the Models folder.
A view should contain only logic related to generating the user interface. A controller should only contain the bare minimum of logic required to return the right view or redirect the user to another action (flow control). Everything else should be contained in the model.
In general, you should strive for fat models and skinny controllers. Your controller methods should contain only a few lines of code. If a controller action gets too fat, then you should consider moving the logic out to a new class in the Models folder.

Summary

This tutorial provided you with a high level overview of the different parts of an ASP.NET MVC web application. You learned how ASP.NET Routing maps incoming browser requests to particular controller actions. You learned how controllers orchestrate how views are returned to the browser. Finally, you learned how models contain application business, validation, and database access logic.

Understanding the ASP.NET MVC Execution Process


Requests to an ASP.NET MVC-based Web application first pass through the UrlRoutingModule object, which is an HTTP module. This module parses the request and performs route selection. The UrlRoutingModule object selects the first route object that matches the current request. (A route object is a class that implements RouteBase, and is typically an instance of the Route class.) If no routes match, the UrlRoutingModule object does nothing and lets the request fall back to the regular ASP.NET or IIS request processing.

From the selected Route object, the UrlRoutingModule object obtains the IRouteHandler object that is associated with the Route object. Typically, in an MVC application, this will be an instance of MvcRouteHandler. The IRouteHandler instance creates an IHttpHandler object and passes it the IHttpContext object. By default, the IHttpHandler instance for MVC is the MvcHandler object. The MvcHandler object then selects the controller that will ultimately handle the request.


Note:
When an ASP.NET MVC Web application runs in IIS 7.0, no file name extension is required for MVC projects. However, in IIS 6.0, the handler requires that you map the .mvc file name extension to the ASP.NET ISAPI DLL.

The module and handler are the entry points to the ASP.NET MVC framework. They perform the following actions:
  • Select the appropriate controller in an MVC Web application.
  • Obtain a specific controller instance.
  • Call the controller s Execute method.

The following table lists the stages of execution for an MVC Web project.

Stage Details
Receive first request for the application In the Global.asax file, Route objects are added to the RouteTable object.
Perform routing The UrlRoutingModule module uses the first matching Route object in the RouteTable collection to create the RouteData object, which it then uses to create a RequestContext (IHttpContext) object.
Create MVC request handler The MvcRouteHandler object creates an instance of the MvcHandler class and passes it the RequestContext instance.
Create controller The MvcHandler object uses the RequestContext instance to identify the IControllerFactory object (typically an instance of the DefaultControllerFactory class) to create the controller instance with.
Execute controller The MvcHandler instance calls the controller s Execute method.
Invoke action Most controllers inherit from the Controller base class. For controllers that do so, the ControllerActionInvoker object that is associated with the controller determines which action method of the controller class to call, and then calls that method.
Execute result A typical action method might receive user input, prepare the appropriate response data, and then execute the result by returning a result type. The built-in result types that can be executed include the following: ViewResult (which renders a view and is the most-often used result type), RedirectToRouteResult, RedirectResult, ContentResult, JsonResult, and EmptyResult.