Making a registration system with Asp.Net and Linq-to-SQL (part 2)

Verifiying your users' email and activating thier accounts

In part 1 of this tutorial, we ended by emailing the user a verification link. In this part, we will us that to verify the user and activate their account.

markup

The markup is very basic, as almost all of your stuff will be done in the backend:

confirm.aspx
<h2>Confirmation</h2>
<asp:Panel ID="Report" runat="server" />

Handle your input

This is where all the heavy lifting is done.Your user has a link which passes the verification guid in the querysting, so our verification page must parse the querystring.There are a few things we will be doing here:
  • Make sure there is a querystring
  • Make sure the Guid is in Guid format
  • Confirm the user from the DB based on Guid

Which is all reletively simple:

confirm.aspx.cs
if (string.IsNullOrEmpty(Request.QueryString["guid"]))
  throw new Exception("invalid inputs", new Exception("please navigate here from the provided link"));

SqlConnection con = new SqlConnection(ConfigurationManager.ConnectionStrings["demoConnectionString"].ConnectionString);
try
{
  con.Open();
  dbDataContext db = new dbDataContext();
  string guid = Request.QueryString["guid"];
  Guid g = new Guid(guid);
  user u = Users.GetFromGuid(db, g);
  if (u.confirmed)
    Report.Success("account already confirmed", "apparently it was fun enough to do twice...");
  else
  {
    u.confirmed = true;
    db.SubmitChanges();
    Report.Success("account confirmed", "put some link here for the user to continue");
  }
}
catch (Exception ex)
{
  if (ex.Message.Contains("no elements"))
    ex = new Exception("confirmation number not found");   Report.Error(ex);
}
finally { con.Close(); }

Users.cs
public static user GetFromGuid(dbDataContext db, Guid guid)
{ return db.users.Single(u => u.guid_ == guid); }

Ok, now what was done there? First, we simply test to see if a proper querystrirng was passed, if not, it throws an error which is handled with my.Error() extension.Then it goes on to make a SqlConnection, which might seems strange as it is not necessary, *but* since we are possibly making two calls to the DB: getting the user and confirming the user (if necessary) - opening the connection will make sure it only opens once as opposed to twice if we weren't to open it; it is unnecessary, but it is ever-so-slightly more efficient.After that it goes to get the user object from the DB by the GUID, if it is found, it is then confirmed.If there are eny errors or it is not found, it is handled.

Now, since nothing is emailed or anything like that, we can assume (maybe not totally safely) that the user who confirms is the one who got the email. I suppose there could be a brute force GUID bombing of the site, but that would be incredibly inefficient, not to mention pointless as it would only activate accounts and not provide the hacker with anything.

And that is it. You now have a fully working registration system that requires email confirmation and it wasn't even that tough. Here is the code:


« Part 1» Part 2

Making a registration system with Asp.Net and Linq-to-SQL (part 1)

This registration has a slick interface and user verification

NOTE* I do not cover encryption in this tutorial, but I highly discourage storing plain-text passwords in your DB!

It is inevitable that every programmer will want to eventually make a site which requires registration. Then you need to deal with spam accounts and all that good stuff. This is part one of a series of tutorials, where I will show you how to set up a registration process that requires a valid email address. I will be building on the code provided here for the next tutorial and so on. In the future, I will also include a login process I developed that will lock out accounts after a certain amount of attempts, retrieve passwords, and all sorts of other goodies. But before users can log in, they have to register.

I will be using a lot of other people's stuff in this example, such as jQuery, jQuery extensions, Grid960 and so on as well as a lot of my own Extensions, etc. With that said, this is going to have a lot of 'extras' included such as Ajax functionality and some UI niceties to make it a quality interface - you can feel free to cut these parts out, but I feel it will be nice for those that want it. All of the code referenced is included.

Storage

