Validating Hidden Fields in ASP.NET MVC 2


One of the many nice things they have added in MVC 2 is model/view model validation on both the client and server side.  I won’t go into the basics here since they have been covered extensively by others.  However, recently I was adding validation to require a value for a hidden field on a form, and found that the default validation functionality just didn’t work on the client side for a hidden field.  Here I will go over how I added some custom validation to allow me to do it.
While it might sound weird to validate a hidden field, something a user will never directly interact with, in this age of complex interfaces built in JavaScript it’s easy to imagine a scenario where a series of interactions leads to storing some kind of value in a hidden field.  If that end value is something that is required, it’s only natural that that should be enforced by the model.
First, let’s start by defining a very simple view model for the form:
public class DemoForm
{
    [Required(ErrorMessage = "You must enter a name")]
    public string Name { get; set; }
}
It just defines a single property for storing a name.  Now we can define a very basic view that is strictly typed to that view model.  Here is the code for setting up the form:
<% using (Html.BeginForm())
{ %>

    <%= Html.HiddenFor(model => model.Name) %>

    <p>
        <%= Html.ValidationMessageFor(model => model.Name) %>
    </p>

    <input type="submit" value="Submit" />

<% } %>
For the sake of this demo, I am going to add some buttons that can populate and clear the hidden field.  To get that out of the way, here is the markup for that:
<div>
    <input id="PopulateField" type="button" value="Populate Hidden Field" />

    <input id="ClearField" type="button" value="Clear Hidden Field" />
</div>

<p>
    Last submitted value: <%= ViewData["Name"] %>
</p>
and here is some JavaScript (using jQuery) to wire them up:
And now we’re almost done with the boring part.  Finally, we wire up the controller actions like so:
[HttpGet]
public ActionResult Index()
{
    return View();
}

[HttpPost]
public ActionResult Index(DemoForm form)
{
    ViewData["Name"] = form.Name;
    return View(form);
}
Validation form
If you run the page now, you will get correct server-side validation, but nothing on the client.  To get client validation in MVC, we need to tell the page to enable client validation.  To do that, call this before starting the form:
<% Html.EnableClientValidation(); %>
You will also need some JavaScript references to these files, which are included by default in any new MVC project:
<script type="text/javascript" src="<%= ResolveUrl("~/Scripts/MicrosoftAjax.js") %>"></script>
<script type="text/javascript" src="<%= ResolveUrl("~/Scripts/MicrosoftMvcAjax.js") %>"></script>
<script type="text/javascript" src="<%= ResolveUrl("~/Scripts/MicrosoftMvcValidation.js") %>"></script>
Now if you reload the page, client validation still isn’t working.  Why not?  After digging through the Microsoft validation scripts a bit, I found that the Required validator ignores hidden fields altogether in JavaScript.  We could change it from being a hidden field to a text field and the same code would work as expected, but that’s not what we want to do.
As it turns out, they made it rather simple to extend the built-in validation with your own custom rules.  First, we’ll define the attribute class, so that it can be added as a decorator to a property the same way the other validators work.  In this case, since the Required attribute actually works fine on the server side, our class will extend that and do nothing else.  This way we can provide the client side functionality and rely on the existing server validation.
public class HiddenRequiredAttribute : RequiredAttribute
{
}
Now that we have the attribute, we need a validator that uses it.  To do that, we extend the DataAnnotationsModelValidator class, and tell it to use the attribute we just defined:
public class HiddenRequiredValidator : DataAnnotationsModelValidator<HiddenRequiredAttribute>
{
    private string _errorMessage;

    public HiddenRequiredValidator(ModelMetadata metaData, ControllerContext context, HiddenRequiredAttribute attribute)
        : base(metaData, context, attribute)
    {
        _errorMessage = attribute.ErrorMessage;
    }

    public override IEnumerable<ModelClientValidationRule> GetClientValidationRules()
    {
        var rule = new ModelClientValidationRule
        {
            ErrorMessage = _errorMessage,
            ValidationType = "hiddenRequired"
        };

        return new[] { rule };
    }
}
The important part to notice here is where we set the ValidationType to “hiddenRequired.”  This is the unique identifier we define for referencing our validator in JavaScript.  Remember it, because you’re going to need it later.
So now we have an attribute, and a validator for it.  Next up, we need to register our validator in the application’s Application_Start function:
DataAnnotationsModelValidatorProvider
    .RegisterAdapter(typeof(HiddenRequiredAttribute), typeof(HiddenRequiredValidator));
