Scott Mitchell 的ASP.NET 2.0數據教程之二十三:基於用戶對修改數據進行限制

Scott Mitchell 的ASP.NET 2.0數據教程之二十三:基於用戶對修改數據進行限制


導言

很多Web程序都支持用戶帳號,根據不同的登錄用戶提供不同的選項,報表等功能。例如,就我們的教程中,我們要允許供應商公司的一些賬戶能登錄網站並更新它們的產品-包括產品名稱和單價,或許還有供應商的信息,比如它們的公司名稱,地址,以及聯繫人信息等等。此外,可能我們還想包含一些帳號留給我們自己公司用戶,以便讓它們能夠登錄並進行產品信息修改,比如股價,級別調整等等。我們的Web程序同時也可以允許匿名登錄,但是僅僅讓這些用戶瀏覽數據。幷包含一個系統用戶,通過對ASP.NET頁面中的數據控件能夠進行數據的增,改,刪。

在這篇教程中,我們將考察如何動態地根據不同的訪問用戶來調整數據的修改能力。更進一步,我們新建一個頁面,通過一個可編輯的DetailsView來顯示供應商信息,以及一個GridView控件列出供應商提供的產品。如果訪問者來自我們公司,它們可以查看任何供應商信息,編輯他們的地址,編輯任何一個供應商提供的產品信息。然而,如果用戶來自某個具體某個公司,它們只能查看和修改他們自己公司的地址信息,或者修改他們那些沒有缺貨的商品信息。

圖1:一個來自我們公司的用戶可以編輯任何一個供應商信息

 

圖2:一個來自某個供應商公司的他只能查看和編輯他們自己的信息

 讓我們開始吧!

注意:ASP.NET 2.0的MemberShip體系提供了一個可以創建,管理,驗證用戶帳號的標準的,可以擴展的平臺。考慮到考察membership體系已經超出了本文的內容,本文將假設匿名用戶已經通過了membership體系,可以認爲他們是來自一個具體的供應商或者是我們公司。有關更多membership的內容,可以參考我的 考察ASP.Net 2.0 的Membership, Roles, Profiles文章系列。

第一步: 允許用戶能夠指定他們的訪問權限

 在實際的Web應用程序中,一個用戶的帳號信息已經包含了識別他們是來自我們公司或者供應商,而且這種信息在用戶一登錄之後便可以在ASP.NET中代碼訪問到。這個信息可以通過ASP.NET 2.0的角色體系獲取,作爲檔案系統或者其他業務上的用戶帳號信息。

由於這篇教程是示範針對不同登錄用戶調整數據修改的能力, 並不是要凸顯使用ASP.NET 2.0的membership,roles,和profile系統,我們會使用一種很簡單的機制來決定用戶訪問網頁的這種能力-通過一個下拉框,用戶可以選擇他們想要查看或修改任何一個供應商的信息,或者是作爲一個供應商,只能查看和修改自己的信息。如果用戶使用默認的可以查看和修改任何供應商信息,那麼她分頁查看所有的供應商,編輯他們的地址信息,以及編輯選擇的某個供應商的產品的名字或者單價。如果選擇了只能查看和編輯某個供應商的信息,那麼她只能查看這個供應商的產品具體信息,更新那些沒有過期的產品的名稱和單價。

 接下來我們將要新建一個DropDownList並給它填充系統供應商信息數據。打開EditInsertDelete文件夾下的UserLevelAccess.aspx頁面,添加一個DropDownList控件,設置它的ID屬性爲Suppliers,並綁定到一個命名爲AllSuppliersDataSource的ObjectDataSource控件。

 

圖3:創建一個名爲AllSuppliersDataSource的ObjectDataSource控件

 因爲我們想要DropDownList顯示所有的供應商,我們配置ObjectDataSource調用SuppliersBLL類中的GetSuppliers()方法。同時確保控件的Update()方法映射到SuppliersBLL類的UpdateSupplierAddress方法,而這個ObjectDataSource控件將同時被下一步中的DetailsView控件使用。完成ObjectDataSource的設置後,再完成設置Suppliers下拉框控件,讓它的每個ListItem顯示數據成員爲CompanyName,數據成員的值爲SupplierID。

 圖4:配置Suppliers DropDownList使用CompanyName和SupplierID數據成員

 到這裏,這個DropDownList列出了數據庫中的所有供應商的名字。然而,我們還需要對下拉框添加一個“顯示/編輯所有供應商”的選項。可以通過設置Suppliers下拉框的AppendDataBoundItem屬性爲true,並添加一個ListItem,設置它的Text屬性爲“顯示/編輯所有供應商”,value屬性爲-1來實現。這些可以通過直接聲明標記語言或者在屬性窗口的設計器中點擊下拉框的Item屬性來添加完成。

注意:可以回頭參考教程《使用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:包含一個“Show ALL”的ListItem的Suppliers下拉框,以及其他每個供應商名稱

由於我們想讓用戶改變選擇後,立刻更新用戶界面,我們需要設置Suppliers下拉框的AutoPostBack屬性值爲true。在第二步中我們將創建一個DetailsView控件來顯示基於用戶選擇後顯示的供應商詳細信息。然後,在第三步中,我們會創建一個下拉框的SelectedIndexChanged事件的處理器,在代碼裏面我們將根據當前的選擇來把具體的供應商信息綁定到DetailsView控件中。

 第二步:添加一個DetailsView控件

 讓我們使用DetailsView控件來顯示供應商信息。對於能夠查看和編輯所有供應商信息的用戶,這時DetailsView將支持分頁,並允許用戶逐個查看每個供應商資料。如果用戶是其中的某個供應商,這時DetailsView將只顯示當前這個供應商信息,而且不會包含分頁的界面。在兩種情況下,DetailsView都將允許用戶能夠編輯所能訪問到的供應商的地址,城市和國家等屬性值。

 在Suppliers下拉框下添加一個DetailsView,並設置它的ID屬性爲SupplierDetails,然後把它綁定到前面創建的AllSuppliersDataSource ObjectDataSource控件。之後,在DetailsView的智能標籤中打開Enable Paging和Enable Editing 的多選框。

 注意:如果你在DetailsView標籤代碼中沒有看到Enable Edit屬性,那是因爲你沒有把ObjectDataSource的Update()方法

綁定到SuppliersBLL類的UpdateSupplierAddress方法。花一點事件回過去重新設置一下,然後再回來就能看到Enable Edit屬性。由於SuppliersBLL類的UpdateSupplierAddress方法只允許有四個參數-supplierID,address,city以及country,需要修改DetailsView的BoundFields讓CompanyName和Phone兩個BoundFields是read-only。此外,把SupplierID這個BoundField完全刪除。最後,這個AllSuppliersDataSource控件已經含有一個OldValuesParameterFormatString屬性,值爲original_{0}。再花些時間把它完全刪除或者修改它的值爲默認值{0}。通過對SupplierDetails這個DetailsView控件以及對AllSuppliersDataSource這個ObjectDataSource控件的設置,我們的代碼將如下所示:

 1<asp:ObjectDataSource ID="AllSuppliersDataSource" runat="server"
 2
 3    SelectMethod="GetSuppliers" TypeName="SuppliersBLL"
 4
 5    UpdateMethod="UpdateSupplierAddress">
 6
 7    <UpdateParameters>
 8
 9        <asp:Parameter Name="supplierID" Type="Int32" />
10
11        <asp:Parameter Name="address" Type="String" />
12
13        <asp:Parameter Name="city" Type="String" />
14
15        <asp:Parameter Name="country" Type="String" />
16
17    </UpdateParameters>
18
19</asp:ObjectDataSource>
20
21
22
23<asp:DetailsView ID="SupplierDetails" runat="server" AllowPaging="True" AutoGenerateRows="False"
24
25    DataKeyNames="SupplierID" DataSourceID="AllSuppliersDataSource">
26
27    <Fields>
28
29        <asp:BoundField DataField="CompanyName" HeaderText="Company" ReadOnly="True"

SortExpression="CompanyName" />
30
31        <asp:BoundField DataField="Address" HeaderText="Address" SortExpression="Address" />
32
33        <asp:BoundField DataField="City" HeaderText="City" SortExpression="City" />
34
35        <asp:BoundField DataField="Country" HeaderText="Country" SortExpression="Country" />
36
37        <asp:BoundField DataField="Phone" HeaderText="Phone" ReadOnly="True" SortExpression="Phone" />
38
39        <asp:CommandField ShowEditButton="True" />
40    </Fields>
41</asp:DetailsView>
 

這時不管在Suppliers下拉框中選擇什麼,DetailsView能夠被分頁瀏覽,選擇的供應商的地址信息也可以被更新。(見圖6)

 

圖6:任何供應商的信息都可以被查看,並且他的地址可以被更新。

 第三步:顯示只有被選擇的供應商信息

我們的頁面此刻不管從Suppliers下拉框中選擇哪個供應商,都能看到所有的供應商信息。爲了只顯示選擇的供應商信息,我們需要添加另外一個ObjectDataSource到頁面,用來獲取一個特定的供應商信息。

 在頁面上添加一個新的ObjectDataSource控件,命名爲SingleSupplierDataSource。在標籤智能標籤中,點擊Configure Data Source的鏈接,讓它使用SuppliersBLL類的GetSupplierBySupplierID(supplierID)方法。和AllSuppliersDataSource這個控件一樣,把SingleSupplierDataSource控件的Update()方法指向到SuppliersBLL類的UpdateSupplierAdress方法。

 圖7:配置SingleSupplierDataSource ObjectDataSource控件並使用GetSupplierBySupplierID(supplierID)方法

接下來,提示我們需要爲GetSupplierBySupplierID(supplierID)方法的supplierID參數指定參數來源。因爲我們想要從下拉框選擇的供應商來顯示信息,這裏我們選擇Suppliers下拉框的SelectedValue屬性作爲參數來源。

 

圖8:使用Suppliers下拉框作爲supplierID參數來源

 即使我們添加了第二個ObjectDataSource,目前的DetailsView仍然是使用AllSuppliersDataSource這個ObjectDataSource控件。我們需要添加一些邏輯,根據選擇不同的Suppliers中的供應商來調整數據源的使用。爲了做到這些,可以爲Suppliers下拉框添加一個SelectedIndexChanged事件的處理器。可以通過最簡單地在設計器中雙擊下拉框完成。這個事件處理器需要決定使用什麼數據源,而且需要重新綁定數據道DetailsView控件。下面是完成功能的代碼:

 1protected void Suppliers_SelectedIndexChanged(object sender, EventArgs e)
 2
 3{
 4
 5    if (Suppliers.SelectedValue == "-1")
 6
 7    {
 8
 9        // The "Show/Edit ALL" option has been selected
10
11        SupplierDetails.DataSourceID = "AllSuppliersDataSource";
12
13
14
15        // Reset the page index to show the first record
16
17        SupplierDetails.PageIndex = 0;
18
19    }
20
21    else
22
23        // The user picked a particular supplier
24
25        SupplierDetails.DataSourceID = "SingleSupplierDataSource";
26
27
28
29    // Ensure that the DetailsView is in read-only mode
30
31    SupplierDetails.ChangeMode(DetailsViewMode.ReadOnly);
32
33
34
35    // Need to "refresh" the DetailsView
36
37    SupplierDetails.DataBind();
38
39}
40
41
42
 

這個事件處理器由是否選擇“顯示/編輯所有的供應商“來觸發。如果選擇,它會設置SupplierDetails這個DetailsView控件的DataSourceID爲AllSuppliersDataSource控件,並且通過指定PageIndex爲0來使得用戶看到這些供應商中的第一條記錄。然而,如果用戶從下拉框選擇了一個特定的供應商,DetailsView的DataSourceID將會被設置成SingleSuppliersDataSource。不管使用哪一種數據源,SuppliersDetails將會被設置成只讀模式,通過調用SuppliersDetails控件的DataBind()方法來重新綁定DetailsView的數據。

 通過這個事件處理器,DetailsView現在可以顯示選擇的供應商信息,除非選擇了“顯示/編輯所有的供應商“,那樣所有的供應商可以通過分頁被瀏覽。圖9顯示了選擇了“顯示/編輯所有的供應商“的頁面,注意分頁界面的實現,允許用戶訪問和更新供應商信息。圖10顯示了選擇的Ma Maison供應商信息。這種情況下只有Ma Maison的信息是可以被瀏覽和編輯的。

 

圖9:所有的供應商信息可以被瀏覽和編輯

 

圖10:只有選擇的供應商信息才能被瀏覽和編輯

 注意:對於這個教程,DropDownList和DetailsView控件的EnableViewState都必須設置成默認的true,這是因爲改變DropDownList的SelectedIndex和DetailsView的DataSourceID屬性,必須在回傳的時候被記錄。

 第四步:在一個可編輯的GridView中列出供應商信息

 隨着DetailsView的完成,我們下一步要根據選擇的供應商提供一個可以編輯的GridView。這個GridView控件應該只允許編輯ProductName和QuantityPerUnit屬性。此外,如果用戶是一個特定的供應商,並且訪問這個頁面,應該允許他能夠更新那些可以沒有過時的產品。爲了實現這些我們首先需要給ProductBLL類添加一個重載的UpdateProducts方法,讓它使用ProductID,ProductName,以及QuantityPerUnit作爲輸入參數。在前面的教程中我們已經做過類似的操作,所以這裏我們僅看一下需要添加到ProductsBLL類中的代碼:

 1[System.ComponentModel.DataObjectMethodAttribute(System.ComponentModel.DataObjectMethodType.Update, false)]
 2
 3public bool UpdateProduct(string productName, string quantityPerUnit, int productID)
 4
 5{
 6
 7    Northwind.ProductsDataTable products = Adapter.GetProductByProductID(productID);
 8
 9    if (products.Count == 0)
10
11        // no matching record found, return false
12
13        return false;
14
15
16
17    Northwind.ProductsRow product = products[0];
18
19
20
21    product.ProductName = productName;
22
23    if (quantityPerUnit == null) product.SetQuantityPerUnitNull(); else product.QuantityPerUnit = quantityPerUnit;
24
25
26
27    // Update the product record
28
29    int rowsAffected = Adapter.Update(product);
30
31
32
33    // Return true if precisely one row was updated, otherwise false
34
35    return rowsAffected == 1;
36
37}

 

通過這個重載方法,我們可以開始添加GridView控件以及相關的ObjectDataSource控件。添加一個GridView控件到頁面上,並且設置它的ID屬性爲ProductBySupplier,然後配置它使用新的名爲ProductBySupplierDataSource的ObjectDataSource控件。因爲我們想通過選擇供應商來使GridView顯示那些產品,我們需要使用ProductBLL類的GetProductsBySupplierID(supplierID)方法。同時需要把剛剛添加的重載方法UpdateProduct映射到ObjectDataSource的Update()方法。

 

圖11:配置ObjectDataSource使用添加的UpdateProduct重載方法

 接下來提示要我們爲GetProductsBySupplierID(supplierID)方法的supplierID輸入參數選擇參數源。因爲我們想要在DetailsView中顯示供應商的產品信息,我們使用SuppliersDetails這個DetailsView控件的SelectedValue屬性作爲參數源。

 

圖12:使用SuppliersDetails DetailsView控件的SelectedValue屬性作爲參數源

 回到GridView中,除去ProductName,QuantityPerUnit,還有Discontinued以外的其他成員,並標記Discontinued CheckBoxField爲read-only。而且,在GridView的智能標籤中勾上Enable Editing這個選項。做完這些改變之後,GridView和ObjectDataSource聲明標記應該和下面的相似:

 1<asp:GridView ID="ProductsBySupplier" runat="server" AutoGenerateColumns="False" DataKeyNames="ProductID"
 2
 3    DataSourceID="ProductsBySupplierDataSource" EnableViewState="False">
 4
 5    <Columns>
 6
 7        <asp:CommandField ShowEditButton="True" />
 8
 9        <asp:BoundField DataField="ProductName" HeaderText="Product" SortExpression="ProductName" />
10
11        <asp:BoundField DataField="QuantityPerUnit" HeaderText="Qty/Unit" SortExpression="QuantityPerUnit" />
12
13        <asp:CheckBoxField DataField="Discontinued" HeaderText="Discontinued" ReadOnly="True"
14
15            SortExpression="Discontinued" />
16
17    </Columns>
18
19</asp:GridView>
20
21
22
23<asp:ObjectDataSource ID="ProductsBySupplierDataSource" runat="server" OldValuesParameterFormatString="original_{0}" SelectMethod="GetProductsBySupplierID"
24
25    TypeName="ProductsBLL" UpdateMethod="UpdateProduct">
26
27    <UpdateParameters>
28
29        <asp:Parameter Name="productName" Type="String" />
30
31        <asp:Parameter Name="quantityPerUnit" Type="String" />
32
33        <asp:Parameter Name="productID" Type="Int32" />
34
35    </UpdateParameters>
36
37    <SelectParameters>
38
39        <asp:ControlParameter ControlID="SupplierDetails" Name="supplierID" PropertyName="SelectedValue"
40
41            Type="Int32" />
42
43    </SelectParameters>
44
45</asp:ObjectDataSource>

 和前面一個ObjectDataSource一樣,這個ObjectDataSource控件的OldValuesParameterFormatString設置成了original_{0},會在更新產品名稱及其單價時候出現問題。可以從聲明中除去這個屬性或者把它的值設置爲默認的{0}。在配置完成之後,我們的頁面現在可以在GridView中顯示被選擇的供應商的產品信息(見圖13)。現在任何一個產品的名字以及單價都是可以編輯更新的。然後,我們需要更新代碼邏輯,以確保爲指定供應商相關的用戶過濾那些已經廢棄的產品。我們將在第四步的最後實現它。

 

圖13:選擇的供應商的產品被顯示

 注意:通過添加可編輯的GridView控件,在Suppliers下拉框的SelectedIndexChanged事件處理器中,我們應該更新GridView回到只讀狀態。否則,如果在編輯一個產品信息的同時,更換一個供應商,一個相應的GridView的索引將會保持GridView的可編輯狀態。爲了避免發生這類事情,可以在SelectedIndexChanged事情中很簡單地設置GridView的EditIndex屬性爲-1。

 第五步:當“顯示/編輯所有供應商”沒有被選擇時候,不允許編輯已經廢棄的產品

 儘管ProductsBySupplier GridView已經功能全面,它當前爲來自指定供應商的用戶提供了太多的訪問權限。根據我們的業務規則,用戶不能夠更新廢棄的產品。爲了執行這些,當來自某個指定供應商的用戶訪問這個頁面時,我們會在GridView中這些廢棄的產品前隱藏掉編輯按鈕。

爲GridView的RowDataBound事件新建一個事件處理器。在事件處理器中我們需要決定用戶是否和某個供應商相關聯,對於教程中,我們可以通過Suppliers這個下拉框來決定-如果不是-1,那麼用戶就是和當前供應商關聯的。對於某些用戶,我們需要決定產品是否已經廢棄。我們可以通過e.Row.DataItem屬性來獲得一個綁定到GridView上的ProductRow實例,正如我們在在GridView的頁腳顯示摘要信息教程中所提及到的。如果產品過期了,我們可以使用在前面教程-爲刪除數據添加客戶端確認中所討論的技術,在GridView的CommandFild中獲取一個編輯按鈕的引用,一旦我們獲得這個引用,我們就可以隱藏或者刪除這個按鈕。

 1protected void ProductsBySupplier_RowDataBound(object sender, GridViewRowEventArgs e)
 2
 3{
 4
 5    if (e.Row.RowType == DataControlRowType.DataRow)
 6
 7    {
 8
 9        // Is this a supplier-specific user?
10
11        if (Suppliers.SelectedValue != "-1")
12
13        {
14
15            // Get a reference to the ProductRow
16
17            Northwind.ProductsRow product = (Northwind.ProductsRow)((System.Data.DataRowView)e.Row.DataItem).Row;
18
19
20
21            // Is this product discontinued?
22
23            if (product.Discontinued)
24
25            {
26
27                // Get a reference to the Edit LinkButton
28
29                LinkButton editButton = (LinkButton)e.Row.Cells[0].Controls[0];
30
31
32
33                // Hide the Edit button
34
35                editButton.Visible = false;
36
37            }
38
39        }
40
41    }
42
43}

 在事件處理器中,當一個來自指定供應商的用戶訪問這個頁面時候,那些被廢棄的產品將是不可編輯的,因爲這些產品的編輯按鈕不再可見。比如,Chef Anton’s Gumbo Mix對於New Orleans Cajun Delights供應商來講是一個廢棄產品。當這個供應商的用戶訪問這個頁面時候,這個產品的編輯按鈕是不可見的(見圖14)。然而,當使用“顯示/編輯所有供應商”訪問頁面時候,編輯按鈕是可見的(見圖15)。

 

圖14:對於指定供應商用戶,Chef Anton’s Gumbo Mix的編輯按鈕是被隱藏的

 

圖15:對於“顯示/編輯所有供應商”用戶,Chef Anton’s Gumbo Mix的編輯按鈕是可見的

 在業務邏輯層中檢查訪問權限

 在這篇教程中,ASP.NET通過處理邏輯讓用戶可以訪問那些信息以及哪些產品他可以更新。概念上來講,這些邏輯也可以在業務邏輯成完成。例如,在SuppliersBLL類中的GetSuppliers()方法可以包含一個檢測,以保證當前登錄的用戶不和一個指定的供應商相關。同樣,UpdateSupplierAddress方法可以添加一個檢測,確保當前登錄用戶既可以是來自我們公司(因此可以更新所有供應商的地址資料),或者跟一個特定的供應商相關,只能更新他的數據。在這裏我不會包含任何業務邏輯層的檢測代碼,因爲在教程中,用戶的權限由頁面的下拉框所決定,業務邏輯層的類代碼是無法訪問到的。當使用成員系統或者是ASP.NET提供的一種認證模式(比如Windows認證),當前登錄用戶的信息和角色信息可以通過業務邏輯層訪問到,因此在那種情況下在表現層和業務層都能夠進行權限判斷。

 總結

 大部分提供帳號的站點需要根據登錄用戶而定製不同的數據修改界面。管理員或許能夠刪除或編輯任何記錄,沒有管理權限的用戶可能被限制只能更新或刪除他們自己創建的數據。無論是哪類情況,數據web控件,ObjectDataSource,以及業務邏輯層的類可以用來擴展成基於登錄用戶的添加刪除功能。在這篇教程中,我們看到了如果根據用戶是否和某個供應商相關聯,或者是爲我們公司一員來限制顯示和編輯數據。

 這篇教程總結了使用GridView,DetailsView,以及FormView控件來添加,更新,刪除數據。從下一篇開始,我們將關注添加分頁和排序的功能支持。

 祝編程愉快!


 作者簡介

 Scott Mitchell,著有六本ASP/ASP.NET方面的書,是4GuysFromRolla.com的創始人,自1998年以來一直應用 微軟Web技術。Scott是個獨立的技術諮詢顧問,培訓師,作家,最近完成了將由Sams出版社出版的新作,24小時內精通ASP.NET 2.0。他的聯繫電郵爲[email protected],也可以通過他的博客http://ScottOnWriting.NET與他聯繫。



 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章