asp.net|iis
摘要:本文介紹如何通過建立三層結構式 ASP.NET 2.0 應用程式來維護 IIS 生產伺服器中的成員資格資料庫和角色資料庫。
簡介
成員資格編輯器
Microsoft Visual Studio 2005 版本中沒有用於維護 Microsoft IIS 中的成員資格資料庫和角色資料庫的“現成”解決方案。將開發環境中的應用程式移至 IIS 生產伺服器時這就會是個問題。Microsoft 提供的公用程式 ASP.NET Web Configuration 只能在非生產的開發環境中運行。本文及其關聯代碼將通過對成員和角色管理實現三層式解決方案,同時使用 Microsoft ASP.NET 標準工具,來解決這個問題。這意味著該公用程式將可在任何 ASP.NET 2.0 環境(包括 IIS)中運行。該解決方案十分靈活,可以輕易添加到任何現有的 ASP.NET 2.0 網站項目中。
該解決方案的層定義如下。第一層 ASP.NET 頁面(也稱為展示層)通過對象資料來源與兩個業務對象進行串連。這些業務對象起中介層作用,是成員和角色的封裝程式。第三層(即後端)由 ASP.NET 提供的成員資格和角色管理器 API 組成。中介層對象可以輕鬆地加入任何 ASP.NET 2.0 項目,並且幾乎無需進行任何更改就可以直接使用。
本文深入地介紹了中介層(即資料對象及其關聯 ObjectDataSource)的實現。接著,介紹了如何在使用 Microsoft SQL Server Express 2005(捆綁有 Visual Studio 2005)的 ASP.NET Web 項目中使用這些對象。但是由於 Microsoft 提供的成員資格 API 使用其供應商的技術,因此此處介紹的解決方案與資料庫無關。從 LDAP、SQL Server 或 Oracle 即可輕鬆獲得成員資格和角色資訊。
採用的技術
ObjectDataSource
定義了兩個 ObjectDataSource 執行個體。一個是有關成員資格資料(使用者名稱、建立日期、批准狀態等)的,另一個是有關角色(管理員、朋友等)的。這兩個資料來源均完全填充了所有資料存取方法,即兩者都包含執行插入、更新、刪除和選擇的 Member 函數。兩個 ObjectDataSource 執行個體都返回 Generic List 類型,這意味著在 GridView 中,列名將自動化佈建為 ObjectDataSource 的屬性值名。此外,還實現了自訂排序,以便使用者可以單擊 GridView 中的欄位標題來根據需要對資料進行正向或反向排序。
SQL Server Express 2005 和 Web.Config
成員資格資料庫和角色資料庫的資料提供者源是 SQL Server Express 2005。為實現這一點,需要在 web.config 檔案中設定相應的條目。本文稍後將對如何從頭開始設定新項目進行簡要的介紹。web.config 檔案中未提及 SQL Server Express 2005 的連接字串,因為它已在 Microsoft .NET 2.0 Framework 的預設部分 Machine.Config 檔案中定義。
支援 IIS(5.1 和 6.0)
Web 服務器可以為 5.1 版,也可以為 6.0 版。若要對登入 Web 應用程式的多個使用者進行測試,必須使用 IIS。內建開發 Web 服務器不能正確保持各不同登入使用者的狀態。內建開發 Web 服務器不能正確保持各不同登入使用者的狀態。儘管可以使 Asp.net Web 組態工具與 IIS 一起工作,但尚未完成實現這一目的所必需的附加安全工作。
GridView 控制項
GridView 用於顯示成員資格和角色的資料。如上文所述,由於使用了 ObjectDataSource 的 Generic 類型,GridView 的列名將自動以 ObjectDataSource 的屬性值命名。如果沒有使用 Generic 類型,則列名恢複為無意義的預設值,必須手動逐個進行編輯。
應用程式和項目
運行此公用程式所需的項目非常簡單,並且是獨立的。專案檔可以下載,包含功能完整的樣本。由於使用者和角色沒有直接存取資料庫的許可權,因此所要做的事情就是擷取三個資料對象(MembershipDataObject.cs、MembershipUserSortable.cs 和 RoleDataObject.cs,請參見圖 2)。
圖 2:成員資格編輯器項目
SamplePages 檔案夾中有幾個其他的樣本,示範了前面提及的模組的用法。圖 1 中顯示的 Membership.aspx 即是其中一例,它可用於選擇、更新、插入及刪除成員和角色,以及為成員分配角色。
使用已有工作成員資格模組的工作 ASP.NET 2.0 應用程式時,無需對這些頁面進行已做配置之外的外部配置。可以將這些檔案直接複製到項目中,複製後即可使用。
如果是第一次在應用程式中實現成員資格和角色管理,則建立使用這些對象的解決方案的過程如下,
1. 使用 Visual Studio 2005 建立類型為 ASP.NET 網站的新 Web 項目。
2. 單擊菜單上的 Website / ASP.NET Configuration(網站/ASP.NET 配置)。
3. 按照嚮導提示的步驟(1 至 7)進行操作來建立一些樣本使用者和角色。這將在當前項目中有效地建立有效 web.config 檔案,其中包含能夠啟動並運行成員管理的充足資訊。預設情況下,它將在其預設配置中使用 SQL Server Express 2005。
4. 在項目中添加三個 .cs 檔案,然後添加樣本 .aspx 頁面作為樣本。
ObjectDataSource 詳細資料
採用 ObjectDataSource 技術可以建立作用與 SqlDataSource 非常相似的資料來源,即它提供允許從永久資料存放區區(例如資料庫)中進行選擇、更新、插入和刪除記錄(或類似記錄的對象)的介面。本文以下各部分將討論 ObjectDataSource 用於操作成員資格的對象(即類檔案)。其在項目中的名稱為 MembershipUserODS.cs。
類 (MembershipUserODS)
由於是通過 Microsoft 成員資格 API 檢索資料,因此使用 ObjectDataSource 來解決問題。第一步是建立獨立的類,該類對 MembershipUser 進行封裝,以便它可以與 ObjectDataSource 關聯。下例中介紹了一組需要實現的典型方法,本文以下各部分將介紹如何?每個成員函數。本文省略了許多細節,但本文附帶的原始碼中包含這些細節。
[DataObject(true)
public class MembershipUserWrapper {
[DataObjectMethod(DataObjectMethodType.Select, true)]
static public Collection<MembershipUserWrapper> GetMembers(string
sortData) {
return GetMembers(true, true, null, sortData);
}
[DataObjectMethod(DataObjectMethodType.Insert, true)]
static public void Insert(string UserName, bool isApproved,
string comment, DateTime lastLockoutDate, ...) {
}
[DataObjectMethod(DataObjectMethodType.Delete, true)]
static public void Delete(object UserName, string Original_UserName){
Membership.DeleteUser(Original_UserName, true);
}
[DataObjectMethod(DataObjectMethodType.Update, true)]
static public void Update(string original_UserName,string email,...){
}
}
類聲明
上面顯示的類聲明因具有屬性 [(DataObject(true)],比較特殊。此屬性告訴 Visual Studio 2005 ObjectDataSource 建立嚮導,在資料類中搜尋 DataObject 時只尋找具有此特殊屬性的成員。請參閱本部分中介紹在何處為 GridView 組件分配此類的樣本。
Insert 方法
各部分的細節都涉及以非常簡單的方式使用 Microsoft 提供的成員資格 API。例如,下面是一個較詳細的典型 Insert 方法。
[DataObjectMethod(DataObjectMethodType.Insert,true)]
static public void Insert(string userName, string password,)
{
MembershipCreateStatus status;
Membership.CreateUser(userName, password,);
}
此類 Insert 是多態的,這意味著可以存在用於不同目的的多個 Insert 方法。例如,動態決定是否應該根據環境批准建立的使用者時,可能需要使用它。又如,在管理螢幕中建立的新使用者可能想建立預設為已獲批准的使用者,而使用者註冊螢幕可能預設為未批准。為此,需要另一個具有額外參數的 Insert 方法。可實現此目標的 Insert 方法大致如下。
[DataObjectMethod(DataObjectMethodType.Insert,false)]
static public void Insert(string userName, string password, bool isApproved)
{
MembershipCreateStatus status;
Membership.CreateUser(UserName, password,,
isApproved, out status);
}
與此處所列的其他方法一樣,顯示的樣本並非附帶源中實際存在的樣本。此處的樣本是為了說明各個方法的典型用法。原始碼中包含的用法更為完備且帶有注釋。
Update 方法
Update 方法是實現成員資格 API 的一種非常簡單的方法。與 Insert 方法一樣,Update 方法也可以有多種實現。此處只介紹一種實現。在可下載的代碼中,有更多 Update 的多態實現,其中一種只設定 IsApproved 屬性(如下例所示)。
[DataObjectMethod(DataObjectMethodType.Update,false)]
static public void Update(string UserName,bool isApproved)
{
bool dirtyFlag = false;
MembershipUser mu = Membership.GetUser(UserName);
if (mu.isApproved != isApproved)
{
dirtyFlag = true;
mu.IsApproved = isApproved;
}
if (dirtyFlag == true)
{
Membership.UpdateUser(mu);
}
}
Delete 方法
Delete 方法是最簡單的方法,它只使用一個參數 UserName。
static public void Delete(string UserName)
{
Membership.DeleteUser(UserName,true);
}
具有 Sort 屬性的 Select 方法
在本樣本中,Select 方法 GetMembers 具有多個組件,每個組件都值得介紹。首先介紹其返回的值,然後是方法本身,最後介紹其如何排序傳回值。
Select 方法的傳回值(類型為 Collection)
Select 方法(也稱為 Get)的傳回值為 Generic Collection 類。使用 Generic 是因為最終與該類關聯的 ObjectDataSource 使用反射來確定列名和類型。這些名稱和類型與返回的每行資料相關聯。此方法與 SqlDataSource 使用表或預存程序的資料庫中繼資料來確定每行的列名相同。由於 Select 方法的傳回型別為 MembershipUserWrapper(繼承自 MembershipUser),此類的大多數屬性都是與 MembershipUser 關聯的相同屬性。這些屬性包括,
• ProviderUserKey
• UserName
• LastLockoutDate
• CreationDate
• PasswordQuestion
• LastActivityDate
• ProviderName
• IsLockedOut
• Email
• LastLoginDate
• IsOnline
• LastPasswordChangedDate
• Comment
在此插一句,屬性值有一個非常好的特點 - 它們可以是唯讀(無設定方法)、唯寫的(無讀取方法),當然也可以是讀/寫的。ObjectDataSource 嚮導考慮到了這一點,並建立了相應的參數,這樣在使用 ObjectDataSource 呈現資料控制項時,只有可更新(讀/寫)的欄位能夠編輯。這意味著您不能更改某些屬性,例如 UserName 屬性。如果這一點現在還不清楚,稍後在我們更詳細地闡述 ObjectDataSource 和資料群組件時,就容易明白。
Select 方法本身
與 Insert 和 Update 方法一樣,Select 方法也是多態的。有多少種情況,就可以有多少種 Select 方法。例如,最好能夠使用 Select 方法按照使用者的批准狀態(已批准、未批准或兩者)來選擇使用者。通常,有一個 Get 方法具有與其關聯的儘可能多的參數,其他 Get 方法對其進行調用。在我們的樣本中,有三個 Get 方法,一個檢索所有記錄,一個根據批准狀態檢索記錄,一個根據選擇字串檢索單個記錄。下例介紹的是調用返回所有使用者的方法。將兩個布爾值均設定為 true,可以返回所有使用者。
[DataObjectMethod(DataObjectMethodType.Select, true)]
static public List<MembershipData> GetMembers(string sortData)
{
return GetMembers(true,true,null,null);
}
下面的樣本介紹了一個較詳細的 Get 方法。此樣本僅介紹方法的開頭部分,未介紹方法的詳細資料,包括完成屬性分配、按批准狀態篩選並拒絕不滿足條件的記錄,以及應用排序條件。此樣本後面是有關排序條件的詳細說明。(請注意,對包含數百個使用者 [不超過五百] 的資料庫調用 GetAllUsers,很快就會成為代價非常高昂的操作。)
[DataObjectMethod(DataObjectMethodType.Select, true)]
static public List<MembershipData> GetMembers(bool AllApprUsers,
bool AllNotApprUsers, string UserToFind, string sortData)
{
List<MembershipData> memberList = new List<MembershipData>();
MembershipUserCollection muc = Membership.GetAllUsers();
foreach (MembershipUser mu in muc)
{
MembershipData md = new MembershipData();
md.Comment = mu.Comment;
md.CreationDate = mu.CreationDate;
...
自訂排序條件
請注意,在前面的代碼中,名為 sortData 的參數字串傳遞到了 GetMembers 中。如果在 ObjectDataSource 聲明中,SortParameterName 被指定為其一個屬性,則此參數將自動傳遞到所有 Select 方法。其值將為資料控制項列中的 SortExpression 屬性指定的名稱。在我們的樣本中,資料控制項為 GridView。
Comparer 方法是根據傳遞給 GetMembers 方法的 sortName 參數調用的。由於這些 ASP.NET 網頁無狀態,因此必須假定當前排序的方向(正向或反向)儲存在檢視狀態中。每次調用都顛倒前一次調用的方向。即使用者單擊欄位標題時,在正向排序和反向排序之間切換。
假定使用的是 GridView,傳遞到 GetMembers(sortData) 的參數中包含 GridView 列的屬性 SortExpression 中的資料。如果請求反向排序,則“DESC”一詞附加在排序字串後面。例如,使用者第一次單擊 Email 列時,傳遞到 GetMembers 的 sortData 為“Email”。使用者第二次單擊該列時,參數 sortData 就變為“Email DESC”,然後是“Email”、“Email DESC”,依此類推。特別需要注意的是,第一次載入頁面時,傳遞的 sortData 參數是零長度的字串(非空)。下面是 GetMembers 方法的一部分,該方法檢索資料並對其進行排序,以便按正確的順序返回這些資料。
[DataObjectMethod(DataObjectMethodType.Select, true)]
static public List<MembershipData> GetMembers(string sortData)
{
List<MembershipData> memberList = new List<MembershipData>();
MembershipUserCollection muc = Membership.GetAllUsers();
List<MembershipUser> memberList = new List<MembershipUser>(muc);
foreach (MembershipUser mu in muc)
{
MembershipData md = new MembershipData(mu);
memberList.Add(md);
}
... Code that implements Comparison
memberList.Sort(comparison);
return memberList;
}
在下一部分中,將此併入 GridView 中後,就比較清楚了。
ObjectDataSource 聲明
聲明 ObjectDataSource 最簡單的方法是,先使用 Visual Studio 2005 嚮導建立一個空的 ASP.NET 頁面,然後將資料控制項中的資料控制項拖放到工具列中。建立 ObjectDataSource 後,可以擷取建立 ObjectDataSource 右上方的小標記;然後單擊 Configure Data Source(配置資料來源)開啟一個嚮導,其中顯示“Configure Data Source-ObjectDataSource1”(配置資料來源 - ObjectDataSource1)(請參見圖 3)。
圖 3:配置 ObjectDataSource
此時,將顯示可與 ObjectDataSource 關聯的兩個類。MembershipUserODS 是本文的主要主題。RoleDataObject 基本相同,但其封裝成員資格角色。另外,請記住,此處顯示的只是聲明具有特殊類屬性 [DataObject(true)](在“類定義”中介紹)的對象。
選擇 MembershipUserODS 後,將顯示一個具有四個選項卡的對話方塊。要通過 MembershipUserODS 類調用的方法將在這些選項卡中定義。Select、Update、Insert 和 Delete 方法將與 MembershipUserODS 中的成員函數關聯。在許多情況下,類中都有多種方法適用於其中每種情況。必鬚根據所需資料方案選擇一個適當的方法。圖 4 中顯示了這四個選項卡。預設情況下,將在這些選項卡中填充標有特殊屬性 [DataObjectMethod(DataObjectMethodType.Select, false)] 的成員。當然,此特殊屬性是 Select 的預設值。將運算式 DataObjectMethodType.Select 改為 DataObjectMethodType.Insert、DataObjectMethodType.Update 和 DataObjectMethodType.Delete 將為其他選項卡確定相應的預設值。第二個參數是一個布爾值,表示此方法(請記住,它能以多態方式定義)是預設方法,應在索引標籤控制項中使用。
Select 方法
如前面在介紹 MembershipUserODS 類的部分中所述,GetMembers 函數返回 Generic Collection 類。這樣,此處定義的 ObjectDataSourceMembershipUser 控制項可以使用反射,並確定與 GetMembers 調用關聯的調用參數。在本樣本中,用於調用 GetMembers 的參數是 returnAllApprovedUsers、returnAllNotApprovedUsers、userNameToFind 和 sortData。基於此,新 ObjectDataSource 的實際定義如下。
圖 4:指定 Select 方法
<asp:ObjectDataSource ID="ObjectDataSourceMembershipUser"runat="server"
SelectMethod="GetMembers" UpdateMethod="Update"
SortParameterName="SortData"
TypeName="MembershipUtilities.MembershipDataODS"
DeleteMethod="Delete" InsertMethod="Insert" >
<SelectParameters>
<asp:Parameter Name="returnAllApprovedUsers" Type="Boolean" />
<asp:Parameter Name="returnAllApprovedUsers" Type="Boolean"/>
<asp:Parameter Name="usernameToFind" Type=" String" />
<asp:Parameter Name="sortData" Type=" String" />
</SelectParameters>
...
...
</asp:ObjectDataSource>
Insert 方法
在本樣本中,Insert 方法被指定給成員函數 Insert()。請注意,調用此方法時只使用了兩個參數,UserName 和 Password(請參見圖 5)。參數的數目必須等於 ObjectDataSource 中聲明的參數的數目。ObjectDataSource 中的參數聲明如下所示。另一個定義的函數為 Insert Member,用於添加第三個參數,approvalStatus。如果此 ObjectDataSource 的功能要包括在設定 approvalStatus 時進行插入操作,則應從下拉式清單中選擇其他 Insert 方法。這會導致以下 InsertParameters 插入 .aspx 頁面中。如果選擇包含兩個參數的方法,則塊中不會包括名為 isApproved 的 asp:Parameter。請記住,本樣本可能與附帶的原始碼不一致,此處僅作為樣本。附帶的原始碼要完整得多。
圖 5:指定 Insert 方法
<asp:ObjectDataSource ID="ObjectDataSourceMembershipUser"runat="server"
SelectMethod="GetMembers"UpdateMethod="GetMembers"
SortParameterName="SortData"
TypeName="MembershipUtilities.MembershipDataObject"
DeleteMethod="Delete" InsertMethod="Insert">
<InsertParameters>
<asp:Parameter Name="userName" Type="String" />
<asp:Parameter Name="password" Type="String" />
<asp:Parameter Name="isApproved" Type="Boolean" />
</InsertParameters>
...
</asp:ObjectDataSource>
請記住,如果使用的是具有最少參數的 Insert 方法,則需要在方法中設定預設密碼。在生產系統中,這是個糟糕的辦法。有關如何處理插入的更好樣本,請參閱附帶的原始碼。具體地說,請參閱 Membership.aspx 頁面瞭解此功能。
Update 方法
在本樣本中,Update 方法被指定給成員函數 Update()。請注意,調用此方法時使用了多個參數,UserName、Email、isApproved 和 Comment(請參見圖 6)。此外,還有一種 Update 方法,它包含所有可更新參數。如果要建立具有儘可能多的更新功能的控制項,這很有用。與 Insert 一樣,為此 ObjectDataSource 選擇適當的 Update 方法。完成嚮導後,將自動建立 UpdateParameters,如下所示。
圖 6:指定 Update 方法
<asp:ObjectDataSource ID="ObjectDataSourceMembershipUser"runat="server"
SelectMethod="GetMembers" InsertMethod="Insert"
SortParameterName="SortData"
TypeName="MembershipUtilities.MembershipUserODS"
UpdateMethod="Update" DeleteMethod="Delete">
<UpdateParameters>
<asp:Parameter Name="Original_UserName" />
<asp:Parameter Name="email" Type="String" />
<asp:Parameter Name="isApproved" Type="Boolean" />
<asp:Parameter Name="comment" Type="String" />
</UpdateParameters>
...
...
</asp:ObjectDataSource>
Delete 方法
在本樣本中,Delete 方法被指定給成員函數 Delete()。當然,只需一個 Delete 方法(請參見圖 7)。下面是支援此 Delete 方法的 ObjectDataSource 的聲明。
圖 7:指定 Delete 方法
<asp:ObjectDataSource ID="ObjectDataSource1" runat="server"
SelectMethod="GetMembers" InsertMethod="Insert"
SortParameterName="SortData"
TypeName="MembershipUtilities.MembershipUserODS"
UpdateMethod="Update" DeleteMethod="Delete">
<DeleteParameters>
<asp:Parameter Name="UserName" />
<asp:Parameter Name="Original_UserName" />
</DeleteParameters>
...
</asp:ObjectDataSource>
類 (RoleDataObject)
與成員資格一樣,設定角色時也使用其自己的 DataObject。由於角色無特殊之處,本文不對其設定進行詳細介紹。瞭解成員資格 DataObject 的設定方式後,即可瞭解角色的設定方式。在成員資格中,封裝成員資格 API 的 Microsoft C# 對象是 MembershipDataObject.cs。封裝角色 API 的相似類是 RoleDataObject.cs。
GridView 中的 ObjectDataSource(資料控制項)
本文的前面部分中已建立了成員資格使用者和角色的類聲明。此外,還在 ASP.NET 頁面中加入了完整的 ObjectDataSource 對象。最後一步是建立使用者介面,也稱為應用程式的使用者互動層或展示層。由於建立的對象完成了這麼多的工作,因此所需做的只是建立簡單的 GridView 並將其與 ObjectDataSource 關聯。步驟如下,
1. 在 ASP.NET 頁面設計器的可視模式下,將 GridView 資料群組件拖放到先前建立的 ObjectDataSource 關聯頁面中。
2.啟用選擇、刪除、更新、插入和排序。
圖 8 顯示的是與配置 Gridview 關聯的對話方塊。
圖 8:配置 GridView
此處應特別注意,下面顯示的 GridView 控制項中的 DataKeyNames 是自動化佈建的。這是因為,在具有屬性 [DataObjectField(true)] 的 MembershipUserSortable 類中對主鍵添加了標記,如下所示。請注意,由於 UserName 是 MembershipUser 類的屬性,需要在擴充 MembershipUser 的類中提供預設屬性。由於是唯讀屬性,因此只聲明了 Get 方法(對於 MembershipUser,UserName 是公用虛擬)。
[DataObjectField(true)]
public override string UserName {
get { return base.UserName;
}
GridView 中有一個屬性必須手動設定,必須在控制項中設定主鍵。為此,需要將屬性 DataKeyName 與 UserName 相關聯。GridView 聲明如下。
<asp:GridView ID="GridView1" DataKeyNames="UserName" runat="server"
AllowPaging="True" AutoGenerateColumns="False"
DataSourceID="ObjectDataSourceMembershipUser"
AllowSorting="True">
<Columns>
...
...
結論
至此,您現在應熟悉如何建立自己的三層結構式 ASP.NET 應用程式。此外,目前還要有兩個可任意使用來封裝成員和角色的對象。例如,現在可以使用 DetailView 控制項,在幾分鐘內建立一個針對成員的完整 DetailView 介面,用於對成員進行導航、插入、更新及刪除操作。試一試吧!
我並未具體介紹如何?添加、更新和刪除成員或角色。如果您查看原始碼,就會發現我使用 API 的方法非常簡單。在此詳細介紹那些調用並無多大用處,因為我確信,如果您仍在閱讀本文,您會和我一樣,邊學邊實踐。
今年我有幸參加了在奧蘭多舉辦的 MS TechEd 和在洛杉磯舉辦的 PDC,有機會向 ASP.NET 小組請教了許多問題。特別感謝 Brad Millington 和 Stefan Schackow 在這幾周解答了我提出的許多問題,感謝 Jeff King 和 Brian Goldfarb 對本文進行潤色提供的所有協助。從某些方面來講,本文是對提供過協助的人的回報,希望他們將來不必回答這麼多問題。