Now let’s switch the view model’s Name property to use the new HiddenRequired attribute instead of the Required one we had before:
public class DemoForm
{
    [HiddenRequired(ErrorMessage = "You must enter a name")]
    public string Name { get; set; }
}
All that remains is to put in some JavaScript code to handle the validation.  I’ll show the code here, and then discuss it.  Since this is a contained demo, I will add the code directly to the view.
Sys.Mvc.ValidatorRegistry.validators["hiddenRequired"] = function(rule)
{
    return function(value, context)
    {
        if (value)
        {
            return true;
        }

        return rule.ErrorMessage;
    }
};
If that code looks simple, that’s because it is.  Since we’re only validating that the field has some kind of value, that is all we need to check for.  The reason that the return function is wrapped in another function is to allow for adding more complex logic before the return function if needed.  For this example, it isn’t needed.
Validation form
Now if you reload the page you will get the client-side validation we were aiming for, triggered by the submit button.  Always remember that client-side validation is just there for improving the user experience, and is no substitute for server validation.

.............................................................................................................................................................
In the previous posts in this series I introduced the client side state managementand one of its techniques - the ViewState. Today I'm going to drill down into the hidden fields technique. As mentioned in the previous post, the ViewState stores its state in a hidden field.

What are Hidden Fields?

Hidden fields technique is widely used in ASP.NET programing. Hidden fields are html input control with hidden type that store hidden data in the html. An example for a hidden field can look like this:
1.<input type="hidden" name="__EVENTTARGET" id="__EVENTTARGET" value="" />
In the example above, I chose to show the event target hidden field in order to indicate that even in postback mechanism hidden fields are being used. The data stored in a hidden field is available when the form is processed on the server or when we use javascript.

Hidden Fields Values 

Hidden fields store only one value in their value property. The value is saved as a string and therefore in order to use it for other types you need to perform casting. Hidden fields' data is submitted to the server only in HTTP post operation. You can see the stored data easily by using the View Source operation of the browser. You can see it by clicking the right mouse button and then choosing View Source from the menu (if the operation is available). Be aware not to use hidden fields to store confidential data! The values has page context and therefore when you leave a page the data stored in the hidden fields is disposed.

Server Control Hidden Fields 

