建立一个使用.Net 2.0 MemberShip功能的标准例程(三)——绑定访问篇


经过上一章的例子  我们已经建立了一个标准的,有很多有趣(甚至有些是专业级)功能的登陆系统了。
可是我们如何管理这个系统呢?难道我们要用m$提供的asp.net管理工具管理一辈子么?

——当然不!那太可怕了!T_T

——我们要自己写一个后台,一个可以根据用户权限自己修改的后台!@_@

当然有一种数据库狂人,他们只冷冷的瞥了几眼m$提供的数据库结构,轻描淡写的破译了其中所有的奥妙之处,随手拖了5-6个grid 写了10多行SQL 就用数据库方式搞定了。对于这种高手,我们仰慕,我们恨不得马上吸光他的百年功力,然后杀之后快,NND.

但是成为这样的高手需要非常的经验和手腕。我们这些小菜鸟,没有写轮眼,也不是圣斗士,所谓“看穿”技能在我们的身上是不能工作的。我们只有membership标准对象  profile标准对象  和roles标准对象。

难道就不能很方便的通过绑定方式访问这些对象么?





通过页面访问较为复杂的对象——在.net 1.x 的时代——对我们曾是一种煎熬。明明好多对象有着数据行的特性,为什么不能直接访问呢?于是好多人---包括我,尝试过各种办法。我是失败那批5555,也有很多的人成功了,研究出一些很有效的办法。可是这个状况没有持续多久——自从.net 2.0推出了ODS ,我的失败阴影就再也不复回来~~~




1     用ODS绑定MemberShip的总体思想

如图所示
      


从战略上 我们把每个用户看成一个行,把Membership中的GetAllUsers ()看成一个Select语句。
但是一个标准的System.Web.Security.MembershipUser并不具有数据绑定对象的特性 
比如主键/只读等信息所以我们可以重写它为它添加上这些特性

2  简单的绑定方法
这里采用的是MSDN上的部分c#代码修改成的VB代码  顺便我也把版权信息粘贴上来
'/*
'
Copyright ?2005, Peter Kellner
'
All rights reserved.
'
http://peterkellner.net

'Redistribution and use in source and binary forms, with or without
'
modification, are permitted provided that the following conditions
'
are met:

'- Redistributions of source code must retain the above copyright
'
notice, this list of conditions and the following disclaimer.

'- Neither Peter Kellner, nor the names of its
'
contributors may be used to endorse or promote products
'
derived from this software without specific prior written 
'
permission. 

'THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
'
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
'
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
'
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 
'
COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
'
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES INCLUDING,
'
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
'
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 
'
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 
'
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 
'
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
'
POSSIBILITY OF SUCH DAMAGE.
'
*/


Imports System
Imports System.Data
Imports System.Configuration
Imports System.Web
Imports System.Web.Security
Imports System.Web.UI
Imports System.Web.UI.WebControls
Imports System.Web.UI.WebControls.WebParts
Imports System.Web.UI.HtmlControls
Imports System.Collections.Generic
Imports System.ComponentModel

 _



'/ <summary>
'
/ Summary description for MembershipUserWrapper
'
/ This class is inherited from MembershipUser 
'
/ Using the sytax public class ClassName (..) : base(initializers) allows for calling the
'
/ contstructor of the base class.  In this case MembershipUser.
'
/ </summary>
'


Namespace Membership_Tool


    
Public Class MembershipUserWrapper
        
Inherits MembershipUser


        
'/ <summary>
        '/ This constructor is used to create a MembershipUserWrapper from a MembershipUser object.  MembershipUser is a default type used
        '/ in the Membership API provided with ASP.NET 2.0
        '/ </summary>
        '/ <param name="mu">MembershipUser object</param>
        Public Sub New(ByVal mu As MembershipUser)
            
MyBase.New(mu.ProviderName, mu.UserName, mu.ProviderUserKey, mu.Email, mu.PasswordQuestion, mu.Comment, mu.IsApproved, mu.IsLockedOut, mu.CreationDate, mu.LastLoginDate, mu.LastActivityDate, mu.LastPasswordChangedDate, mu.LastLockoutDate)
        
End Sub
 'New<DataObjectField(True)>  _



        
'/ <summary>
        '/ This calls the base class UserName property.  It is here so we can tag
        '/ this property as the primary key so that datakeynames attribute gets set in the data control.
        '/ </summary>
        '
        <DataObjectField(True)> _
        
Public Overrides ReadOnly Property UserName() As String
            
Get
                