The first step is setting up your user table in the database. The most important thing to think of here is: what am I going to need to collect? I am all about being as simple as possible, so I am going to require two things: email and password - that is it. This is what I came up with:
  • userid - the primary key integer
  • email - the user's email
  • password - user's password
  • guid - guid for verification
  • created - date created; both for record keeping and to see if it was not confirmed after a long time it can be removed
  • confirmed - whether or not it is confirmed
  • last try* - the last login
  • number of failed logins* - number of failures for lockout

The two starred items will not really be used in this too tutorial and are optional if you do not want to prevent unlimited login attempts; though they will be relevant in upcoming tutorials.

Here is the SQL to create my table for users:
CREATE TABLE dbo.users (
userid INT NOT NULL PRIMARY KEY IDENTITY,
email VARCHAR(100) NOT NULL UNIQUE,
password_ VARCHAR(30) NOT NULL,
guid_ UNIQUEIDENTIFIER NOT NULL UNIQUE,
created DATETIME NOT NULL,
confirmed BIT NOT NULL DEFAULT 0,
last_try DATETIME NOT NULL,
tries INT NOT NULL DEFAULT 0
);

Markup

Now that we have our table, go ahead and drag it into a dbml - for this example, I will use one named db.dbml. Now we have our access layer built, we can work on making a registration control; I will be making an ascx registration control, so it can be plugged in aywhere I want to use it. Since I am only collecting two bits of information, this will be a simple control. Here is the markup, I will explain it afterwards:

register.ascx
<asp:UpdatePanel ID="upContact" runat="server">
  <ContentTemplate>
    <div class="pad">
      <asp:Panel ID="Report runat="server" />
      <asp:Panel ID="pnlRegister" runat="server" DefaultButton="registerSubmit">
        <div class="pad_sides">
          <div>
            <h4>
              <asp:RequiredFieldValidator ID="rfvRegisterEmail" runat="server" CssClass="validate" ValidationGroup="register"
                ControlToValidate="registerEmail" ErrorMessage="required" Display="Dynamic" />
            <asp:RegularExpressionValidator ID="regRegisterEmail" runat="server" ControlToValidate="registerEmail"
                ErrorMessage="invalid email" CssClass="validate" Display="Dynamic" ValidationGroup="register"
                ValidationExpression="^([a-zA-Z0-9_\-\.]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([a-zA-Z0-9\-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$"
            />
              email
            </h4>
            <asp:TextBox ID="registerEmail" runat="server" CssClass="inputBig full" />
          </div>
        <div>
          <h4>
            <asp:RequiredFieldValidator ID="rfvRegisterPassword" runat="server" CssClass="validate" ValidationGroup="register"
                ControlToValidate="registerPassword" ErrorMessage="required" Display="Dynamic" InitialValue="8 character minimum" />
            <asp:RegularExpressionValidator ID="regRegisterPassword" runat="server" CssClass="validate" ValidationGroup="register"
              ControlToValidate="registerPassword" ErrorMessage="must be at least 8 characters" Display="Dynamic"
              ValidationExpression="^.{8}.*$" />
            password
          </h4>
          <asp:TextBox ID="registerPassword" runat="server" CssClass="inputBig full wm watermark" Text="8 character minimum" />
        </div>
        <div class="summary field">
          <asp:LinkButton ID="registerSubmit" CssClass="button"
            Text="submit" runat="server" ValidationGroup="register" onclick="registerSubmit_Click" />
          </div>
        </div>
      </asp:Panel>
    </div>
  </ContentTemplate>
</asp:UpdatePanel>

Ok, there is a lot going on here, so I will go part by part.

First of all, you will notice that it is within a Asp.Net UpdatePanel which I have been trying to get away from for most things, but for such small controls I have found that is is the best way to go about it: easy and fast.

Next you will see that I have added a Panel with an ID of "Report" - I use this as a standard in most applications as to where to output my 'updates' to the user. This is explained here. The code for this is included in the Extensions.cs file.

