Check Duplicate User Names In ASP.NET Web Pages Sites

The Web Pages Starter Site template provides a useful starting point for developing a Razor Web Pages site that includes membership. But it doesn't include any mechanism for preventing duplicate user names. This article offers one solution to the problem that uses jQuery.

If you ask how to prevent duplicate user names in forums, one of the suggestions that is often put forward is to apply a unique constraint in the database column that holds the user name. Any attempt to submit a duplicate value will result in an exception being raised in the relevant database provider. You can catch this exception and show the user an appropriate message. This works - and you are always advised to add unique constraints to database columns that should hold unique values - but it's a fairly clunky solution. And many people feel that you should not use exceptions as a means to manage your business rules (myself included).

The solution featured in this article uses AJAX to query the database and to give the user immediate feedback when they enter their chosen user name. The AJAX call requests a page that exists purely to query the database to see if the selected user name is already in use. The solution also includes a server side chekc to ensure that users who have disabled JavaScript so not slip through the net. The solution requires a couple of amendments to the Register.cshtml file in the Starter Site, and the addition of 3 files. But first, the changes to the Register.cshtml page. The first change is in the inclusion of a JavaScript file called dupecheck.js (which will be created a bit later):

@* Remove this section if you are using bundling *@
@section Scripts {
    <script src="~/Scripts/jquery.validate.min.js"></script>
    <script src="~/Scripts/jquery.validate.unobtrusive.min.js"></script>
    <script src="~/Scripts/dupecheck.js"></script>
}

And the second is the server side check to see if the username is in use:

if(Functions.IsDuplicate(email)){
    ModelState.AddError("email", "User name is already taken");
}

I placed this in the if(IsPost) section just after the initial variables that represent the submitted values (email, password and confirmPassword) are declared. The code calls a function named IsDuplicate. The function is declared in a file called Functions.cshtml which is placed in a folder called App_Code:

@functions {
    public static bool IsDuplicate(string username){
        var db = Database.Open("StarterSite");
        var commandText = @"SELECT COUNT(Email) FROM UserProfile WHERE Email = @0";
        return (int)db.QueryValue(commandText, username) > 0;
    }
}
Note that the name of the folder is important. The function returns a bool. The value of the bool is determined as a result if the SQL query which gets a count of the rows containing the provided user name. By default, the Starter Site uses a column called Email in the UserProfile table for the storage of user names. This function is also called in a separate file named DupeCheck.cshtml. This file is placed in the root of the site:

 

@{
    Layout = null;
    if(IsAjax){
        var username = Request["username"];
        var result = Functions.IsDuplicate(username);
        Json.Write(new { isDupe = result }, Response.Output);
    }
}

DupeCheck.cshtml is designed to work exclusively with AJAX. The code includes an instruction to nullify any layout pages that might have been set in a _PageStart file, and then it uses the IsAjax property to determine if the page has been requested via an AJAX call. If it has, it uses the IsDuplicate method to check the availability of the posted username and returns the result to the calling code. The result is an anonymous type that has one propery: isDupe, which is a boolean. The anonymous type is serialised to JSON by the Json helper.

The final part of the solution is the dupecheck.js file. This uses jQuery:

$(function () {
    $('#email').change(function () {
        $.post(
            '/DupeCheck',
            { username: $(this).val() },
            function (data) {
                var emailValidation = $('span[data-valmsg-for="email"]');
                if (data.isDupe) {
                    if (emailValidation.hasClass('field-validation-valid')) {
                        emailValidation.removeClass('field-validation-valid');
                        emailValidation.addClass('field-validation-error');
                        emailValidation.text('That name is already taken!');
                    }
                } else {
                    if (emailValidation.hasClass('field-validation-error')) {
                        emailValidation.removeClass('field-validation-error');
                        emailValidation.addClass('field-validation-valid');
                        emailValidation.text('');
                    }
                }
            },'json'
        );
    });
});

An event handler is attached to the change event of the user name input (which has an id if email in the Starter Site). The current value is posted to the DupeCheck.cshtml page via AJAX. The code above checks the response from the server to see if the value is a duplicate, and if it is, an appropriate error message is displayed to the user.