Return MyBase.UserName
            
End Get
        
End Property




        
'/ <summary>
        '/ This constructor is used to create a MembershipUserWrapper from individual parameters values.  
        '/ For details of what each parameter means, see the Microsoft Membership class.
        '/ </summary>
        '/ <param name="comment">Passes to MembershipUser.comment</param>
        '/ <param name="creationDate">Passes to MembershipUser.creationDate</param>
        '/ <param name="email">Passes to MembershipUser.email</param>
        '/ <param name="isApproved">Passes to MembershipUser.isApproved</param>
        '/ <param name="lastActivityDate">Passes to MembershipUser.lastActivityDate</param>
        '/ <param name="lastLoginDate">Passes to MembershipUser.lastLoginDate</param>
        '/ <param name="passwordQuestion">Passes to MembershipUser.passwordQuestion</param>
        '/ <param name="providerUserKey">Passes to MembershipUser.providerUserKey</param>
        '/ <param name="userName">Passes to MembershipUser.userName</param>
        '/ <param name="lastLockoutDate">Passes to MembershipUser.lastLockoutDate</param>
        '/ <param name="providerName">Passes to MembershipUser.providerName</param>
        '
        Public Sub New(ByVal comment As StringByVal creationDate As DateTime, ByVal email As StringByVal isApproved As BooleanByVal lastActivityDate As DateTime, ByVal lastLoginDate As DateTime, ByVal passwordQuestion As StringByVal providerUserKey As ObjectByVal userName As StringByVal lastLockoutDate As DateTime, ByVal providerName As String)
            
MyBase.New(providerName, userName, providerUserKey, email, passwordQuestion, comment, isApproved, False, creationDate, lastLoginDate, lastActivityDate, DateTime.Now, lastLockoutDate)
        
End Sub
 'New
    End Class
 'MembershipUserWrapper
    ' This calls a constructor of MembershipUser automatically because of the base reference above

End Namespace

这样MembershipUser  的Username  被标记成 只读/主键 在绑定的时候  GridView会识别这一说明生成相应的编辑模式模板。






数据行已经被我们建立好了,那么数据从哪里来呢?我们建立一个新类MembershipUserODS 把提供我们的数据的方法集中在这个类中,它就能起到Dataadepter的作用。
    <DataObject(True)> _
    
Public Class MembershipUserODS

    
End Class

其中加入如下方法来实现数据的读取,也就是Select操作:
    <DataObjectMethod(DataObjectMethodType.Select, False)> Public Shared _