Next there is a good amount of validation going on.
  • First I use RequiredFieldValidators for both fields
  • Then I added the RegularExpressionValidator for emails
  • Then I added the RegularExpressionValidator for password length
  • Finally you will notice that the password entry has a watermark which is called via jQuery in the MasterPage

You might notice that I am not using a password field or asking for password verification. This is something you might want to do, but for this example, security is not really a concern, simplicity is; so I figure if you can see your password, you wont screw it up. Also, since we will be adding a password retrieval function, this won't be a big deal.

Backend

That is the markup, but now we have to go to the code so it actually does something. Now what does this have to accomplish?
  • Check if the Email has already been registered
  • Create a new entry in the users table
  • Send an email with a verification link

Not too much going on here, here is the code for accomplishing that, followed by an explanation:

register.ascx.cs
using System;

public partial class controls_register : System.Web.UI.UserControl
{
  protected void Page_Load(object sender, EventArgs e)
  {
    if (this.Attributes["in_page"] != null)
    {
      Utils.DuplicateValidators(this);
    }
  }

  protected void registerSubmit_Click(object sender, EventArgs e)
  {
    try
    {
      dbDataContext db = new dbDataContext();
      if(Users.DuplicateEmail(db, registerEmail.Text))
        throw new Exception("email already registered");
      Guid g = Guid.NewGuid();
      user u = new user()
      {
        created = DateTime.Now,
        email = registerEmail.Text,
        guid_ = g,
        password_ = registerPassword.Text,
        last_try=DateTime.Now
      };
      db.users.InsertOnSubmit(u);
      db.SubmitChanges();
      Email email = new Email(registerEmail.Text, Settings.Get("gm"),
        "please verify your email address",
        "http://yoursite.com/confirm.aspx?guid=" + g.ToString()); //we will get to this in the next tutorial
      email.Send();
      Report.Success("account successfully created", "please check your email to verify your account");
      pnlRegister.Visible = false;
    }
      catch (Exception ex)
    {
      Report.Error(ex);
    }
  }
}

The first thing that happens here is the check for if the Attribute "in_page" is set. This is a bit of a sidebar as it just takes care of duplicate validators if there is more than one of these controls on the page, since I plan on showing how to use them both as a modal popup as well as a standalone page I had to add this check; that way, if you are filling out the popup instead of the form on the page it makes sure that it will not fire the validation for the form you are not using, all it does is change the validation group. The code is visible in the Utils class if you are curious about it. Don't really worry about this too much right now, as it will be covered in an upcoming tutorial.

Next it checks if the email is a duplicate. This calls the Users.cs class, we will get to that next; just remember for now it returns true if it is already in the system, false if not.

If it is new, a new user is then made and inserted into the DB via Linq-to-SQL.

An email is made and sent to the user with the link to the authorization page (which will be coevered in the next tutorial). This is sent using a simplified Email class. The authorization is the guid which was produced - I will cover the authorization page in the next part of the tutorial.

Then the user is notified of the status whether it is success or error using the panel reporting extensions.

This is all pretty simple, all that is left is to explain what is going on in the Users.cs class which is also simple:

Users.cs
using System.Linq;

public static class Users
{
  public static user GetFromEmail(dbDataContext db, string email)
  { return db.users.First(u => u.email.ToLower().Equals(email.ToLower())); }

  public static bool DuplicateEmail(dbDataContext db, string email)
  {
    try
    {
      user temp = GetFromEmail(db, email);
      span class="var">return true;
    }
    catch { return false; }
  }
}

As you can see, this is just two basic LINQ queries, the first one retrieving a user object based on an email string. And the duplicate check which tries to find a user with the given email, if it can, it will return true, meaning it is already in there, otherwise it spits out a false.

And that is all there is to it so far. It is not yet a working system as the user has not verified their identity, but we have accomplished the base of a registration system:
  • Collected the user's email address
  • Collected their password
  • Produced a unique Guid for verification
  • Sent them an email to verify their identity

