How to Create SQL Membership Schema at Run-Time
ASP.NET Forms authentication with SQL Server used to store user's information is very popular way to secure web site. There are numerous tutorials on Internet that explain how to create tables, views and procedures for SQL membership using ASPNET_REGSQL tool. ASPNET_REGSQL is easy to use, but it is not available in every case. If you create ASP.NET web applications, like portal or blog, for users worldwide, you can't expect that your clients who are not programmers know how to use ASPNET_REGSQL. Also, if you have a lot of web sites it is better to automate things and avoid repetitive work.
More efficient approach is to create Setup page where you can install SQL membership schema programmatically with ASP.NET server side code. You can do this with SqlFeatures class from System.Web.Management namespace. To see how it works, create new web site and place Button control on web form, named "btnInstallMembership". On button's click event, SQL schema will be created in selected database:
[ C# ]
using System;
using System.Configuration;
using System.Web.Management;
public partial class Admin_Setup : System.Web.UI.Page
{
protected void btnInstallMembership_Click(object sender, EventArgs e)
{
try
{
// Installs SQL membership schema to selected database
SqlServices.Install(
// first parameter is database name
"your_database_name",
// we'll select installing of all features
SqlFeatures.All,
// get connection string from web.config
ConfigurationManager.ConnectionStrings["Your-Connection-String-Name"].ConnectionString);
// Informs user that schema is installed successfully
Response.Write("SQL membership schema installed successfully");
}
catch(Exception ex)
{
// Something went wrong, get exception message
Response.Write(ex.Message);
}
}
}
[ VB.NET ]
Imports System
Imports System.Configuration
Imports System.Web.Management
Partial Class Default2
Inherits System.Web.UI.Page
Protected Sub btnInstallMembership_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles btnSendEmail.Click
Try
' Installs SQL membership schema to selected database
' first parameter is database name
' in second parameter we'll select installing of all features
' third parameter is connection string that we'll take from web.config
SqlServices.Install( _
"your_database_name", _
SqlFeatures.All, _
ConfigurationManager.ConnectionStrings( _
"Your-Connection-String-Name").ConnectionString)
' Informs user that schema is installed successfully
Response.Write("SQL membership schema installed successfully")
Catch ex As Exception
' Something went wrong, get exception message
Response.Write(ex.Message)
End Try
End Sub
End Class
As you see, installing of SQL membership schema requires only one call of the SqlServices.Install method.
SqlServices.Install method has three overrides:
1. SqlServices.Install(string database, SqlFeatures features, string connectionString)
database - SQL Server database where we want to install objects
features - specifies which features to install, possible self-descriptive values are:
- SqlFeatures.All
- SqlFeatures.Membership
- SqlFeatures.None
- SqlFeatures.Personalization
- SqlFeatures.Profile
- SqlFeatures.RoleManager
- SqlFeatures.SqlWebEventProvider
connectionString - connection string to Sql Server, used only to connect to server not to specify database. To choose database, use database parameter. If you have database selected in connection string, it will be ignored.
2. SqlServices.Install(string server, string database, SqlFeatures features)
server - Sql Server name or address
database - database name where SQL schema will be installed
features - features to install
3. SqlServices.Install(string server, string user, string password, string database, SqlFeatures features)
server - server name or address
user - database user name
password - user's password
features - features to install
Possible permission error when you try to install Sql membership schema
Using of SqlServices.Install method requires AspNetHostingPermission. This permission is often denied by hosting companies, especially if you are on shared hosting. If you try to install SQL schema with limited rights you'll get an error like this:
Server Error in '/' Application.
Security Exception
Description: The application attempted to perform an operation not allowed by the security policy. To grant this application the required permission please contact your system administrator or change the application's trust level in the configuration file.
Exception Details: System.Security.SecurityException: Request for the permission of type 'System.Web.AspNetHostingPermission, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' failed.
The possible solution is to ask your hosting provider to increase permissions for your web application. But, many hosting providers refuse to do this because of security risk. Fortunately, there is a possibility to execute needed SQL scripts manually and install SQL membership schema even on shared hosting with medium trust. Instead of direct use of SqlServices.Install method, you can use SqlServices.GenerateApplicationServicesScripts
method to get needed SQL code and then execute SQL script against your database.
Notice that these SQL scripts are also easily accessible on local disc of your development computer too. GenerateApplicationServicesScripts method only returns content of several .sql files located usually in c:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\ folder (or C:\WINDOWS\Microsoft.NET\Framework\v4.0.21006 if you use ASP.NET 4.0). Depending of do you want to install or uninstall Sql membership schema, you need to execute SQL scripts with Install or Uninstall prefix.
These SQL scripts contain GO keyword so SQL can't be executed with standard SqlCommand.ExecuteNonQuery() method. There are two possible solutions for this. You can split script to sub queries without GO keywords and then execute them in sequence with SqlCommand.ExecuteNonQuery method, or use SQL Server Management Services. I found the second option much simpler to implement but it requires FullTrust for your code. Let's see how both methods works:
Installing SQL membership schema with Server Management Objects (SMO)
First, add reference in your project to Microsoft.SqlServer.Smo.dll and Microsoft.SqlServer.ConnectionInfo.dll files (you can do this through menu, go Website -> Add Reference... and add these files. Notice they are not part of core .Net Framework. You can find them in Microsoft SQL Server/100/SDK/Assemblies/ folder). In this example, I'll use web form with one Button control named btnInstallSqlMembership. Here is the code that installs SQL membership schema on site with FullTrust permission:
[ C# ]
using System;
using System.Collections.Generic;
using System.Web.Management;
using System.Data.SqlClient;
// Located in Microsoft.SqlServer.Smo.dll file
using Microsoft.SqlServer.Management.Smo;
// This assembly you can find in Microsoft.SqlServer.ConnectionInfo.dll
using Microsoft.SqlServer.Management.Common;
public partial class Admin_Test : System.Web.UI.Page
{
protected void btnInstallSqlMembership_Click(object sender, EventArgs e)
{
// get SQL script for manual installation of SQL membership schema
string SqlScript = SqlServices.GenerateApplicationServicesScripts(true,
SqlFeatures.All, "your-database-name");
// Establish connection to SQL Server database
SqlConnection conn = new SqlConnection(
ConfigurationManager.ConnectionStrings[
"Your-Connection-String-Name"].ConnectionString);
// Declare server object from Server Management Objects (SMO)
Server server = new Server(new ServerConnection(conn));
// Execute SQL script that contains GO keywords
server.ConnectionContext.ExecuteNonQuery(SqlScript);
}
}
[ VB.NET ]
Imports System
Imports System.Configuration
Imports System.Web.Management
Imports System.Data.SqlClient
' Located in Microsoft.SqlServer.Smo.dll file
Imports Microsoft.SqlServer.Management.Smo
' This assembly you can find in Microsoft.SqlServer.ConnectionInfo.dll
Imports Microsoft.SqlServer.Management.Common
Partial Class Default2
Inherits System.Web.UI.Page
Protected Sub btnInstallSqlMembership_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles btnSendEmail.Click
' get SQL script for manual installation of SQL membership schema
Dim SqlScript As String = SqlServices.GenerateApplicationServicesScripts(True, _
SqlFeatures.All, "your-database-name")
' Establish connection to SQL Server database
Dim conn As SqlConnection = New SqlConnection( _
ConfigurationManager.ConnectionStrings( _
"Your-Connection-String-Name").ConnectionString)
Dim server As Server = New Server(New ServerConnection(conn))
server.ConnectionContext.ExecuteNonQuery(SqlScript)
End Sub
End Class
If your web site runs in Medium Trust you can't use this option because these dlls don't allow calls from partially trust callers. But, you can run code on your localhost to see how it works and review SQL returned from GenerateApplicationServicesScripts method, e.g. you can execute code against remote SQL Server, store value of SqlScript variable to TextBox, save it as .sql file to disc etc.
Installing Sql Membership schema with ASP.NET code in Medium Trust
Previous example is very simple, but in Medium Trust level you can't use SqlServices or ConnnectionContext. But, you can execute required SQL scripts using simple SqlCommand object. In this example, required SQL scripts are uploaded to web server and placed in /SQL subfolder. /SQL subfolder will contain these files:
InstallRoles.sql
InstallCommon.sql
InstallMembership.sql
InstallPersonalization.sql
InstallProfile.sql
InstallWebEventSqlProvider.sql
Code example bellow will load these files and execute them against chosen database. Unlike previous code, this is more complex but it works on shared hosting with limited permissions:
[ C# ]
using System;
using System.Configuration;
using System.Data;
using System.Data.SqlClient;
// We need this namespace to read .sql files from disc
using System.IO;
public partial class Admin_Setup : System.Web.UI.Page
{
protected void btnInstallMembership_Click(object sender, EventArgs e)
{
// Declare list of .sql files,
// notice that InstallCommon.sql must be installed first
string[] fileNames = {"InstallCommon.sql",
"InstallRoles.sql",
"InstallMembership.sql",
"InstallPersonalization.sql",
"InstallProfile.sql",
"InstallWebEventSqlProvider.sql"};
string SqlQuery = "";
string CurrentLine = "";
SqlConnection conn = new SqlConnection(
ConfigurationManager.ConnectionStrings[
"connection-string-name"].ConnectionString);
SqlCommand comm = new SqlCommand();
conn.Open();
SqlTransaction trans = conn.BeginTransaction();
comm.Connection = conn;
comm.Transaction = trans;
comm.CommandType = CommandType.Text;
// Iterates through all files
for (int i = 0; i < fileNames.Length; i++)
{
// Open current SQL script from SQL/ sub folder
string FileContent = File.ReadAllText(Server.MapPath("SQL/") + fileNames[i], System.Text.Encoding.UTF8);
// replace default database name 'aspnetdb' to name of your database
FileContent = FileContent.Replace("aspnetdb", "your-database-name");
// Loads string to StreamReader
MemoryStream ms = new MemoryStream(System.Text.Encoding.UTF8.GetBytes(FileContent));
StreamReader sr = new StreamReader(ms);
while (!sr.EndOfStream)
{
CurrentLine = sr.ReadLine();
// Check if line is empty
if (CurrentLine != null && CurrentLine != "")
{
if (CurrentLine.Trim().ToUpper() != "GO")
{
// Build Sql to execute
SqlQuery += CurrentLine + "\n";
}
else
{
// Current line is 'GO' so execute code chunk
comm.CommandText = SqlQuery;
try
{
comm.ExecuteNonQuery();
}
catch (Exception ex)
{
Response.Write(ex.Message);
try
{
trans.Rollback();
}
catch (Exception exception)
{
Response.Write("<br />" + exception.Message);
}
}
SqlQuery = "";
}
}
}
}
Response.Write("SQL membership schema installed successfully");
conn.Close();
comm.Dispose();
trans.Dispose();
conn.Dispose();
}
}
[ VB.NET ]
Imports System
Imports System.Configuration
Imports System.Data
Imports System.Data.SqlClient
' We need this namespace to read .sql files from disc
Imports System.IO
Partial Class Default2
Inherits System.Web.UI.Page
Protected Sub btnInstallSqlMembership_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles btnSendEmail.Click
' Declare list of .sql files,
' notice that InstallCommon.sql must be installed first
Dim fileNames() As String = {"InstallCommon.sql", _
"InstallRoles.sql", _
"InstallMembership.sql", _
"InstallPersonalization.sql", _
"InstallProfile.sql", _
"InstallWebEventSqlProvider.sql"}
Dim SqlQuery As String = ""
Dim CurrentLine As String = ""
Dim conn As SqlConnection = New SqlConnection( _
ConfigurationManager.ConnectionStrings( _
"connection-string-name").ConnectionString)
Dim comm As SqlCommand = New SqlCommand()
conn.Open()
Dim trans As SqlTransaction = conn.BeginTransaction()
comm.Connection = conn
comm.Transaction = trans
comm.CommandType = CommandType.Text
' Iterates through all files
For i As Integer = 0 To i < fileNames.Length
' Open current SQL script from SQL/ sub folder
Dim FileContent As String = File.ReadAllText(Server.MapPath("SQL/") _
& fileNames(i), System.Text.Encoding.UTF8)
' replace default database name 'aspnetdb' to name of your database
FileContent = FileContent.Replace("aspnetdb", "your-database-name")
' Loads string to StreamReader
Dim ms As MemoryStream = New MemoryStream(System.Text.Encoding.UTF8.GetBytes(FileContent))
Dim sr As StreamReader = New StreamReader(ms)
While Not sr.EndOfStream
CurrentLine = sr.ReadLine()
' Check if line is empty
If Not (CurrentLine Is Nothing) And CurrentLine <> "" Then
If CurrentLine.Trim().ToUpper() <> "GO" Then
' Build Sql to execute
SqlQuery += CurrentLine + "\n"
Else
' Current line is 'GO' so execute code chunk
comm.CommandText = SqlQuery
Try
comm.ExecuteNonQuery()
Catch ex As Exception
Response.Write(ex.Message)
Try
trans.Rollback()
Catch exception As Exception
Response.Write("<br />" + exception.Message)
End Try
End Try
SqlQuery = ""
End If
End If
End While
Next
Response.Write("SQL membership schema installed successfully")
conn.Close()
comm.Dispose()
trans.Dispose()
conn.Dispose()
End Sub
End Class
Also, you can uninstall SQL schema with similar code. Just use files with uninstall prefix located in your v2.0
folder (probably located on c:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\ ). To uninstall SQL membership schema, use the same code but run these files:
UninstallRoles.sql
UninstallCommon.sql
UninstallMembership.sql
UninstallPersonalization.sql
UninstallProfile.sql
UninstallWebEventSqlProvider.sql
Conclusion
ASPNET_REGSQL tool is often recommended as a way to install SQL membership schema to SQL Server database. Although you can use this tool whenever is appropriate, now you know that ASPNET_REGSQL doesn't do anything special. It just runs few SQL scripts against selected database. To automate this task, you can use provided code examples instead of manual repetitive work.
To understand deeply how ASP.NET user and role management works, check Professional ASP.NET 3.5 Security, Membership, and Role Management with C# and VB (Wrox Programmer to Programmer) book which covers these issues in details.
Happy coding!
Tutorial toolbar: Tell A Friend | Add to favorites | Feedback |
|