Function GetMembers(ByVal returnAllApprovedUsers As BooleanByVal returnAllNotApprovedUsers As BooleanByVal usernameToFind As StringAs List(Of MembershipUserWrapper)




            
Dim memberList As New List(Of MembershipUserWrapper)


            
'看看是否只需要返回某个特定的用户
            If Not (usernameToFind Is NothingThen
                
'           {
                Dim mu As MembershipUser = Membership.GetUser(usernameToFind)
                
If Not mu Is Nothing Then



                    
Dim md As MembershipUserWrapper = New MembershipUserWrapper(mu)
                    memberList.Add(md)
                
End If

            
Else

                
Dim muc As MembershipUserCollection = Membership.GetAllUsers()
                
Dim mu As MembershipUser
                
For Each mu In muc
                    
If returnAllApprovedUsers = True And mu.IsApproved = True Or (returnAllNotApprovedUsers = True And mu.IsApproved = FalseThen
                        
Dim md As New MembershipUserWrapper(mu)
                        memberList.Add(md)
                    
End If
                
Next mu
       
return  memberList
end Function

(在随后的源代码包里面有排序的功能 大家参考下就好)

这时候我们已经可以对 MembershipUserODS   进行数据绑定了----这个方法实现了数据绑定的最基础的动作:选择。

利用同样的方式  我们建立对应 插入、更新和删除操作的 方法:
插入:
 <DataObjectMethod(DataObjectMethodType.Insert, True)> Public Shared _
    
Sub Insert(ByVal userName As StringByVal isApproved As BooleanByVal comment As StringByVal lastLockoutDate As DateTime, ByVal creationDate As DateTime, ByVal email As StringByVal lastActivityDate As DateTime, ByVal providerName As StringByVal isLockedOut As BooleanByVal lastLoginDate As DateTime, ByVal isOnline As BooleanByVal passwordQuestion As StringByVal lastPasswordChangedDate As DateTime, ByVal password As StringByVal passwordAnswer As String)


            
' The incoming parameters, password and passwordAnswer are not properties of the
            ' MembershipUser class.  Membership has special member functions to deal with these
            ' two special properties for security reasons.  For this reason, they do not appear
            ' in a datacontrol that is created with this user object.  
            '
            ' the only reason you may want to have defaults is so you can build insert into your
            ' datacontrol.  A better approach would be to either follow the example shown in the
            ' Membership.asp page where the parameters are set directly to the userobject, or not
            ' include "new" at all in your control and use the other controls in the Membership API
            ' for creating new members.  (CreateUserWizard, etc)
            '
            ' It is recommended that you only enable the following lines if you are sure of what you are doing
            'if (password == null)
            '{
            '    password = "pass0word";
            '}
            'if (passwordAnswer == null)
            '{
            '    passwordAnswer = "Password Answer";
            '}


            
Dim status As MembershipCreateStatus
            Membership.CreateUser(userName, password, email, passwordQuestion, passwordAnswer, isApproved, status)

            
If status <> MembershipCreateStatus.Success Then
                
Throw New ApplicationException(status.ToString())
            
End If

            
Dim mu As MembershipUser = Membership.GetUser(userName)
            mu.Comment 
= comment
            Membership.UpdateUser(mu)
        
End Sub
 

更新:
   <DataObjectMethod(DataObjectMethodType.Update, True)> Public Shared _
    
Sub Update(ByVal UserName As StringByVal email As StringByVal isApproved As BooleanByVal comment As StringByVal lastActivityDate As DateTime, ByVal lastLoginDate As DateTime)
            
Dim dirtyFlag As Boolean = False

            
Dim mu As MembershipUser = Membership.GetUser(UserName)

            
If mu.Comment Is Nothing Or (mu.Comment & "").CompareTo(comment) <> 0 Then
                dirtyFlag 
= True
                mu.Comment 
= comment
            
End If

            
If mu.Email Is Nothing Or (mu.Email & "").CompareTo(email) <> 0 Then
                dirtyFlag 
= True
                mu.Email 
= email
            
End If

            
If mu.IsApproved <> isApproved Then
                dirtyFlag 
= True
                mu.IsApproved 
= isApproved
            
End If

            
If dirtyFlag = True Then
                Membership.UpdateUser(mu)
            
End If
        
End Sub
 

删除:
     <DataObjectMethod(DataObjectMethodType.Delete, True)> Public Shared _
    
Sub Delete(ByVal UserName As String)
            Membership.DeleteUser(UserName, 
True)
        
End Sub


以上的工作,我们在ODS和membership之间  建立了一条桥梁,通过我们的代码,membership 复杂的对象被我们用简单的数据属性所代理,而能够被ODS正确识别

下面我们简单的试验下我们工作的成果,亲手绑定一下。



建立一个新页面Default.aspx  在上面放置一个GridView和一个ODS控件。
效果如下图示:


选择“配置数据源”,你刚才建立的、带有<DataObject>声明的类便会被枚举出来:


下一步 选择每种动作的对应方法:



当你把4种基本动作全部配置完毕  ,在属性栏把ObjectDataSource1.OldValuesParameterFormatString 的内容设置为{0}  就可以绑定了:




基本上不会出现什么错误拉。。。。
 

同样的方式   你可以用如下的代码建立Roles和Profile 的绑定


Roles
'/*
'
Copyright ?2005, Peter Kellner
'
All rights reserved.
'
http://peterkellner.net

'Redistribution and use in source and binary forms, with or without
'
modification, are permitted provided that the following conditions
'
are met:

'- Redistributions of source code must retain the above copyright
'
notice, this list of conditions and the following disclaimer.

'- Neither Peter Kellner, nor the names of its
'
contributors may be used to endorse or promote products
'
derived from this software without specific prior written 
'
permission. 

'THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
'
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
'
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
'
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 
'
COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
'
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES INCLUDING,
'
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
'
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 
'
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 
'
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 
'
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
'
POSSIBILITY OF SUCH DAMAGE.
'
*/


Imports System
Imports System.Data
Imports System.Configuration
Imports System.Web
Imports System.Web.Security
Imports System.Web.UI
Imports System.Web.UI.WebControls
Imports System.Web.UI.WebControls.WebParts
Imports System.Web.UI.HtmlControls
Imports System.Collections.Generic
Imports System.ComponentModel
Imports System.Collections.ObjectModel

Namespace Membership_Tool



    
'/ <summary>
    '/ A class used to encapsulate the Roles in ASP.NET Membermanagement 2.0
    '/ </summary>
    <DataObject(True)> _
    
Public Class RoleDataODS
        
' This attribute allows the 

        
'/ <summary>
        '/ Used to get all roles available
        '/ </summary>
        '/ <returns></returns>
        '
        <DataObjectMethod(DataObjectMethodType.Select, True)> Public Overloads Shared _
       
Function GetRoles() As List(Of RoleData)
            
Return GetRoles(NothingFalse)
        
End Function
 'GetRoles


        
'/ <summary>
        '/ Returns a collection of RoleData type values.  This specialized constructor lets you request by
        '/ an individual user
        '/ </summary>
        '/ <param name="userName">if null and showOnlyAssignedRolls==false, display all roles</param>
        '/ <param name="showOnlyAssignedRolls">if true, just show assigned roles</param>
        '/ <returns></returns>
        <DataObjectMethod(DataObjectMethodType.Select, False)> Public Overloads Shared _
       
Function GetRoles(ByVal userName As StringByVal showOnlyAssignedRolls As BooleanAs List(Of RoleData)
            
Dim roleList As New List(Of RoleData)

            
Dim roleListStr As String() = Roles.GetAllRoles()
            
Dim roleName As String
            
For Each roleName In roleListStr
                
Dim userInRole As Boolean = False
                
' First, figure out if user is in role (if there is a user)
                If Not (userName Is NothingThen
                    userInRole 
= Roles.IsUserInRole(userName, roleName)
                
End If

                
If showOnlyAssignedRolls = False Or userInRole = True Then
                    
' Getting usersInRole is only used for the count below
                    Dim usersInRole As String() = Roles.GetUsersInRole(roleName)
                    
Dim rd As New RoleData()
                    rd.RoleName 
= roleName
                    rd.UserName 
= userName
                    rd.UserInRole 
= userInRole
                    rd.NumberOfUsersInRole 
= usersInRole.Length
                    roleList.Add(rd)
                
End If
            
Next roleName

            
' FxCopy will give us a warning about returning a List rather than a Collection.
            ' We could copy the data, but not worth the trouble.
            Return roleList
        
End Function
 'GetRoles


        
'/ <summary>
        '/ Used for Inserting a new role.  Doesn't associate a user with a role.
        '/ This is not quite consistent with this object, but really what we want.
        '/ </summary>
        '/ <param name="RoleName">The Name of the role to insert</param>
        <DataObjectMethod(DataObjectMethodType.Insert, True)> Public Shared _
       
Sub Insert(ByVal roleName As String)
            
If Roles.RoleExists(roleName) = False Then
                Roles.CreateRole(roleName)
            
End If
        
End Sub
 'Insert


        
'/ <summary>
        '/ Delete any given role while first removing any roles associated with existing users
        '/ </summary>
        '/ <param name="roleName">name of role to delete</param>
        <DataObjectMethod(DataObjectMethodType.Delete, True)> Public Shared _
       
Sub Delete(ByVal roleName As String)
            
' remove this role from all users.  not sure if deleterole does this automagically
            Dim muc As MembershipUserCollection = Membership.GetAllUsers()
            
Dim allUserNames(1As String

            
Dim mu As MembershipUser
            
For Each mu In muc
                
If Roles.IsUserInRole(mu.UserName, roleName) = True Then
                    allUserNames(
0= mu.UserName
                    Roles.RemoveUsersFromRole(allUserNames, roleName)
                
End If
            
Next mu
            Roles.DeleteRole(roleName)
        
End Sub
 'Delete
    End Class
 'RoleDataObject

    
'/ <summary>
    '/ Dataobject class used as a base for the collection
    '/ </summary>
    Public Class RoleData

        
' Non normalized column which counts current number of users in a role
        Private number_OfUsersInRole As Integer

        
Public Property NumberOfUsersInRole() As Integer
            
Get
                
Return number_OfUsersInRole
            
End Get
            
Set(ByVal value As Integer)
                number_OfUsersInRole 
= value
            
End Set
        
End Property

        
Private role_Name As String

        
<DataObjectField(True)> _
        
Public Property RoleName() As String
            
Get
                
Return role_Name
            
End Get
            
Set(ByVal value As String)
                role_Name 
= value
            
End Set
        
End Property

        
Public user_Name As String

        
Public Property UserName() As String
            
Get
                
Return user_Name
            
End Get
            
Set(ByVal value As String)
                user_Name 
= value
            
End Set
        
End Property

        
Private user_InRole As Boolean

        
Public Property UserInRole() As Boolean
            
Get
                
Return user_InRole
            
End Get
            
Set(ByVal value As Boolean)
                user_InRole 
= value
            
End Set
        
End Property

    
End Class
 'RoleData
End Namespace

Profile
Imports System
Imports System.Data
Imports System.Configuration
Imports System.Web
Imports System.Web.Security
Imports System.Web.UI
Imports System.Web.UI.WebControls
Imports System.Web.UI.WebControls.WebParts
Imports System.Web.UI.HtmlControls
Imports System.Collections.Generic
Imports System.ComponentModel
Namespace Membership_Tool
    
<DataObject(True)> _
    
Public Class ProfileODS
        
<DataObjectMethod(DataObjectMethodType.Select, False)> _
        
Public Function GetUserProfile(ByVal UserName As StringAs List(Of ProfileEntry)
            
Return GetUserProfile(UserName, Nothing)


        
End Function



        
<DataObjectMethod(DataObjectMethodType.Select, False)> _
    
Public Function GetUserProfile(ByVal UserName As StringByVal PropertyNames As StringAs List(Of ProfileEntry)

            
Dim doFilter As Boolean = False
            
Dim PNames As String() = Nothing
            
If PropertyNames Is Nothing Then
            
ElseIf PropertyNames = "" Then
            
Else
                PNames 
= PropertyNames.Split("|")
                doFilter 
= True
            
End If

            
Dim pelist As New List(Of ProfileEntry)
            
Dim pf As Profile.ProfileBase = System.Web.Profile.ProfileBase.Create(UserName)
            
'因为PrifileCommon的一个未知bug 在没有访问任何已知属性前,属性的集合将不可访问,所以需要一个缺省的属性来支持
            '以下为试图访问默认的属性bugjumper来获得属性集合
            Try
                
Dim x As String = pf.GetPropertyValue("bugjumper")
            
Catch ex As Exception
                
'  Throw New Exception("管理员并没有为profilecommon的bug设置默认的属性参数bugjumper")
            End Try
            
'处理bug过程结束


            
For Each itm As System.Configuration.SettingsPropertyValue In pf.PropertyValues

                
If itm.Name <> "bugjumper" Then '忽略处理bug用的公共属性
                    Dim doAdd As Boolean = False
                    
If Not doFilter Then
                        doAdd 
= True
                    
ElseIf Array.IndexOf(PNames, itm.Name) <> -1 Then
                        doAdd 
= True

                    
End If
                    
If doAdd Then
                        
Dim pe As New ProfileEntry(UserName, itm.Name, itm.PropertyValue)
                        pelist.Add(pe)
                    
End If
               
                
End If


            
Next




            
Return pelist

        
End Function

        
<DataObjectMethod(DataObjectMethodType.Update, False)> _
        
Public Sub UpdateUserProfile(ByVal UserName As StringByVal Key As StringByVal Value As String)
            
Dim pf As Profile.ProfileBase = System.Web.Profile.ProfileBase.Create(UserName)
            pf.SetPropertyValue(Key, Value)
            pf.Save()
        
End Sub

    
End Class








    
Public Class ProfileEntry
        
Private k, v As String
        
Private U As String
        
<DataObjectField(True)> _
   
Property Key() As String
            
Get
                
Return k
            
End Get
            
Set(ByVal value As String)
                k 
= value
            
End Set
        
End Property



        
Property Value() As String
            
Get
                
Return v
            
End Get
            
Set(ByVal value As String)
                v 
= value
            
End Set
        
End Property


        
<DataObjectField(True)> _
        
Property UserName() As String
            
Get
                
Return U
            
End Get
            
Set(ByVal value As String)
                U 
= value
            
End Set
        
End Property

        
Sub New(ByVal NewUserName As StringByVal NewKey As StringByVal NewValue As String)
            
Me.UserName = NewUserName
            
Me.Key = NewKey
            
Me.Value = NewValue
        
End Sub

    
End Class



End Namespace

ProFile可是我自己写的阿^_^

这里发现了一个bug,由vs2005 生成的profilecommon类 在第一次成功访问某属性前,Properties集合不会正确的枚举出所有成员,所以我在程序中作了一些小小的调整,具体请看示例工程:DDDD
 
下载地址  C#核心代码
-------------------------------
终于填完了  连续睡眠不足词不达意  回头再作修改555555
posted @ 2006-12-18 14:46  MSFT:waywa 韦恩卑鄙  阅读(6970)  评论(21编辑  收藏  举报