15.5 扩展预定义的提供程序
除了从某个抽象的基类(如MembershipProvider)中建立自己的提供程序之外,还可以扩展ASP.NET 2.0中预定义的提供程序。
例如,有时可能需要将成员系统、角色管理系统和SQL Server联合使用,但想改变默认提供程序(SqlMembershipProvider或SqlRoleProvider)的底层工作方式。如果要使用底层的数据库,而该数据库已由某个提供程序使用,修改提供程序的工作方式就比从头开始建立全新的提供程序好得多。
扩展预定义的提供程序的另一个优点是,不需要重写提供程序的内容。如果只修改内置提供程序的一个操作,只需要重写几个方法,就可以使修改过的操作快速、方便地应用到应用程序中。
下面就扩展一个内置的提供程序,以改变该提供程序的底层功能。
15.5.1 用新的LimitedSqlRoleProvider提供程序限制角色功能
假设要在ASP.NET应用程序中使用新的角色管理系统,并为该系统使用SQL Server后端数据库。我们希望限制开发人员可以在其应用程序中创建的角色,并去除在系统中将用户添加到某个角色中的功能。
本例不是以RoleProvider抽象类为基础建立角色提供程序,而是从SqlRoleProvider中派生新的提供程序,再修改创建角色和给角色添加用户的几个方法。
与前面一样,在应用程序的App_Code文件夹中创建新的提供程序。如果要在公司范围内使用这个提供程序,就应创建一个类库项目,这样开发小组就可以使用DLL,而不是一个可修改的类文件。
在App_Code文件夹中创建一个类文件LimitedSqlRoleProvider.vb或.cs,这个类要继承SqlRoleProvider,因此其结构如程序清单15-18所示。
程序清单15-18 LimitedSqlRoleProvider类的开头部分
VB
Imports Microsoft.VisualBasic
Imports System.Configuration.Provider
Public Class LimitedSqlRoleProvider
Inherits SqlRoleProvider
End Class
C#
using System;
using System.Web;
using System.Web.Security;
using System.Configuration;
using System.Configuration.Provider;
public calss LimitedSqlRoleProvider: SqlRoleProvider
{
}
这与XmlMembershipProvider类的创建相似。在创建XmlMembershipProvider类时,可以使用Visual Studio建立整个类框架,以重写所有需要的方法和属性,使新类能运行。在本例中,如果要在Visual Studio中完成这些操作,就会出错(C#),或者得不到任何结果(Visual Basic),因为本例没有使用抽象类。我们不需要重写很多方法和属性,因为本例继承的类是从一个抽象类中派生的,只需重写需要使用的方法和属性即可。
要在Visual Studio中查看这些方法和属性,应输入Public Overrides(Visual Basic)或public override(C#),IntelliSense就会在下拉列表中显示可用的方法和属性,如图15-6所示。
对于本例,只需重写CreateRole()、AddUsersToRoles()和DeleteRole()方法,详见下面的内容。
1. CreateRole()方法
SqlRoleProvider类的CreateRole()方法允许开发人员给系统添加角色。这个方法只带有一个参数:角色名,这是一个字符串值。本例不允许开发人员随意创建角色,所以这个提供程序将开发人员能创建的角色限制为Administrator和Manager。为此,CreateRole()方法的代码如程序清单15-19所示。
程序清单15-19 在CreateRole()方法中只允许创建Administrator和Manager角色
VB
Public Overrides Sub CreateRole(ByVal roleName As String)
If (roleName = "Administrator" Or roleName = "Manager") Then
MyBase.CreateRole(roleName)
Else
Throw New _
ProviderException("Role creation limited to only Administrator
and Manager")
End If
End Sub

图 15-6
C#
public override void CreateRole(string roleName)
{
if (roleName == "Administrator" || roleName == "Manager")
{
base.CreateRole(roleName);
}
else
{
throw new
ProviderException("Role creation limited to only Administrator and Manager");
}
}
在这个方法中,首先进行检查,确定所创建的角色是否为Administrator或Manager,如果不是,就抛出一个ProviderException异常,告诉开发人员,不允许创建该角色。
如果所创建的角色是Administrator或Manager,就调用基类SqlRoleProvider的CreateRole()方法。
2. DeleteRole()方法
如果只允许使用本提供程序的开发人员创建某些角色,就不能让他们具备删除任意角色的权限。因此,必须重写SqlRoleProvider类的DeleteRole()方法,如程序清单15-20所示。
程序清单15-20 禁用DeleteRole()方法
VB
Public Overrides Function DeleteRole(ByVal roleName As String, _
ByVal throwOnPopulatedRole As Boolean) As Boolean
Return False
End Function
C#
public override bool DeleteRole (string roleName, bool throwOn PopulatedRole)
{
return false;
}
在DeleteRole()方法中,完全禁止删除角色。本例并不调用基类的DeleteRole()方法,而是返回如下内容:
Return MyBase.DeleteRole(roleName, throwOnPopulatedRole)
返回False值,不执行任何操作。另一种方式是抛出NotSupportedException异常,如下所示:
Throw New NotSupportedException()
3. AddUsersToRoles()方法
在可以重写的方法中,注意只有一个方法允许把任意多个用户添加到任意多个角色中。Roles类中的许多方法都映射了该方法。在Roles类中,注意AddUserToRole()方法、AddUserToRoles()方法、AddUsersToRole()方法和AddUsersToRoles()方法。这些方法都映射到基类RoleProvider中的AddUsersToRoles()方法。
例如,假设只允许开发人员将用户添加到Manager角色中,但不允许添加到Administrator角色中,就可以构建如程序清单15-21所示的方法。
程序清单15-21 禁止把用户添加到某个特定角色中
VB
Public Overrides Sub AddUsersToRoles(ByVal usernames()As String, _
ByVal roleNames() As String)
For Each roleItem As String In roleNames
If roleItem = "Administrator" Then
Throw New _
ProviderException("You are not authorized to" & _
"add any users to the Administrator role")
End If
Next
MyBase.AddUsersToRoles(usernames, roleNames)
End Sub
C#
public override void AddUsersToRoles( string[] usernames, string[] roleNames)
{
foreach(string roleItem in roleNames)
{
if (roleItem == "Administrator")
{
throw new ProviderException("You are not authorized to" +
"add any users to the Administrator role");
}
}
base.AddUserToRoles(usernames, roleNames);
}
这个重写方法迭代所提供的所有角色,如果字符串数组中包含的某个角色是Administrator,就抛出一个ProviderException异常,告诉开发人员,不允许把用户添加到这个角色中。还可以对基类RoleProvider中的RemoveUserFromRoles()方法采用相同的操作,但这里没有列出该方法。
15.5.2 使用新的LimitedSqlRoleProvider提供程序
有了新的提供程序后,就需要对web.config文件进行一些修改,以便在ASP.NET应用程序中使用这个提供程序。在web.config文件中为使用这个提供程序而添加的代码如程序清单15-22所示。
程序清单15-22 在web.config文件中为使用这个提供程序进行一些修改
<configuration>
<system.web>
<roleManager defaultProvider="LimitedProvider" enabled="true">
<providers>
<add connectionStringName = "LocalSqlServer" applicationName="/"
name = "LimitedProvider"
type = "LimitedSqlRoleProvider" />
</providers>
</roleManager>
</system.web>
</configuration>
必须为defaultProvider属性提供一个值,并在<provider>部分中进一步定义该提供程序,这样才能在应用程序中使用它。还需要将enabled属性设置为true,才能激活该提供程序。在默认情况下,角色管理系统是禁用的。
使用<add>元素,可以添加一个使用LimitedSqlRoleProvider类的新提供程序实例。这个提供程序派生于SqlRoleProvider类,所以必须使用该提供程序需要的属性,如connectionStringName属性,它指向用于连接指定SQL实例的连接字符串。
有了LimitedSqlRoleProvider实例,并在web.config文件中定义了它之后,就可以按照往常的方式在应用程序中使用Roles类了,但注意该类的操作方式与一般的SqlRoleProvider有所不同。
为了理解Roles类的操作方式,构建一个简单的ASP.NET页面,其中包含一个文本框、一个按钮和一个标签服务器控件。该页面如程序清单15-23所示。
程序清单15-23 使用Roles.CreateRole()
VB
<%@ Page Language="VB" %>
<script runat="server">
Protected Sub Button1_Click(ByVal sender As Object, _
ByVal e As System.EventArgs)
Try
Roles.CreateRole(TextBox1.Text)
Label1.Text = "Role successfully created."
Catch ex As Exception
Label1.Text = ex.Message.ToString()
End Try
End Sub
</script>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title>Main Page</title>
</head>
<body>
<form id="form1" runat="server">
<div>
Role Name:<br />
<asp:TextBox ID="TextBox1" runat="server"></asp:TextBox><br />
<br />
<asp:Button ID="Button1" runat="server" Text="Create Role"
OnClick= "Button1_Click" /><br />
<br />
<asp:Label ID="Label1" runat="server"></asp:Label></div>
</form>
</body>
</html>
C#
<%@ Page Language="C#" %>
<script runat="server">
protected void Button1_Click(object sender, EventArgs e)
{
try
{
Roles.CreateRole(TextBox1.Text);
Label1.Text = "Role successfully created.";
}
catch (Exception ex)
{
Label1.Text = ex.Message.ToString();
}
}
</script>
这个简单的ASP.NET页面允许用户在文本框中输入一个字符串值,并用这个值尝试创建一个新角色。注意只要创建的角色不是Administrator或Manager,就会出错。所以在调用Roles.CreateRole()时,如果不遵循提供程序定义的规则,就会产生错误。实际上,运行这个页面,并输入不是Administrator或Manager的角色,就会显示如图15-7所示的窗口。

图 15-7
为了说明这个提供程序的工作情况,创建另一个ASP.NET页面,它允许将用户添加到某个角色中。如前所述,这可以用许多方式实现,但本例使用Roles.AddUserToRole()方法,如程序清单15-24所示。
程序清单15-24 尝试通过新的角色提供程序将用户添加到角色中
VB
<%@ Page Language="VB" %>
<script runat="server">
Protected Sub Button1_Click(ByVal sender As Object, _
ByVal e As System.EventArgs)
Try
Roles.AddUserToRole(TextBox1.Text, TextBox2.Text)
Label1.Text = "User successfully added to role"
Catch ex As Exception
Label1.Text = ex.Message.ToString()
End Try
End Sub
</script>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title>Main Page</title>
</head>
<body>
<form id="form1" runat="server">
<div>
Add the following user:<br />
<asp:TextBox ID="TextBox1" runat="server"></asp:TextBox><br />
<br />
To role:<br />
<asp:TextBox ID="TextBox2" runat="server"></asp:TextBox><br />
<br />
<asp:Button ID="Button1" runat="server" Text="Add User to Role"
OnClick= Button1_Click" /><br />
<br />
<asp:Label ID="Label1" runat="server"></asp:Label></div>
</form>
</body>
</html>
C#
<%@ Page Language="C#" %>
<script runat="server">
protected void Button1_Click(object sender, EventArgs e)
{
try
{
Roles.AddUserToRole(TextBox1.Text, TextBox2.Text);
Label1.Text = "User successfully added to role";
}
catch (Exception ex)
{
Label1.Text = ex.Message.ToString();
}
}
</script>
在这个例子中,提供了两个文本框。第一个文本框要求输入用户名,第二个文本框要求输入角色,以便将用户添加到该角色中。按钮单击事件的代码使用了Roles.AddUserTo Role()方法。前面建立了提供程序,所以如果试图把用户添加到Administrator角色中,就会抛出一个错误,如图15-8所示。

图 15-8
本例试图把User2添加到Administrator角色中,当然,这会抛出一个错误,并返回在提供程序中定义的错误信息。