Now we have it all built, we just need to display everything and call the necessary scripts. I am going to stick all of these pages within a masterpage which calls the scripts in the Page_Load:

demo.master.cs
protected void Page_Load(object sender, EventArgs e)
{
  Page.ClientScript.RegisterClientScriptInclude(typeof(demo), "jQuery", ResolveUrl("~/js/jquery.js"));
  //this will be used in the next tutorial
  Page.ClientScript.RegisterClientScriptInclude(typeof(demo), "jQuery", ResolveUrl("~/js/jqModal.js"));
}

Then just call the control (registered in the web.config file) and the js in the markup:

register.aspx
<h2>Registration</h2>
<cc:register id="reg" runat="server" />

and call the watermark frorm jQuery:

demo.master
<script type="text/javascript" language="javascript">
   $().ready(function() {
     swapValues = [];
     $(".wm").each(function(i) {
       swapValues[i] = $(this).val();
       $(this).focus(function() {
         if ($(this).val() == swapValues[i]) {
           $(this).val("").removeClass("watermark")
         }
       })
     })
   });
</script>

Notice that I am calling the watermark in the masterpage. This may seem strange, but this stops me from writing redundant code as this will take care of *all* watermarks that I will put into this project due to the versatiliy of jQuery.

All we have to do to complete this registration process is to verify the email which will be the next part to this tutorial. I am also going to show how to add this into a registration popup. The hard part is all finished.


Shout it » Part 2 » Part 3

Keeping all your settings in an XML file in a centralized location

Often times you want to centralize some key data, and avoid a DB call

Most of my applications use a settings.xml file where keep all of my settings. I then use a Settings.cs class to view/update those values. It is great for centralizing data while avoiding unnecessary trips to the DB; change once, it's changed everywhere.

One of the most common things I use in my settings class is for my email settings, just in case I want to change the account or smtp that I am using, here is a sample of what one would look like:
<?xml version="1.0" encoding="utf-8" ?>
<settings>
  <smtp>smtp.gmail.com</smtp>
  <ssl>True</ssl>
  <mail_account>demo@naspinski.net</mail_account>
  <mail_password>Iparty</mail_password>
</settings>

Pretty basic stuff there, now here is the Settings.cs class I use to access it using a little Linq-to-XML:
using System.Linq;
using System.Web;
using System.Xml.Linq;

public static class Settings
{
  public static string Get(string setting)
  { // gets the specified setting
    XElement x = XElement.Load(HttpContext.Current.Server.MapPath("~") + "\\App_Data\\settings.xml");
    return (from p in x.Descendants(setting) select p).First().Value;
  }

  public static void Update(string setting, string value)
  { // changes the specified setting
    string file_location = HttpContext.Current.Server.MapPath("~") + "\\App_Data\\settings.xml";
    XElement x = XElement.Load(file_location);
    XElement xe = (from p in x.Descendants(setting) select p).First();
    xe.Value = value;
    x.Save(file_location);
  }
}

