My goal in this short piece is to provide an example of how to pull them all together.
We'll use the standard Membership Provider for authentication, the standard
Role provider for Authorization, and we'll add a completely custom Profile
example to show how custom user Profile data can be stored on a per-user basis,
even for anonymous users, and then automatically migrated to their full User
profile when they actually "sign up" on your site.
You can set this up with any SQL Server database you want, including either a new
or existing one. Plus, I'll include a SQL Script that will show you how to
install Membership, Role and Profile required database information on a hosted
site, and even an ASP.NET "SetupASPNETDatabase.aspx" page that does
all this programmatically - something many developers aren't even aware exists.
Finally, I'll include some nice "Admin" pages, courtesy of my good
friend and fellow MVP Peter Kellner, to allow Administrators to work with users and roles online, and point you to some
additional resources that have become available.
In short, you'll have a basic example code framework and controls to do just
about anything you would expect to need to perform in ASP.NET for a site, using
all three providers - Membership, Role, and Profile. So, let's get started.
The first thing we want to do before we even open the sample solution is to enable
our database for the providers. There are actually three separate ways you can
1) Run ASPNET_REGSQL and follow the prompts. Of course, this requires that you have
command-prompt access to the machine on which your site will be deployed. On my x64 machine, this can be found in C:\Windows\Microsoft.NET\Framework64\v4.0.30319
2) Run ASP.NET_REGSQL on your own sample database locally, and EXPORT all the sql
script necessary to create the tables, views and stored procedures on a remote
database. Again, this assumes you have the ability to access a remote hosted
site's SQL Server online and execute a rather large SQL Script on it. A sample
script is included in the "SQL" Folder in the download. If you can
run this against your remote hosted SQL Server Database, that will be fine. It's
important to understand that the ASPNETDB.MDF default database that is provided
with an ASP.NET Web Application is not necessary - you really should and would
want to install all the tables and stored procs in your own site database, and
not use this default one at all. You can export complete script for a database with SQL Publishing Wizard. Mine is at C:\Program Files (x86)\Microsoft SQL Server\90\Tools\Publishing\1.4\SqlPubWiz.exe . If you do not have this, you can download and install it.
3) Do it programmatically: Make a "Setup.aspx" page that uses the System.Web.Management
Management.SqlServices.Install("server", "USERNAME", "PASSWORD",
This does everything that ASPNET_REGSQL does. You'd think they would make it
more obvious that you can do this, given the large number of sites that are hosted
by commercial shared hosting companies, but they decided to push ASPNET_REGSQL
as if everybody everywhere automatically has access to it. There is a page "SetUpASPNetDatabase.aspx"
in the sample solution where you can simply fill in the above parameters in a
form, and press a button to set up your database with ASP.NET Membership, Roles
and Profile. You would only need to run this page "one time".
So, now we have our database (either SQL Server 2005 / SQLEXPRESS or SQL Server 2008
/ SQLExpress) set up for Membership, Roles and Profiles. The rest is just figuring
out what code and what controls to use to make everything work. Fortunately,
ASP.NET 2.0 and higher provide a group of controls that handle most of these
tasks out of the box and can also be highly customized beyond their default behaviors.
The easiest way to review this process is to go through it in stages with the sample
application open in Visual Studio.NET 2010. So, unzip the sample into your favorite
folder, and double-click on the "MembershipRolesProfile.sln" solution
file to bring it all up. This is configured as a Web Application Project.
The sample database for this exercise is "Articles" so go ahead and create
a new database of that name now, and then run the "TablesSprocAndData.sql"
Sql script in the "SQL" subfolder against it. This creates the table
and stored procs that are used in the sample, so that we don't need to "reinvent
the wheel". Next, if you haven't set up your new database for Membership,
Roles and Profiles, you can either execute the stock "ASPNETREGSQLFULL.sql"
script that I have prepared as in "2" above, or you can "View
In Browser" on the SetupASPNETDatabase.aspx page in the solution to see
how the option "3" above works. Fill in Server, Username, password
and database names, and press the "Set Up Database" button, and the
SqlServices.Install Method will be run. Finally, make sure the connection strings
in the web.config actually work for your specific environment, otherwise you
won't get very far.
At this point, all your infrastructure for the sample is set up, and we are ready
to look at some code. If you look at the Default.aspx page, it has a Repeater
to display content, and sports a LoginStatus control pointing to "~/Login.aspx"
as its login/ logout page action. Now let's move to the Login.aspx page.
You can see that Login.aspx has only a Login Control. The property settings on this
control are where we can take advantage of our Membership Provider automatically.
In this case, our MembershipProvider is set to "DefaultMembershipProvider",
and whatever we have in our web.config for that now takes over. In my codebehind
for this control, I have only the following:
protected void Login1_Authenticate(object sender, AuthenticateEventArgs e)
string userName = Login1.UserName.ToString();
string password = Login1.Password.ToString();
if (Membership.ValidateUser(userName, password))
if (Request.QueryString["ReturnUrl"] != null)
lblResults.Visible = true;
lblResults.Text = "Unsuccessful login. Please re-enter your information and try again.";
if ((Membership.GetUser(userName) != null) &&
(Membership.GetUser(userName).IsLockedOut == true))
lblResults.Text += " Your account has been locked out.";
So, I am using the Login Control to employ the Membership Provider's "ValidateUser"
method, with Forms Authentication, which is already set up in the web.config.
Obviously, if you aren't a "member" yet, you'll want to click the
"Create Account" link on the Login Control, and this will take you
to the "Admin.aspx" page, where we have a CreateUserWizard, a ChangePassword,
and a PasswordRecovery control. All the settings on these are highly configurable;
in most cases you will not need to write any code to use them. In my CreateUserWizard
Control, I am using the following codebehind:
protected void CreateUserWizard1_CreatedUser(object sender, EventArgs e)
string username = this.CreateUserWizard1.UserName;
If the User Role doesn't exist yet (first time the app is ever used) we create
one, and then we Add this user to the "user" role. The rest of the
CreateUserWizard process doesn't require any custom code - its automatic,
based on the property settings of the control. You can check for and add additional
roles here if you like. The only thing we have not done is to make the user an
"Admin" role user, and that's why I have commented out the <allow
roles... element in the Admin section web.config. Once you have an Admin user
(you can do this in the Admin section) you can create an /Admin folder with your
admin-only pages and provide a separate web.config in it to only allow Admin
users into this section of the site.
Now let's take a look at some Profile Features. Start the application, but do
not log in or create an account. Go to the Profile page, and fill in some Profile
data. Then, press the Get Profile Values button to see that your Profile data
has been saved as an anonymous user. You can close your browser, and come back
again, and see that we are indeed holding Profile data for you, even as an anonymous
user. Now, create a new account for yourself and log in. Then, go to the Profile
page again, and you will see that we have migrated your anonymous data to your
new "real member" Profile. This is done in the new "OnMigrateAnonymous"
event which fires in Global.asax:
public void Profile_OnMigrateAnonymous(Object sender, ProfileMigrateEventArgs args)
if (Profile.LastActivityDate == DateTime.MinValue)
Profile.UserDetails.Address = anonymousProfile.UserDetails.Address;
Profile.UserDetails.City = anonymousProfile.UserDetails.City;
Profile.UserDetails.State = anonymousProfile.UserDetails.State;
Profile.UserDetails.Zip = anonymousProfile.UserDetails.Zip;
Profile.UserDetails.Email = anonymousProfile.UserDetails.Email;
Profile.UserDetails.Phone = anonymousProfile.UserDetails.Phone;
Profile.UserDetails.GetNewsletter = anonymousProfile.UserDetails.GetNewsletter;
// delete the anonymous user data, user is no longer anonymous
//delete the anonymous cookie so this event no longer fires for a logged-in user:
Now, let's take a look at the web.config setup for all this, and how it works:
<add name="LocalSqlServer" connectionString="server=localhost;database=Articles;Integrated Security=SSPI" providerName="System.Data.SqlClient"/>
<add key="articleFolder" value="Articles"/>
<add name="State" type="System.String" allowAnonymous="true"/>
<add name="Email" type="System.String" allowAnonymous="true"/>
<add name="Address" type="System.String" allowAnonymous="true"/>
<add name="City" type="System.String" allowAnonymous="true"/>
<add name="Zip" type="System.String" allowAnonymous="true"/>
<add name="Phone" type="System.String" allowAnonymous="true"/>
<add name="GetNewsletter" type="System.Boolean" allowAnonymous="true"/>
<!-- membership provider -->
<roleManager enabled="true" cacheRolesInCookie="true" createPersistentCookie="true">
<add applicationName="/" connectionStringName="LocalSqlServer" name="DefaultRoleProvider" type="System.Web.Security.SqlRoleProvider"/>
<remove name="ASPNETSqlMembershipProvider" />
<add connectionStringName="LocalSqlServer" enablePasswordRetrieval="true" enablePasswordReset="true"
requiresQuestionAndAnswer="false" applicationName="/" requiresUniqueEmail="true"
passwordFormat="Clear" minRequiredPasswordLength="6" minRequiredNonalphanumericCharacters="0" passwordStrengthRegularExpression="" name="DefaultMembershipProvider"
<compilation debug="true" targetFramework="4.0">
<forms name=".SITE1" loginUrl="Login.aspx" protection="All" timeout="30" path="/" requireSSL="false" slidingExpiration="true" defaultUrl="Default.aspx" enableCrossAppRedirects="true"/>
<pages controlRenderingCompatibilityVersion="3.5" clientIDMode="AutoID"/>
In the beginning, I start with the ConnectionStrings section, removing the Default
LocalSqlServer Provider (which defaults to SQLExpress) so that I can add back
in my own, specifying the connection string and database.
Next, I specify that anonymousIdentification is enabled. This is what lets us store
Profile Data for the anonymous users, and migrate it to their permanent Profile
when they join the site. Then, I have my profile section where I actually set
up the profile items I want to store. If you look in the MembershipUtilities
project in the solution, you'll see the CustomProfile.cs class I've created
and how easy it is to override the ProfileBase class. In particular, notice that
I've decorated each item with the [System.Web.Profile.SettingsAllowAnonymous(true)]
Next, I specify the Membership Provider, and it this case I am using the DefaultProvider.
Next, the Role provider. Finally I have my Forms Authentication setup. The last
entry is a location path block to allow only Administrators access to the SamplePages
folder. Originally I was going to write up a page to manage Members and Roles,
but then I found Peter Kellner's nice workup and sample on this, so in the
interest of my credo "Don't reinvent the wheel", I've included
those pages in the app. You can, of course, comment that section out temporarily
to allow you access so you can make yourself an Administrator after you log into
your own site.
By the way, for those Master Mechanics who are interested in getting their hands
greasy, Scott Guthrie announced some time ago the download of the sample Provider
Toolkit, but since that time developers have written a whole slew of custom
providers. This search on Codeplex.com will help you find most of them. There are currently FIFTY ONE separate offerings
of various providers for Membership, Roles and Profile on Codeplex.com alone
- just in case you are wondering if this framework has broad acceptance in the
Finally, be advised that this is not a complete website -- it is just a sample application
that was designed solely to illustrate how to pull together the various providers
and make them work together. As such, I wouldn't recommend using the solution
as the basis for a real web site, but rather, as a test bed to play around with
and get used to how the various parts work.
I hope this effort at bringing together examples of how the Membership, Roles and
Profile providers work together, in a single article, has been useful to you.
You can download the complete Visual Studio 2010 Solution here.