There are two types of server control hidden fields -  System.Web.UI.WebControls.HiddenField and System.Web.UI.HtmlControls.HtmlInputHidden. Both types has the same primary Value property to hold the value of the hidden field. You should choose between these types whenever you need to use a server side hidden field (A note - The difference between HtmlControls and WebControls isn't in the context of this post). When you don't use server controls you can use the Request.Form NameValueCollection to get the hidden field value by providing the client
id of the hidden field. For example the code bellow will return the string value of the __EVENTTARGET hidden field:
1.string eventTarget = Request.Form["__EVENTTARGET"];

Hidden Fields Example

I got some requests for an example of how to use the hidden fields inside web forms. The example is very easy to understand. I have an html input with type hidden. In the first request I fill the input with a message value with javascript function. I use a button control to do a post back to the server and in the post back the value of the hidden
field is inserted into a label control. Pay attention that because I use an html hidden field that isn’t a server control after a second post back it’s value will be empty. The only reason that the label will still show a message is because
the label’s default value of the EnableViewState is true. The web page’s code:
01.<%@ Page="" Language="C#" AutoEventWireup="true" Codebehind="Default.aspx.cs"
02.Inherits="AJAXEnabledWebApplication1._Default" EnableSessionState="False" %>
03. 
04.<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
05. 
06.<html xmlns="http://www.w3.org/1999/xhtml">
07. 
08.<head runat="server">
09.<title>Untitled Page</title>
10. 
11.<script type="text/javascript">
12.function PutHtmlHiddenFieldValue(message)
13.{
14.var hidden = document.getElementById('HTMLHiddenField');
15. 
16.if (hidden != null)
17.{
18.hidden.value = message;
19.}
20.}
21.</script>
22.</head>
23. 
24.<body dir="ltr">
25.<form id="form1" runat="server">
26.<div>
27.<div>
28.<asp:Label ID="lblHTMLHiddenField" runat="server"></asp:Label>
29.<input id="HTMLHiddenField" type="hidden" name="HTMLHiddenField"/>
30.</div>
31. 
32.<div>
33.<asp:Button ID="btnForPostBack" runat="server" Text="Do Postback" />
34.</div>
35.</div>
36.</form>
37.</body>
38.</html>
On the server side:
01.public partial class _Default : System.Web.UI.Page
02.{
03.#region Consts
04. 
05.private const string SCRIPT_KEY = "HtmlHiddenFieldScript";
06. 
07.#endregion
08. 
09.#region Page Events
10. 
11.protected void Page_Load(object sender, EventArgs e)
12.{
13. 
14.// Insert message to the label of the html control hidden
15.// field if there is a value in the html hidden field
16. 
17.string message = Request.Form["HTMLHiddenField"];
18. 
19.if (!string.IsNullOrEmpty(message))
20.{
21.lblHTMLHiddenField.Text = message;
22.}
23.}
24. 
25.protected void Page_PreRender(object sender, EventArgs e)
26.{
27. 
28.// Register a startup script in order to fill the html
29.// hidden field with a message value
30. 
31.if (!ClientScript.IsClientScriptBlockRegistered("HtmlHiddenFieldScript") &&             !IsPostBack)
32.{
33. 
34.ClientScript.RegisterStartupScript(typeof(Page), SCRIPT_KEY,                     "PutHtmlHiddenFieldValue('Html hidden hello world');", true);
35. 
36.}
37.}
38. 
39.#endregion
40.}

Summary

To sum up the post, today I drilled down into the hidden fields technique. This technique is very popular and is used widely in ASP.NET. In the next post I'll continue the tour in the client side state management techniques.



explain about querry string in asp.net


You want to see how to use the QueryStringcollection in the ASP.NET web development framework. Almost all examples show one way of using this NameValueCollection, but there are faster and more logical ways. This short information sheet has some examples and tips with QueryString, using the C# programming language.

Use QueryString

Here we see an .aspx Web Forms page that executes when the user accesses Default.aspx. The code here is the code-behind part, Default.aspx.cs, and it is written in the C# programming language. To test the above code, run the page in the web browser on the ASP.NET development server. It will be completely blank. Try adding the string "?param=dotnet" at the end of the URL.
QueryString example [C#]

using System;
using System.Web.UI;

public partial class _Default : Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
 string v = Request.QueryString["param"];
 if (v != null)
 {
     Response.Write("param is ");
     Response.Write(v);
 }
    }
}

Program result
    It prints value of param query.
    The screenshot shows the result.
Testing query string in ASP.NET page

Two query string parameters

Here we see how you can test two query string URL parameters. Another common requirement is to test two different query strings. You may have to use either one or both at once. The next example here has some inefficiencies, but it works well otherwise.
QueryString example with multiple parameters [C#]

using System;
using System.Web.UI;

public partial class _Default : Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
 string v = Request.QueryString["param"];
 if (v != null)
 {
     Response.Write("param is ");
     Response.Write(v);
 }
 string x = Request.QueryString["id"];
 if (x != null)
 {
     Response.Write("   id detected");
 }
    }
}

Test code with this url:

?param=first&id=true
Testing the code. Try the above example code by typing, at the end of the URL in Internet Explorer or Firefox, the test url. The above string specifies that the "param" query is equal to "first", while the "id" param is equal to "true". Always be careful with the "id" query string, as Google's spiders may avoid it.
Query string usage example

Advanced example

The Request.QueryString collection is a NameValueCollection internally. QueryString is implemented as a property getter for an internal NameValueCollection. The NameValueCollection is a specialized collection that has a somewhat unusual implementation. MSDN describes it as a "collection of associated String keys and String values that can be accessed either with the key or with the index."
msdn.microsoft.com
Testing the QueryString collection. My testing indicates that using the index to access a value in NameValueCollection is far faster than using the ["string"] syntax.
Page that uses HasKeys() on QueryString [C#]

using System;
using System.Web.UI;
using System.Collections.Specialized;

public partial class _Default : Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
 // 1
 // Get collection
 NameValueCollection n = Request.QueryString;
 // 2
 // See if any query string exists
 if (n.HasKeys())
 {
     // 3
     // Get first key and value
     string k = n.GetKey(0);
     string v = n.Get(0);
     // 4
     // Test different keys
     if (k == "param")
     {
  Response.Write("param is " + v);
     }
     if (k == "id")
     {
  Response.Write("id is " + v);
     }
 }
    }
}
Overview. In step one, this code gets a reference to the QueryString collection. In high-performance code, you want to avoid excessive property lookups.
Next steps in the code. In step two, we use the HasKeys() method on QueryString. This is very useful for telling whether there are any query string keys available on the URL. In step four, we do two lookups on the NameValueCollection to get the very first key and the very first value. Because we only access the first key and value, this code doesn't work for more than one key value pair.