As you can see, I keep my settings in the App_Data folder. Here you can see how I use it in some apps, for example, this is how I use it with my Email.cs class:
public Email(string to, string from, string subject, string body) : base(Settings.Get("smtp"), 587)
{
  this.Credentials = new System.Net.NetworkCredential(Settings.Get("mail_account"), Settings.Get("mail_password"));
  ...

I think you can figure out Settings.Update() as it is basically the same. You can use whatever method to encrypt your data whereever you keep it.


Method of Simplifying Reporting Errors/Success to Users

Since updating users to what is going on is so common, I use a simple group of extensions to simplify my life

There is most likely crap going on in your apps, so there will be success and errors. If the users don't see these things going on, they will assume nothing is. Therefore, it is in your best interest to keep them posted. Since you have to do this basically all the time, it makes sense to make it simple.

I output all of my stuff to an asp:panel control, so I decided just to extend it to handle these conditions.

using System;
using System.Web.UI;
using System.Web.UI.WebControls;

public static class Extensions
{
  public static void Error(this Panel pnl, Exception ex)
  { // reporting extension for Panel indicating error
    try { pnl.Report(false, "Error: " + ex.Message, ex.InnerException.Message); }
    catch { pnl.Report(false, "Error: " + ex.Message, null); }
  }

  public static void Success(this Panel pnl, string headline, string message)
  { // reporting extension for Panel indicating success
    pnl.Report(true, headline, message);
  }

  public static void Report(this Panel pnl, bool successful, string headline, string message)
  { //this is a helper used by the Success and Error extensions in extensions.cs - for ease of error/success reporting
    string msg = "<h5 class='" + (successful ? "success" : "error") + "'>" + headline + "</h5>";
    msg += !string.IsNullOrEmpty(message) ? "<div>" + message + "</div>" : "<br />";
    pnl.Controls.Add(new LiteralControl(msg));
    pnl.Visible = true;
  }
}

Nothing fancy here, Report just takes in a bool which switches the css class to 'success' if true, 'error' if false, and outputs the 'headline' in h5 tags and the 'message' in a smaller line underneath it. You can use this method by itself, but I also have the Error and Success classes to make it even easier for myself. Of course you have to set up your css to work with this, but it shouldn't be too hard. I will often use it like this:
try
{
  //do some crap here
  myReportPanel.Success("Success!", "whatever you did worked");
}
catch(Exception ex) { myReportPanel.Error(ex); }

And that is it, centralizing your error/success handling also makes it easier to change formatting and be consistent throughout your project. Not to mention it makes it extremely easy to use and implement.


new advertising affiliate 'the Lounge'

a great advertising group that focuses on .Net technologies specifically

I was recently lucky enough to be able to join the '.NET Open Source Room' on The Lounge Advertising Network with my open source project Slick-Ticket, and they seem to have a pretty awesome setup. I stumbled upon the lounge while look at Matt Berseth's page as he is one of the member's of their 'Web Publishers Room' which includes a lot of great minds in the .Net community.
The Lounge is an exclusive advertising network of trusted and respected publishers focused on Microsoft technologies

Reach our passionate audience with quality ad placements that deliver results and enhance your brand.

If you are interested in advertising or publishing, check them out; they were extremely fast and friendly in response!

An improved Email Class for .Net

Nothing too special, just combining a couple classes for a more logical approach to email

Most all of my websites/projects require some email to be sent. The System.Net.Mail class is great, but to me, sending an email is not a logical process. Also, the fact that you will likely only use one smtp server throughout a project seems to make the SmtpClient setup a bit repetetive to me. With my class you can setup and send an email as simply as this:
new Email("recipient@hotmail.com", "sender@gmail.com", "You stink", "here are the reasons why...").Send();

Now you wouldn't have to do it all in one line, but this demonstrates that the smtp is already taken care of in the background, this simply builds on the SmtpClient class. Here is the code:
using System;
using System.Net.Mail;

public class Email : SmtpClient
{
  public MailMessage Message { get; private set; }

  // keep in mind I don't recommend hard coding these values in, this is just for example

  public Email(string to, string from, string subject, string body) : base("smtp.gmail.com", 587)
  {
    // this next line is only necessary if you want to enable SSL
    this.Credentials = new System.Net.NetworkCredential("some_guy@gmail.com", "crap_nugget");
    this.Message = new MailMessage(from, to, subject, body);
  }

  public void Send()
  {
    try { this.Send(Message); }
    catch (Exception ex) { throw ex; }
  }
}

Notice that I do not recommend hard-coding your username/password and/or smtp server in the class -- but it will work. Also, this example is to use Gmail, but it will work just as well with another SMTP (I use this with a local Exchange server some times).

Just polishing up an already good tool and making it a bit better.




Setting the row background color in a GridView based on a value in the the row

This is something that needs to be done often, but it is not always obvious how to do it

Often times you want a row to be colored different or highlighted based on values, this can be useful for multiple reasons. This is a very simple thing, but not all that obvious.

Make your different CSS classes

Set up some CSS classes that will be used for the row coloring:
.blue { background:Blue; }
.orange { background:Orange; }
.normal { background:Transparent; }

Set your RowDataBound event in your GridView

This is going to happen on the RowDataBound event, so be sure to call one in your GridView:
<asp:Repeater ID="gv" onrowdatabound="gv_RowDataBound" ...

Set up your event to handle the row

Now handle each row in the event:
protected void gv_RowDataBound(object sender, GridViewRowEventArgs e)
{
  e.Row.CssClass = e.Row.Cells[0].Text.Equals("1") ? "blue" : "normal";
}

It's just that simple. Now, if the text in Cell[0] (the first cell) of a row is equal to "1" the row will have it's CssClass set to "blue", otherwise it will be "normal".

Now if you want to have multiple different cases:
protected void gv_RowDataBound(object sender, GridViewRowEventArgs e)
{
  switch(e.Row.Cells[0].Text)
  {
    case "1" : e.Row.CssClass = "blue"; break;
    case "2" : e.Row.CssClass = "orange"; break;
    default : e.Row.CssClass = "normal"; break;
  }
}

Now if the first cell is equal to "1", it will be blue, if it is equal to "2" is will be orange, or else it will be transparent; so simple!

You might be making a lot of unneccessary trips to your DB with Linq-to-SQL

Linq-to-SQL has great use of relations, but lazy loading can cause a huge amount of trips to your DB - implementing eager loading is one answer

After my rant on L2E, Dan from Microsoft got back to me in the comments and pointed out a lot of what I wanted to know. One thing that he brought up was the use of eager loading which I hadn't looked in to; turns out it can be VERY important!

Let's say you are using a GridView and for each row, you are using a relation to the 'states' table to pull the name of a 'state' object:
<asp:BoundField DataField="item.state.state_name" HeaderText="State Name" SortExpression="item.state.state_name" />

Now this will work just fine, but each time you add a row, there is going to be another call to the DB to get the state name. Now if you only have a few rows, no big deal, but what if you are calling 100 records each with a a call to the state name and a country name? It is no longer one trip to get the items, it grows huge! Here is some ugly psuedo-code for understanding:
ONE_SQL_QUERY -> 100_ROWS_RETURNED (1 trip)
FOREACH(ROW row in 100_ROWS_RETURNED)
{
  QUERY_FOR_STATE_NAME (1 trip)
  QUERY_FOR_COUNTRY_NAME (1 trip)
}

That one query has now jumped to 201 database calls (1+100(1+1)) ... that is no small difference! Now, that first query, no matter how big and ugly it is is dwarfed by these petty little calls for one small string. Just imagine what happens if you are calling multiple and nested relations, that one call can easily jump to well over 1000 in no time.

So what to do?

While searching on Linq-to-Entities (yes, I still want to make the switch some time) I came across this: http://blogs.msdn.com/adonet/archive/2008/10/07/migrating-from-linq-to-sql-to-entity-framework-eager-loading.aspx which shows how you deal with this in both L2S and L2E (also answered my last question on my rant against L2E. This is actually a situation where (I think) L2E shines over L2S, but that's a whole other debate. Here is what you would need to do.
dbDataContext db = new dbDataContext();
DataLoadOptions dlo = new DataLoadOptions();
dlo.LoadWith<item>(i => i.state);
dlo.LoadWith<item>(i => i.country);
db.LoadOptions = dlo;

What this is doing is telling your DataContext to load those other elements with your every 'item' you load (don't forget to include using System.Data.Linq;). This will obviously make for a larger query to begin with, but it will cut out those multiple return trips that are the real problem!

On an somewhat related note... I am going to force myself to use L2E on my next project.

Using jquery.autosuggest.js with Asp.Net

Simple Auto-Suggest with Asp.Net and jQuery

After getting fed up with the Ajax Control Toolkit's AutoCompleteExtender and it's inability to deal with strings that are numbers, I decided to look to my new friend jQuery. Apparently they decided for you if you are feeding it an array of number strings (i.e. { "001","002", etc.}) that the auto-suggest will strip all leading zeroes... BUT I NEEDED THOSE ZEROES!

The jQuery solution is pretty simple, much easier than I thought thanks to this awesome plugin: jquery.autocomplete.js. All you really need to do is make a simple aspx page that spits out the data you want based on a QueryString, use a little jquery to tie it up with a TextBox and you are all set. Here is how its done:


Make your data retrieval page

This plugin consumes it's data in one form: text, with one item per line. This makes it very easy to use and very versatile. You could simply point it at a static .txt file, feel it a javascript variable, or, what we are going to do, use Asp.Net to get you some filtered data.

This tutorial is going to use two examples, one pulling from a list of numbers 1-1000, and another pulling form an xml file.

First, the code-behind for number.aspx:
protected void Page_Load(object sender, EventArgs e)
{
  if (!string.IsNullOrEmpty(Request.QueryString["q"]))
  {
    int q = 0;
    if (Int32.TryParse(Request.QueryString["q"], out q))
    {
      for (int i = q; i < 1001; i++)
        if (i.ToString().Contains(q.ToString())) Response.Write(i + Environment.NewLine);
    }
    else
      Response.Write("Not a number fool!");
  }
}

Doesn't get much easier than that... simply writes out any numbers that contain the number (not mathematically, though we could do that) passed in the 'q' QueryString, so if you were to pass the url: number.aspx?q=100 you will get the results:
100
1000

simple.

Now we do the same thing, but dig into an xml file with Linq:
protected void Page_Load(object sender, EventArgs e)
{
  string q = Request.QueryString["q"] ?? string.Empty;
  IEnumerable<string> things = from p in XDocumentLoad(path).Descendants("thing")
        where p.Value.Contains(q) select p.Value;
  foreach(string s in things) Response.Write(s + Environment.NewLine);
}

That was actually the hardest part. Notice that on the Response.Write() I used Environment.NewLine - that will make a newline in the proper format for the jquery to digest, a <br /> or a \n will not work with this plugin. What was made is a psuedo-web-service (it's not really becuase it does not output xml).

Tie it to some TextBoxes

Now just make a couple TextBoxes that will use them:
<asp:textbox ID="txtNumbers" runat="server" />
<asp:textbox ID="txtThings" runat="server" />

Now use jQuery to attach the pages we made to the proper TextBox:

$().ready(function() {
  $("#txtNumbers").autocomplete("number.aspx");
  $("#txtThings").autocomplete("thingXml.aspx");
});

And that's it, you have working auto-suggesters; pretty simple. One thing to notice that can give you some headaches in Asp.Net is that I used the IDs for selection in the code, which is fine if you are not using User Controls or Masterpages, but if you are you get those funky '...ctl100_...' ids that are made at runtime, so you might want to select them with a different method, like making a dummy class and using that as a selector in jQuery

That's about it - but don't forget to style your auto-suggest box or it will just be transparent; there is some basic css showing the classes produced by the jquery in the example. This was just enough to get you started; there is a bunch of documentation and other variables, settings, formatting tricks that you can use (this is a really powerful plugin) - you can read up on them here: http://docs.jquery.com/Plugins/Autocomplete.

IMPORTANT: Remember to delete *everything* on your handling .aspx page other than the '@' declaration so you don't have any extra html in there (I think I left it in there on the download...).


Setting up Subversion (SVN) with Visual Studio walkthrough/tutorial

From no source control to full Subversion support in 10 minutes - for free

UPDATE Somehow I didn't realize that VisualSVN was only a trial version (thanks for pointing that out guys!) for free :P - but, there is a great free VS plugin (ANKHSVN) available here http://ankhsvn.open.collab.net/

Now if you don't already know, SVN is a great program for keeping track of all your changes in code... and it's what all the cool kids are doing (Google, CodePlex, etc.). Your code is all kept in the repository, if you or someone wants to use it, they check it out Not to mention it is incredibly useful for teams and will save you hours/days if your coding takes a wrong turn and you need to move backwards. Bottomline is: if you are not using it, start!

There are now a bunch of free SVN providers out there, and all you really need is a client installed on your machine and you are good to go. There are a lot of great clients out there, I prefer using VisualSVN to tie together the functionality of TortoiseSVN with the UI of Visual studio (though Tortoise is great on its own as well). As for host, I actually prefer OpenSVN as ghetto as it may appear... is a quite humble site, but it is incredibly useful and FREE! Don't be fooled by its 1990 styling and clunky interface... it is there to help! Most important it is simple and to the point. Here is a walkthrough on how to set up your OpenSVN account, and get it integrated with Visual Studio 2008.

Set up your OpenSVN account

First thing is first, go to OpenSVN and sign up for your account. All you need is a project name, a couple email addresses and click Go!.

Next you will have to check your email and retrieve your password; now go back to OpenSVN, click on 'Manage Your Project' and enter your username/password.

Now that you are in take notice of the project location urls, we will need those later, also, I would recommend changing your password to something you will remember. Then click on 'Access Control' link on the top and add at least one new user.

Now you are all done with OpenSVN and you really never have to go back unless you are adding new users.

Install the Required Programs on your Machine

There are a few options here, but I prefer to use the tag-team of VisualSVN and TortoiseSVN in combination with Visual Studio, it works flawlessly!

TortoiseSVN is a standalone client that integrates directly with windows and does not neet Visual Studio, you can use this by itself if you want, but I prefer to also add VisualSVN as it ties Tortoise and VS together.

Install Tortoise first (this will require a reboot) then install VisualSVN. Now that is all done... simple.

Putting it all together

Ok, now the last part which is the hardest, and it is still incredibly simple.
  • Open an existing website in Visual Studio and you will see a new menu: 'VisualSVN' click on it and go down to 'Add Solution to Subversion'
  • A popup will come up, check 'I will set working copt root manually' and click 'Next'
  • This is the important part: Make sure that you select the correct folder that your project is in! Then click 'Next'
  • Choose 'Exisiting Repository' and click 'Next'
  • In the 'Destination URL:' box, type in the project location url from above (I prefer to use the https one)
  • It will prompt you for username/password, this is the user that you made up above, then click 'OK'
  • After verification, it will ask you if you want to import your new files (since the repository is empty) click 'Import' - this may take a bit
  • And... your done, everything should be working correctly now - you can use either Visual Studio or Tortoise to keep your project up to date


Finished


And that's it, you are set up to use SVN. There are simple buttons in VS that allow you to commit and update. Commit will push your changes to the repository, while update will pull the most recent copies in the repository to your local directory. Also, you will notice that you will be able to see the file/folder status of the items in your solution in the Solution Explorer (green=current, yellow=needs commit, etc.) -- great integration with VS. Keep in mind that you can also do commits, updates and all other operations in Windows with the Tortoise interface which is built in - just right click on a file or folder and look in the 'TortoiseSVN' menu.

Now that you have SVN working, there is a great free online book all about it: http://svnbook.red-bean.com/en/1.5/svn-book.html.

Other Free SVN Host Providers

Oddly enough, OpenSVN does not dazzle everyone, there are more out there, I just prefer the simplicity it brings. These are all available for free, though you have to pay for upgraded usage:

Shout it kick it on DotNetKicks.com