Saturday, January 26, 2008

Linq - Start Using It and the Single method explored

Let's start off by registering users on your website or you application. You have a very simple form where the user can enter his desired username n password. We will validate if that username is already in use and also make sure that the password is in the required format.

I am gonna write up some back-end code for a web application when the user enters his information and click on the submit button.


protected void btnRegisterUser_Click(object sender, EventArgs e)
{
String _username = tbUsername.Text.Trim().ToLower().Replace("'", "''");
String _password = tbPassword.Text.Trim().ToLower().Replace("'", "''");

MemberDataContext MemberDC = new MemberDataContext();
Member new_member = new Member();
new_member.Username = _username;
new_member.Password = _password;
MemberDC.Members.InsertOnSubmit(new_member);
MemberDC.SubmitChanges();
}

I had a tough time inserting data in the database since all the examples out there don't include the line I marked in bold. The line tells the MemberDataContext to insert the new_member object in the Members table. However, the data is not saved until MemberDC.SubmitChanges() is not executed.

This was the basic example. However, you might not want two users to use the same username on the system. So, you will want to perform validation. From experience, I suggest using the Single method from the collection of LINQ functions. The Single method always tries returns a single row from the database. If the result of the query returns zero or more than one row, an exception is thrown.

I will modify the btnRegisterUser_Click method to reflect the username validation.


protected void btnRegisterUser_Click(object sender, EventArgs e)
{
MemberDataContext MemberDC = new MemberDataContext();
String _username = tbUsername.Text.Trim().ToLower().Replace("'", "''");
try
{
var member = MemberDC.Members.Single(u => u.Username == _username);
//The username is already in use
return;
}
catch (Exception ex) {}

String _password = tbPassword.Text.Trim().ToLower().Replace("'", "''");
Member new_member = new Member();
new_member.Username = _username;
new_member.Password = _password;
MemberDC.Members.InsertOnSubmit(new_member);
MemberDC.SubmitChanges();
}


Its easy to understand what will happen when a user tries to register an existing username. The Single method will not throw an exception and that means u have to return an error.

You could use the same concept to login a member.

protected void btnLoginSubmit_Click(object sender, EventArgs e)
{
String _username = tbUserName.Text.Replace("'", "''").Trim();
String _password = tbPassword.Text.Replace("'", "''").Trim();

try
{
MembersDataContext MemberDC = new MembersDataContext();
Member member = MemberDC.Members.Single(m => m.Username == _username && m.Password == _password);
FormsAuthentication.RedirectFromLoginPage(member.Username.ToString(), true);
}
catch(Exception ex)
{
//Unsuccessful attempt
errorText.Text = "Invalid username/password combination.";
}
}


Earlier in the post, I spoke about validating the password too. There are 2 options.

1. Modify Member.designer.cs (bad idea bcuz of this warning)
//------------------------------------------------------------------------------
//
// This code was generated by a tool.
// Runtime Version:2.0.50727.1433
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
//

//------------------------------------------------------------------------------


2. Exploit the concept of partial classes.
We will create a class file Member.cs and add the following code.


using System;
using System.Data;
using System.Configuration;
using System.Linq;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Xml.Linq;
using System.Text.RegularExpressions;

namespace LinqDemo
{
public partial class Member
{
partial void OnPasswordChanging(string value)
{
Regex new_password = new Regex("^[A-Z].*?[0-9]$");
if (new_password.IsMatch(value) == false)
{
throw new Exception("Password does not start with a Capital Letter and end in a number");
}
}
}
}

Note, the namespace marked in bold. If you are missing that, make sure you define the name space in this file as well as Member.deginer.cs. Absence of that code would not allow the compiler to combine both the partial classes. Also, note the partial modifier on the me OnPasswordChanging method. This is a new feature in .net 3.0. More about partial methods here.

Next up, exploring LINQ across relations.

No comments: