限制基于用户的数据修改功能 (VB)
在允许用户编辑数据的 Web 应用程序中,不同的用户帐户可能具有不同的数据编辑权限。 本教程介绍如何根据访问用户动态调整数据修改功能。
简介
许多 Web 应用程序支持用户帐户,并根据登录用户提供不同的选项、报告和功能。 例如,在我们的教程中,我们可能希望允许供应商公司的用户登录网站,并更新有关其产品的常规信息(可能每个单位的名称和数量)以及供应商信息,例如其公司名称、地址、联系人信息等。 此外,我们可能希望为公司人员提供一些用户帐户,以便他们可以登录并更新产品信息,例如库存单位、重新排序级别等。 我们的 Web 应用程序可能还允许匿名用户访问 (未登录) ,但会限制他们只查看数据。 有了这样的用户帐户系统,我们希望 ASP.NET 页面中的数据 Web 控件提供适合当前登录用户的插入、编辑和删除功能。
本教程介绍如何根据访问用户动态调整数据修改功能。 具体而言,我们将创建一个页面,用于在可编辑的 DetailsView 中显示供应商信息,以及列出供应商提供的产品的 GridView。 如果访问页面的用户来自我们公司,他们可以:查看任何供应商的信息;编辑他们的地址;并编辑供应商提供的任何产品的信息。 但是,如果用户来自特定公司,则他们只能查看和编辑自己的地址信息,并且只能编辑尚未标记为已停产的产品。
图 1:我们公司的用户可以编辑任何供应商的信息 (单击以查看全尺寸图像)
图 2:特定供应商的用户只能查看和编辑其信息 (单击以查看全尺寸图像)
让我们开始吧!
注意
ASP.NET 2.0 s 成员资格系统提供了一个标准化、可扩展的平台,用于创建、管理和验证用户帐户。 由于成员资格制度的检查超出了这些教程的范围,因此本教程允许匿名访问者选择他们来自特定供应商还是来自我们的公司来“伪造”成员身份。 有关成员身份的详细信息,请参阅 检查 ASP.NET 2.0 s 成员资格、角色和配置文件 系列文章。
步骤 1:允许用户指定其访问权限
在实际 Web 应用程序中,用户帐户信息将包括他们是否为我们公司或特定供应商工作,一旦用户登录到网站,就可以从我们的 ASP.NET 页面以编程方式访问此信息。 此信息可以通过 ASP.NET 2.0 s 角色系统捕获,作为用户级帐户信息通过配置文件系统或通过某些自定义方式捕获。
由于本教程的目的是演示如何根据登录的用户调整数据修改功能,并且不是要展示 ASP.NET 2.0 秒的成员资格、角色和配置文件系统,我们将使用非常简单的机制来确定访问页面的用户的功能 - 一个 DropDownList,用户可以从中指示他们是否能够查看和编辑任何供应商信息,或者, 或者,他们可以查看和编辑哪些特定供应商的信息。 如果用户指示她可以查看和编辑 (默认) 的所有供应商信息,她可以翻页浏览所有供应商,编辑任何供应商的地址信息,并编辑所选供应商提供的任何产品的单位名称和数量。 但是,如果用户指示她只能查看和编辑特定供应商,则她只能查看该供应商的详细信息和产品,并且只能更新 那些未 停产产品的每个单位信息的名称和数量。
本教程的第一步是创建此 DropDownList,并使用系统中的供应商填充它。 UserLevelAccess.aspx
打开 文件夹中的页面EditInsertDelete
,添加属性设置为 Suppliers
的 ID
DropDownList,并将此 DropDownList 绑定到名为 AllSuppliersDataSource
的新 ObjectDataSource。
图 3:创建名为 AllSuppliersDataSource
的新对象DataSource (单击以查看全尺寸图像)
由于我们希望此 DropDownList 包含所有供应商,因此请将 ObjectDataSource 配置为调用 SuppliersBLL
类的 GetSuppliers()
方法。 此外,请确保 ObjectDataSource 方法 Update()
映射到 SuppliersBLL
类 的 UpdateSupplierAddress
方法,因为此 ObjectDataSource 也将由我们将在步骤 2 中添加的 DetailsView 使用。
完成 ObjectDataSource 向导后,通过配置 Suppliers
DropDownList 来完成这些步骤,使其显示 CompanyName
数据字段并将 SupplierID
数据字段用作每个 ListItem
的值。
图 4:将 Suppliers
DropDownList 配置为使用 CompanyName
和 SupplierID
数据字段 (单击 以查看全尺寸图像)
此时,DropDownList 列出了数据库中供应商的公司名称。 但是,我们还需要在 DropDownList 中包含“显示/编辑所有供应商”选项。 为此,请将 Suppliers
DropDownList 的 AppendDataBoundItems
属性设置为 true
,然后添加 ListItem
其 Text
属性为“显示/编辑所有供应商”且值为 -1
的 。 这可以直接通过声明性标记或通过Designer添加,方法是转到属性窗口并单击 DropDownList 属性Items
中的省略号。
注意
有关将“全选”项添加到数据绑定 DropDownList 的更详细讨论,请参阅 使用 DropDownList 筛选母版/详细信息筛选 教程。
AppendDataBoundItems
设置 属性并ListItem
添加 后,DropDownList 的声明性标记应如下所示:
<asp:DropDownList ID="Suppliers" runat="server" AppendDataBoundItems="True"
DataSourceID="AllSuppliersDataSource" DataTextField="CompanyName"
DataValueField="SupplierID">
<asp:ListItem Value="-1">Show/Edit ALL Suppliers</asp:ListItem>
</asp:DropDownList>
图 5 显示了通过浏览器查看时当前进度的屏幕截图。
图 5: Suppliers
DropDownList 包含“全部 ListItem
显示”, (单击可查看全尺寸图像)
由于我们希望在用户更改其选择后立即更新用户界面,因此请将 Suppliers
DropDownList 的 AutoPostBack
属性设置为 true
。 在步骤 2 中,我们将创建一个 DetailsView 控件,该控件将根据 DropDownList 选择显示供应商 () 的信息。 然后,在步骤 3 中,我们将为此 DropDownList 事件 SelectedIndexChanged
创建事件处理程序,在该事件中,我们将添加基于所选供应商将相应供应商信息绑定到 DetailsView 的代码。
步骤 2:添加 DetailsView 控件
让我们使用 DetailsView 显示供应商信息。 对于可以查看和编辑所有供应商的用户,DetailsView 将支持分页,允许用户一次单步执行一条记录的供应商信息。 但是,如果用户为特定供应商工作,则 DetailsView 将仅显示该特定供应商的信息,并且不包含分页界面。 在任一情况下,DetailsView 都需要允许用户编辑供应商地址、城市和国家/地区字段。
将 DetailsView 添加到 DropDownList 下的 Suppliers
页面,将其 ID
属性设置为 SupplierDetails
,并将其绑定到 AllSuppliersDataSource
在上一步中创建的 ObjectDataSource。 接下来,检查 DetailsView s 智能标记中的“启用分页”和“启用编辑”复选框。
注意
如果在 DetailsView 的智能标记中未看到“启用编辑”选项,则它是因为未将 ObjectDataSource 方法 Update()
映射到 SuppliersBLL
类 s UpdateSupplierAddress
方法。 请花点时间返回进行此配置更改,之后,“启用编辑”选项应显示在 DetailsView 的智能标记中。
SuppliersBLL
由于 类方法UpdateSupplierAddress
只接受四个参数 - supplierID
、 city
address
和 country
- 修改 DetailsView 的 BoundFields,使 CompanyName
和 Phone
BoundFields 是只读的。 此外,请完全删除 SupplierID
BoundField。 最后, AllSuppliersDataSource
ObjectDataSource 当前将其 OldValuesParameterFormatString
属性设置为 original_{0}
。 请花点时间从声明性语法中删除此属性设置,或将其设置为默认值 {0}
。
配置 SupplierDetails
DetailsView 和 AllSuppliersDataSource
ObjectDataSource 后,我们将具有以下声明性标记:
<asp:ObjectDataSource ID="AllSuppliersDataSource" runat="server"
SelectMethod="GetSuppliers" TypeName="SuppliersBLL"
UpdateMethod="UpdateSupplierAddress">
<UpdateParameters>
<asp:Parameter Name="supplierID" Type="Int32" />
<asp:Parameter Name="address" Type="String" />
<asp:Parameter Name="city" Type="String" />
<asp:Parameter Name="country" Type="String" />
</UpdateParameters>
</asp:ObjectDataSource>
<asp:DetailsView ID="SupplierDetails" runat="server" AllowPaging="True"
AutoGenerateRows="False" DataKeyNames="SupplierID"
DataSourceID="AllSuppliersDataSource">
<Fields>
<asp:BoundField DataField="CompanyName" HeaderText="Company"
ReadOnly="True" SortExpression="CompanyName" />
<asp:BoundField DataField="Address" HeaderText="Address"
SortExpression="Address" />
<asp:BoundField DataField="City" HeaderText="City"
SortExpression="City" />
<asp:BoundField DataField="Country" HeaderText="Country"
SortExpression="Country" />
<asp:BoundField DataField="Phone" HeaderText="Phone" ReadOnly="True"
SortExpression="Phone" />
<asp:CommandField ShowEditButton="True" />
</Fields>
</asp:DetailsView>
此时,无论在 DropDownList 中 Suppliers
所做的选择如何,都可以对 DetailsView 进行分页,并且可以更新所选供应商的地址信息 (请参阅图 6) 。
图 6:可以查看任何供应商信息,并更新其地址 (单击以查看全尺寸图像)
步骤 3:仅显示所选供应商的信息
我们的页面当前显示所有供应商的信息,无论是否已从 Suppliers
DropDownList 中选择特定供应商。 为了仅显示所选供应商的供应商信息,我们需要将另一个 ObjectDataSource 添加到页面,用于检索有关特定供应商的信息。
将新的 ObjectDataSource 添加到页面,将其 SingleSupplierDataSource
命名为 。 在其智能标记中,单击“配置数据源”链接,并使其使用 SuppliersBLL
类 s GetSupplierBySupplierID(supplierID)
方法。 与 AllSuppliersDataSource
ObjectDataSource 一样,将 SingleSupplierDataSource
ObjectDataSource 的 Update()
方法映射到 SuppliersBLL
类的 UpdateSupplierAddress
方法。
图 7:将 SingleSupplierDataSource
ObjectDataSource 配置为使用 GetSupplierBySupplierID(supplierID)
方法 (单击以查看全尺寸图像)
接下来,我们再次提示指定方法输入参数的参数supplierID
源GetSupplierBySupplierID(supplierID)
。 由于我们想要显示从 DropDownList 中选择的供应商的信息,因此请使用 Suppliers
DropDownList 属性 SelectedValue
作为参数源。
图 8:使用 Suppliers
DropDownList 作为 supplierID
参数源 (单击以查看全尺寸图像)
即使添加了第二个 ObjectDataSource,DetailsView 控件当前也配置为始终使用 AllSuppliersDataSource
ObjectDataSource。 我们需要根据所选的 DropDownList 项添加逻辑来调整 DetailsView Suppliers
使用的数据源。 为此,请为 Suppliers DropDownList 创建 SelectedIndexChanged
事件处理程序。 通过双击Designer中的 DropDownList,可以最轻松地创建此列表。 此事件处理程序需要确定要使用的数据源,并且必须将数据重新绑定到 DetailsView。 这是使用以下代码实现的:
Protected Sub Suppliers_SelectedIndexChanged _
(ByVal sender As Object, ByVal e As System.EventArgs) _
Handles Suppliers.SelectedIndexChanged
If Suppliers.SelectedValue = "-1" Then
' The "Show/Edit ALL" option has been selected
SupplierDetails.DataSourceID = "AllSuppliersDataSource"
' Reset the page index to show the first record
SupplierDetails.PageIndex = 0
Else
' The user picked a particular supplier
SupplierDetails.DataSourceID = "SingleSupplierDataSource"
End If
' Ensure that the DetailsView and GridView are in read-only mode
SupplierDetails.ChangeMode(DetailsViewMode.ReadOnly)
' Need to "refresh" the DetailsView
SupplierDetails.DataBind()
End Sub
事件处理程序首先确定是否选择了“显示/编辑所有供应商”选项。 如果是,则它将 DetailsView 设置为 SupplierDetails
DataSourceID
AllSuppliersDataSource
,并通过将 属性设置为 PageIndex
0,将用户返回到供应商集中的第一条记录。 但是,如果用户已从 DropDownList 中选择了特定供应商,则 DetailsView 将 DataSourceID
分配给 SingleSuppliersDataSource
。 无论使用哪种数据源,模式 SuppliersDetails
都会还原回只读模式,并且通过调用 SuppliersDetails
控件 方法 DataBind()
将数据重新绑定到 DetailsView。
完成此事件处理程序后,DetailsView 控件现在会显示所选供应商,除非选择了“显示/编辑所有供应商”选项,在这种情况下,可以通过分页界面查看所有供应商。 图 9 显示了选择了“显示/编辑所有供应商”选项的页面;请注意,存在分页界面,允许用户访问和更新任何供应商。 图 10 显示了选择了 Ma 毛森供应商的页面。 在这种情况下,只有马梅森的信息是可以查看和编辑的。
图 9:可以查看所有供应商信息 (单击以查看全尺寸图像)
图 10:只能查看和编辑所选供应商的信息 (单击以查看全尺寸图像)
注意
在本教程中,DropDownList 和 DetailsView 控件 EnableViewState
都必须设置为 true
(默认) ,因为必须跨回发记住 DropDownList 和 SelectedIndex
DetailsView 属性 DataSourceID
的更改。
步骤 4:在可编辑的 GridView 中列出供应商产品
完成 DetailsView 后,下一步是包含可编辑的 GridView,其中列出了所选供应商提供的这些产品。 此 GridView 应仅 ProductName
允许编辑 和 QuantityPerUnit
字段。 此外,如果访问页面的用户来自特定供应商,则只允许更新 未 停产的产品。 为此,我们需要首先添加类 方法的ProductsBLL
重载,该重载仅ProductID
采用 、 ProductName
和 QuantityPerUnit
字段作为UpdateProducts
输入。 我们已在众多教程中预先演练了此过程,因此让我们来看看此处的代码,该代码应添加到 ProductsBLL
:
<System.ComponentModel.DataObjectMethodAttribute _
(System.ComponentModel.DataObjectMethodType.Update, False)> _
Public Function UpdateProduct(ByVal productName As String, _
ByVal quantityPerUnit As String, ByVal productID As Integer) As Boolean
Dim products As Northwind.ProductsDataTable = Adapter.GetProductByProductID(productID)
If products.Count = 0 Then
' no matching record found, return false
Return False
End If
Dim product As Northwind.ProductsRow = products(0)
product.ProductName = productName
If quantityPerUnit Is Nothing Then
product.SetQuantityPerUnitNull()
Else
product.QuantityPerUnit = quantityPerUnit
End If
' Update the product record
Dim rowsAffected As Integer = Adapter.Update(product)
' Return true if precisely one row was updated, otherwise false
Return rowsAffected = 1
End Function
创建此重载后,我们准备添加 GridView 控件及其关联的 ObjectDataSource。 向页面添加新的 GridView,将其 ID
属性设置为 ProductsBySupplier
,并将其配置为使用名为 ProductsBySupplierDataSource
的新 ObjectDataSource。 由于我们希望此 GridView 按所选供应商列出这些产品,因此请使用 ProductsBLL
类 GetProductsBySupplierID(supplierID)
方法。 此外,将 Update()
方法映射到刚刚创建的新 UpdateProduct
重载。
图 11:将 ObjectDataSource 配置为使用 UpdateProduct
刚刚创建的重载 (单击以查看全尺寸图像)
我们再次提示选择方法输入supplierID
参数的参数GetProductsBySupplierID(supplierID)
源。 由于我们想要显示 DetailsView 中选择的供应商的产品,因此请使用 SuppliersDetails
DetailsView 控件的 SelectedValue
属性作为参数源。
图 12:使用 SuppliersDetails
DetailsView s SelectedValue
属性作为参数源 (单击以查看全尺寸图像)
返回到 GridView,删除除 、 QuantityPerUnit
和 Discontinued
之外ProductName
的所有 GridView 字段,将 Discontinued
CheckBoxField 标记为只读。 此外,检查 GridView 智能标记中的“启用编辑”选项。 进行这些更改后,GridView 和 ObjectDataSource 的声明性标记应如下所示:
<asp:GridView ID="ProductsBySupplier" runat="server" AutoGenerateColumns="False"
DataKeyNames="ProductID" DataSourceID="ProductsBySupplierDataSource">
<Columns>
<asp:CommandField ShowEditButton="True" />
<asp:BoundField DataField="ProductName" HeaderText="Product"
SortExpression="ProductName" />
<asp:BoundField DataField="QuantityPerUnit" HeaderText="Qty/Unit"
SortExpression="QuantityPerUnit" />
<asp:CheckBoxField DataField="Discontinued" HeaderText="Discontinued"
ReadOnly="True" SortExpression="Discontinued" />
</Columns>
</asp:GridView>
<asp:ObjectDataSource ID="ProductsBySupplierDataSource" runat="server"
OldValuesParameterFormatString="original_{0}" TypeName="ProductsBLL"
SelectMethod="GetProductsBySupplierID" UpdateMethod="UpdateProduct">
<UpdateParameters>
<asp:Parameter Name="productName" Type="String" />
<asp:Parameter Name="quantityPerUnit" Type="String" />
<asp:Parameter Name="productID" Type="Int32" />
</UpdateParameters>
<SelectParameters>
<asp:ControlParameter ControlID="SupplierDetails" Name="supplierID"
PropertyName="SelectedValue" Type="Int32" />
</SelectParameters>
</asp:ObjectDataSource>
与以前的 ObjectDataSources 一 OldValuesParameterFormatString
样,此属性设置为 original_{0}
,这将导致在尝试更新每个单位的产品名称或数量时出现问题。 完全从声明性语法中删除此属性,或将其设置为默认值 {0}
。
完成此配置后,我们的页面现在列出了在 GridView 中选择的供应商提供的产品 (请参阅图 13) 。 目前,可以更新 每个单位的任何 产品名称或数量。 但是,我们需要更新页面逻辑,以便禁止与特定供应商关联的用户的停产产品使用此类功能。 我们将在步骤 5 中处理最后一部分。
图 13:显示所选供应商提供的产品 (单击以查看全尺寸图像)
注意
添加此可编辑 GridView 后, Suppliers
DropDownList 的 SelectedIndexChanged
事件处理程序应更新为将 GridView 返回到只读状态。 否则,如果在编辑产品信息时选择了其他供应商,则新供应商的 GridView 中的相应索引也将是可编辑的。 若要防止这种情况,只需在事件处理程序中SelectedIndexChanged
将 GridView 的 EditIndex
属性设置为 -1
。
此外,请记住,必须启用 GridView 视图状态, (默认行为) 。 如果将 GridView 的 EnableViewState
属性设置为 false
,则存在并发用户无意中删除或编辑记录的风险。
步骤 5:未选中“显示/编辑所有供应商”时,禁止编辑停产产品
ProductsBySupplier
虽然 GridView 功能齐全,但它目前向来自特定供应商的用户授予了过多的访问权限。 根据我们的业务规则,此类用户不应能够更新停产的产品。 为了强制实施此功能,我们可以隐藏 (或禁用) 那些包含已停产产品的 GridView 行中的“编辑”按钮(当来自供应商的用户访问页面时)。
为 GridView 事件 RowDataBound
创建事件处理程序。 在此事件处理程序中,我们需要确定用户是否与特定供应商相关联,对于本教程,可以通过检查 Suppliers DropDownList 的 SelectedValue
属性来确定该属性 - 如果它不是 -1,则用户与特定供应商相关联。 对于此类用户,我们需要确定产品是否已停产。 我们可以通过 属性获取对绑定到 GridView 行的实际ProductRow
实例的引用,如在 GridView 的页脚中显示摘要信息教程中所述。e.Row.DataItem
如果产品已停产,则可以使用上一教程 添加 Client-Side 删除时确认中所述的技术,获取对 GridView 命令字段中“编辑”按钮的编程引用。 获得引用后,可以隐藏或禁用该按钮。
Protected Sub ProductsBySupplier_RowDataBound _
(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.GridViewRowEventArgs) _
Handles ProductsBySupplier.RowDataBound
If e.Row.RowType = DataControlRowType.DataRow Then
' Is this a supplier-specific user?
If Suppliers.SelectedValue <> "-1" Then
' Get a reference to the ProductRow
Dim product As Northwind.ProductsRow = _
CType(CType(e.Row.DataItem, System.Data.DataRowView).Row, _
Northwind.ProductsRow)
' Is this product discontinued?
If product.Discontinued Then
' Get a reference to the Edit LinkButton
Dim editButton As LinkButton = _
CType(e.Row.Cells(0).Controls(0), LinkButton)
' Hide the Edit button
editButton.Visible = False
End If
End If
End If
End Sub
有了此事件处理程序后,当以特定供应商的用户身份访问此页面时,那些停产的产品不可编辑,因为这些产品的“编辑”按钮是隐藏的。 例如,Chef Anton s Gumbo Mix 是新奥尔良卡琼喜悦供应商的停产产品。 访问此特定供应商的页面时,此产品的“编辑”按钮隐藏 (见图 14) 。 但是,在使用“显示/编辑所有供应商”访问时,可以使用“编辑”按钮 (见图 15) 。
图 14:对于 Supplier-Specific 用户,Chef Anton s Gumbo Mix 的“编辑”按钮处于隐藏状态 (单击以查看全尺寸图像)
图 15:对于“显示/编辑所有供应商用户”,“Chef Anton s Gumbo Mix”的“编辑”按钮显示在 (单击以查看全尺寸图像)
在业务逻辑层中检查访问权限
在本教程中,ASP.NET 页处理有关用户可以看到哪些信息以及他可以更新哪些产品的所有逻辑。 理想情况下,此逻辑也会出现在业务逻辑层。 例如,SuppliersBLL
返回所有供应商的类方法 GetSuppliers()
() 可能包含检查,以确保当前登录的用户不与特定供应商相关联。 同样,UpdateSupplierAddress
该方法可以包含一个检查,以确保当前登录的用户为我们公司 (工作,因此可以更新所有供应商地址信息) 或与正在更新数据的供应商相关联。
此处未包含此类 BLL 层检查,因为在我们的教程中,用户权限由页面上的 DropDownList 决定,BLL 类无法访问该列表。 使用成员身份系统或 ASP.NET (提供的开箱即用身份验证方案之一(例如Windows 身份验证) )时,可以从 BLL 访问当前登录的用户信息和角色信息,从而使此类访问权限检查在表示层和 BLL 层都成为可能。
总结
提供用户帐户的大多数站点都需要基于登录的用户自定义数据修改界面。 管理用户可能能够删除和编辑任何记录,而非管理用户可能仅限于更新或删除自己创建的记录。 无论方案是什么,都可以扩展数据 Web 控件、ObjectDataSource 和业务逻辑层类,以基于登录用户添加或拒绝某些功能。 在本教程中,我们了解了如何根据用户是否与特定供应商关联或是否为我们公司工作来限制可查看和可编辑的数据。
本教程总结了如何使用 GridView、DetailsView 和 FormView 控件插入、更新和删除数据。 从下一教程开始,我们将把注意力转向添加分页和排序支持。
编程愉快!
关于作者
Scott Mitchell 是七本 ASP/ASP.NET 书籍的作者, 4GuysFromRolla.com 的创始人,自 1998 年以来一直从事 Microsoft Web 技术工作。 Scott 担任独立顾问、培训师和作家。 他的最新书是 山姆斯在24小时内 ASP.NET 2.0自学。 可以在 上联系 mitchell@4GuysFromRolla.com他, 也可以通过他的博客联系到他,该博客可在 http://ScottOnWriting.NET中找到。