And there it is - a clean way to provide users with immediate feedback on the availability of their chosen user name.

 

Date Posted:
Last Updated:
Posted by:
Total Views to date: 21046

9 Comments

- Fish

Why do the "IsAjax" check?
This somehow related to security? And if how how does it become more secure?

- Mike

@Fish

As it says in the article, the IsAjax property returns true if the request was made by AJAX. That means that people can't simply put yourdomain.com/DupeCheck in their browser and request the file. Also, by default, cross origin AJAX requests are not allowed, so it acts as protection against misuse.

- satyabrata

Nice post.very helpful.

- Kelvin

But isn't dere a way to check if the user's exist without posting d form . becos u can actually if a user exists server side without using ajax .wanted a client side script to achieve this

- Mike

@Kelvin

I have no idea what you are asking. Did your cat walk across the keyboard as you were typing?

- Gautam

Thank you for this post Mike!

But ii have a doubt reg starter site, u said it doesn't check for duplicate user name. But I checked it does. Before adding the user, it queries the database to chk for user(email) ..
If user == null, then only it proceeds otherwise it says "user exists'

- Mike

@Gautam

As far as I can remember, the first version of the site didn't, but the current one does.

- Gautam

Hi Mike,

In the below line

return (int)db.QueryValue(commandText, username) > 0;

why the (int)... is it type casting? if yes, the why is it required. I removed it and tested, query returns an interger only

- Mike

@Gautam,

In this case the cast probably isn't necessary, but Database.QueryValue returns a dynamic type, not an integer. The runtime binder is responsible for choosing an appropriate type.
Add your comment

If you have any comments to make about this article, please use this form to do so. Make sure that your comment relates specifically to the article above. More general comments can be posted through the form on the Contact page.

Please note, all comments are moderated, and I end up deleting quite a lot. The kind of things that will ensure your comment is deleted without ever seeing the light of day are as follows:

  • Requests to fix your code (post a question to forums.asp.net instead, please)
  • Gratuitous links to your own site or product
  • Anything abusive or libellous
  • Spam

I do not pass email addresses on to spammers, so a valid one will assist me in responding to you personally if required.

Recent Comments

hosein ey 2/25/2015 1:56 PM
In response to ASP.NET MVC 5 with EF 6 - Working With Files
tnx for this article do you think next version of entityframework support's the sql server and ?...

Saywer Ford 2/25/2015 5:15 AM
In response to Optimising ASP.NET Web Pages Sites - Bundling And Minification
Great Article. I did everything right and working fine. How about page specific js files. Lets I...

Saravanan 2/24/2015 10:54 AM
In response to Optimising ASP.NET Web Pages Sites - Bundling And Minification
Hi, Great article about the Bundling and minification. Regards the caching as you mentioned the...

Justin 2/24/2015 10:43 AM
In response to Scheduled Tasks In ASP.NET With Quartz.Net
I'm having the same problem as Ingmar. Everything is working fine locally in Visual Studio, but I it...

Logan Mudlo 2/23/2015 4:59 PM
In response to WebMatrix - A First Application
Is there a way to prevent the automatic close on a Database.Open() call?...

Mog0 2/23/2015 11:16 AM
In response to ASP.NET MVC 5 with EF 6 - Working With Files
Probably a silly question but why did you disable cascade delete and implement it yourself?...

Jose-Maria 2/19/2015 3:29 PM
In response to Migrating From Razor Web Pages To ASP.NET MVC 5 - Views and Controllers
Just great ! Many thanks Mike....

Satyabrata Mohapatra 2/19/2015 12:02 PM
In response to Migrating From Razor Web Pages To ASP.NET MVC 5 - Views and Controllers
This is great!!...

Ryan Helmoski 2/19/2015 10:24 AM
In response to Conversion of a datetime2 data type to a datetime data type resulted in an out-of-range value
Thank you!...

Harsha 2/19/2015 8:27 AM
In response to iTextSharp - Adding Text with Chunks, Phrases and Paragraphs
Hi, Can text area be created so that I can type text in the area in pdf document? We have Add text...