NameValueCollection

This article is really more about NameValueCollection than QueryString, as QueryString is simply an instance of NameValueCollection. You can find more detailed information about this collection on this site.
NameValueCollection Usage

Optimizations

Here I benchmarked the query string code examples. The benchmark simply reads the very first query string key/value pair from the URL. My requirement was to accept only the first query string on a page.
Example that uses HasKeys()... [Method A] [C#]

using System;
using System.Web;
using System.Web.UI;
using System.Collections.Specialized;

public partial class _Default : Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
 HttpRequest q = Request;
 NameValueCollection n = q.QueryString;
 if (n.HasKeys())
 {
     string k = n.GetKey(0);
     if (k == "one")
     {
  string v = n.Get(0);
     }
     if (k == "two")
     {
  string v = n.Get(0);
     }
 }
    }
}

Example that uses QueryString["key"] syntax... [Method B] [C#]

using System;
using System.Web;
using System.Web.UI;
using System.Collections.Specialized;

public partial class _Default : Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
 HttpRequest q = Request;
 string v = q.QueryString["one"];
 if (v != null)
 {
 }
 v = q.QueryString["two"];
 if (v != null)
 {
 }
    }
}
Discussion. What we see is that when you only need to access the first query string key/value pair, using HasKeys(), Get(), and GetKey() is lots faster than the QueryString["string"] approach.
No query string

Method A: 0.08 s
Method B: 1.75 s

Matching query string

Method A: 0.45 s
Method B: 2.80 s

No matching query string

Method A: 0.46 s
Method B: 1.73 s
Question mark.

Is QueryString filled lazily?

No. I found that by the time your HttpRequest is being used, the QueryString collection has already been initialized with all the query key/value pairs. I looked at the locals in the Visual Studio debugger to find this. Therefore, the analysis in this article is correct and the faster method shown is really considerably faster than the naive approach.
Visual Studio Debugging Tutorial

Notes

When you use QueryString in Global.asax or another "hot" path in your code, it pays to optimize the logic. When your website runs the code several times each second, you need it to be as fast as possible.

Summary

We saw several examples of Request.QueryString, using the same code pattern that most ASP.NET tutorials use. However, we also noted how you can use other methods on QueryString to gain a substantial performance increase.
.....................................................................................................


Introduction

Often you need to pass variable content between your html pages or aspx webforms in context of Asp.Net. For example in first page you collect information about your client, her name and last name and use this information in your second page.
For passing variables content between pages ASP.NET gives us several choices. One choice is usingQueryString property of Request Object. When surfing internet you should have seen weird internet address such as one below.
http://www.localhost.com/Webform2.aspx?name=Atilla&lastName=Ozgur
This html addresses use QueryString property to pass values between pages. In this address you send 3 information.
  1. Webform2.aspx this is the page your browser will go.
  2. name=Atilla you send a name variable which is set to Atilla
  3. lastName=Ozgur you send a lastName variable which is set to Ozgur
As you have guessed ? starts your QueryString, and & is used between variables. Building such a query string in Asp.Net is very easy. Our first form will have 2 textboxes and one submit button.
Put this code to your submit button event handler.
private void btnSubmit_Click(object sender, System.EventArgs e)
{
Response.Redirect("Webform2.aspx?Name=" +
this.txtName.Text + "&LastName=" +
this.txtLastName.Text);
} 
Our first code part builds a query string for your application and send contents of your textboxes to second page. Now how to retrieve this values from second page. Put this code to second page page_load.
private void Page_Load(object sender, System.EventArgs e)
{
this.txtBox1.Text = Request.QueryString["Name"];
this.txtBox2.Text = Request.QueryString["LastName"];
} 
Request.QueryString is overloaded with a second way. You can also retrieve this values using their position in the querystring. There is a little trick here. If your QueryString is not properly built Asp.Net will give error.
private void Page_Load(object sender, 

System.EventArgs e)
{
this.txtBox1.Text = Request.QueryString[0];
this.txtBox2.Text = Request.QueryString[1];
}
Some other ways to reach contents of QueryString.
foreach( string s in Request.QueryString)
{
Response.Write(Request.QueryString[s]);
}
Or 
for (int i =0;i < Request.QueryString.Count;i++)
{
Response.Write(Request.QueryString[i]);
} 
Advantages of this approach
  1. It is very easy.
Disadvantages of this approach
  1. QueryString have a max length, If you have to send a lot information this approach does not work.
  2. QueryString is visible in your address part of your browser so you should not use it with sensitive information.
  3. QueryString can not be used to send & and space characters.
If you write this code and try them you will see that you have a problems with space and & characters, e.g. if you need to send a variable which contains & such as "Mark & Spencer". There must be a solution for this problem. If you look to Google�s query string you will see that it contains a lot of %20. This is the solution of our third disadvantage. Replace space with %20 and & with %26 for example.
private void btnSubmit_Click(object sender, System.EventArgs e)
{
string p1 = this.txtName.Text.Replace("&","%26");
p1 = this.txtName.Text.Replace(" ","%20");
string p2 = this.txtLastName.Text.Replace("&","%26");
p2 = this.txtName.Text.Replace(" ","%20"); 
            "WebForm2.aspx?" + 
            "Name=" + p1 + 
            "&LastName=" + p2;
Response.Redirect(p2);
} 
Since this is a such a common problem Asp.Net should have some way to solve. There it isServer.UrlEncode. Server.UrlEncode method changes your query strings to so that they will not create problems.
private void btnSubmit_Click(object sender, System.EventArgs e)
{
Response.Redirect("WebForm2.Aspx?" + 
"Name=" +   Server.UrlEncode(this.txtName.Text) + 
"&LastName=" + Server.UrlEncode(this.txtLastName.Text)); 
} 
Same solution is in Microsoft .Net Quick Start tutorials.
ASP.NET --- Working with Web Controls ---
--- Performing Page Navigation (Scenario 1) ---
--- Performing Page Navigation (Scenario 2) ---
Look at them also if you want to see more example for this technique. Also I advise you to look at Alex Beynenson's article about building QueryString(s).

difference between Identity and Primary Keys

If you are an Access user, or migrated from Access to SQL Server, you may think that a primary key and an Identity/Autonumber column are the same thing. Well, they’re not, and there are some very good reasons NOT to use an Identity column for the primary key on your tables.
I'll cover what's bad about Identity and show some examples of alternate key candidates. In Part II I'll go over instances where Identity isn't a bad choice--meaning there is really no other choice. I'll be using the term Identity in these articles, but all of these arguments apply to GUID columns as well.

Definitions

A key is a value or combination of values that is unique for every row in a table. The difference between primary and ordinary keys is that there can be multiple keys, but only one primary key (think of all the things you like about your girlfriend/boyfriend, then pick the one you like most). You can use any data type or combination in a primary key, so long as the column(s) involved are declared NOT NULL (an index is automatically created on the primary key). Keys are also used to create and enforce relationships between tables; these are known as foreign keys. Foreign keys in the referenced (parent) table must be unique, and therefore are superb candidates as primary key. Identity (or AutoNumber) is a column that automatically generates numeric values. You can set a start and increment value, but most people leave these at 1. A GUID column also generates numbers, but you cannot control its values. Identity/GUID columns do not need to be indexed.
An index is a structure in a table that orders the data. It allows the database to access data quickly. The two major kinds of index are clustered and non-clustered; clustered indexes physically store the data in the same order as the index. There can only be one clustered index per table, because the data can only be physically ordered one way (think of page numbers in a book, or names in the phone book). Non-clustered indexes maintain order on the index page only (just like the index in the back of a book), and provide a pointer to the page where the data resides. Indexes will be covered in more detail in Part II; for now they won't affect the choice of primary key.

Identity columns are bad because...

  1. They're not standard SQL. Most products have it but there's no consistent implementation.
  2. They can't be updated. This violates the relational data model (not fatal, but not good either). Duplicates can be accidentally inserted (fatal).
  3. They only create numeric values. GUID/NewID() are also numeric only, and are hard to read.
  4. Numeric values are not meaningful in many tables, and adding them complicates relationships between other tables.

Just to be fair...

  1. Plug-and-play key generation, much easier than writing an algorithm to do it.
  2. Small data space for a wide range of values.
  3. When ALL other key candidates are exhausted, Identity can be a lifesaver, especially when relating tables.
A quick rant about Access: I like it and I think it is a great program (hold on, the rant is coming), but it tends to do things for you that you should do for yourself. If you have not created a primary key on your table, DON'T let the computer add one. I think this is Access' worst feature; it scares newbies and misinforms them about primary keys. They get into the habit of adding an AutoNumber when they don't need it (I was one of them).
Many people have tables with some kind of ID column, and yet they still have unwanted duplicates in the table (removing dupes is a VERY POPULAR post in the SQLTeam forums). Lots of people forget that you can have more than one column in a key. If you have a table like this, use this "natural key" as the primary key instead of adding an Identity column. If Identity can't prevent dupes, don't use it.
By the way, you don't absolutely NEED a primary key. This sounds contrary to Joe Celko's statement "If you don’t have a key, you don’t have a table", but it isn't. He's more concerned about the relational definition of a table -- it requires every row to be uniquely identified by its primary key. If the data in your table doesn't guarantee uniqueness, you should add a column that will.

Duhhhh, then why not add an Identity column?

All it does is add a column of arbitrary and useless numbers, and there are plenty of data models that don't need 'em – imagine...Shakespeare picks up a flower catalog, and writes the immortal phrase, "A #154783 by any other name, uh, number, smells just as sweet." For something like this catalog, use Name AND Color to distinguish between roses. Or add their Latin classification. These may seem like arbitrary values, but they're data with MEANING, especially outside of this one catalog.

How about a real-world example?

This post is a good example of tables that don't really need an Identity column. It beautifully illustrates how Identity actually causes more problems than it solves. As another example, I have a simple Employees table at my job (it incorporates an old Excel phone list but adds more people to it):
IDBossIDPhoneFirstNameLastNameDeptTitleLanBossLan
126555-555-1212RobVolkSQL StuffAged SQL Warriorrvolkjsmith
2202555-555-2121DickCheneyGovernmentVice Presidentdcheneydubya
3107555-555-2112NeilPeartRhythm SectionDrum Godnpeartbrich
5NULL000-000-0000JasonNewstedRhythm SectionDamaged Bass PlayerjnewstedNULL
699NULLJodi AnnPatersonFantasyPlaymate of the Yearjpatersohhefner
91NULLAshleyJuddFantasyRob’s Dream Girlyummygalrvolk
1026555-555-1213JasonNewstedFinanceSenior Accountantjnewsted2jsmith
This second table (WebPages) is for employee web bookmarks; they can store these and access them from any computer:

IDURLLan
1http://www.sqlteam.comrvolk
2http://www.whitehouse.govdcheney
9http://www.internetcity.com/ashleyjudd/yummygal
9http://www.internetcity.com/ashleyjudd/rvolk
3http://www.rushbackstage.comnpeart
3http://www.rush.comnpeart
5http://www.metallica.com/news/2000/jason_release.htmljnewsted
Back to the Employees table. The only numeric column in it is the phone number (the ID columns are here for illustration only). Why not use this as primary key? Not everyone has a phone, sadly -- I wish they did! :) Names won't work: Mr. Newsted didn't leave Metallica to be an accountant, it's really some other guy. In the real table, there actually are two guys named Kevin Jones -- AND they’re both in Sales, so adding Department to the key wouldn’t help. Let's assume for now that I added the ID Identity column as my primary key; I'll come back to it in a second.
Let's do a quick exercise using these two tables. First, in the Employees table, using only the ID and BossID columns, identify each person's boss. Can't do it, huh? That was deliberate. Now in both tables, using the ID column only, identify which bookmark belongs to whom. Possible, but not easy. My point is to reiterate the meaningless nature of an Identity column's values -- imagine doing this with GUID's instead!
When I first put the Employees table together, there was no good primary key candidate, and I WAS going to use Identity until someone mentioned our company's Novell LAN user names. I didn’t need it in my table, but it turned out to be handy: they had to be unique for each user, everyone in the company gets one, and it's small (8 characters) and works as a single column. Now try those exercises using the Lan and BossLan columns. You'll notice a pattern to the values in the Lan column. It's descriptive enough for you to figure out who's who in WebPages without joining it to Employees.
Just for fun, try to figure out the primary key of the WebPages table. It's not a big challenge.

OK Teach, I will never use Identity again...

As promised, I will show some examples where Identity DOES make sense as a primary key, in Part II of this article. I can't stress enough that this should be your last option when designing your tables. It may not seem worth the effort to go to such lengths, but to paraphrase Ron Soukup, former SQL Server project manager, “Identity primary keys are for people who believe there’s never time to design a table right but there’s always time to do it over.”
Addendum - There will not be a Part II to this article. There are numerous discussions about using Identity as a primary key in the Comments section of this article here, if you are interested I recommend you read them by clicking the Comments link below.