现在的位置: 首页 > 综合 > 正文

Profile 详解之 ProfileModule 类

2012年09月03日 ⁄ 综合 ⁄ 共 10887字 ⁄ 字号 评论关闭

Profile 类时用于用户 Profile 的创建和 Profile 事件的管理的,

当启用用户配置文件时,ASP.NET 使用 ProfileModule 来创建用户配置文件,

并且将其存储在当前的 HttpContext 的 Profile 属性中

public sealed class ProfileModule : IHttpModule

从这里的对 ProfileModule 的定义可以看出,

这个 ProfileModule 是实现了 IHttpModule 接口的,

同时其也是一个叶子类,也就是不允许继承,

既然其实现了 IHttpModule 接口的话,那么它便可以拦截住任何的 Request ,

并且可以对这些拦截的 Request 进行修改和控制后再传递给 ASP.NET 处理,

还有需要说明的就是这个 ProfileModule 还公开了三个事件

MSDN 中的说法是

ProfileModule 公开下面的事件,

您可以处理这些事件以在应用程序中配置身份验证:

  • MigrateAnonymous 事件,

  •        用于在匿名用户登录时将配置文件设置从匿名配置文件

  •        迁移到经过身份验证的配置文件。

  • Personalize 事件,用于自定义如何创建用户配置文件。

  • ProfileAutoSaving 事件,

  •        用于在 AutomaticSaveEnabled 属性设置为 true 时

  •        控制如何保存用户配置文件。

上面的三个事件呢,其实有一个事件我在上一篇博文中已经详细的介绍了,

就是从匿名状态转换到登录状态时使用的事件 Profile_MigrateAnonymous ,

还有要提醒的就是上面的这三个事件都是创建在 Global.asax 中的,

分别对应

Profile_MigrateAnonymous()

Profile_Personalize()

Profile_ProfileAutoSaving()

下面就来分别介绍一下

Profile_Personalize()和Profile_ProfileAutoSaving()这两个事件

先来看 Profile_Personalize()事件吧

Personalize 事件的话,

是在 ASP.NET 获取与当前请求相关联的当前状态(如会话状态)时发生,

Personalize 事件呢可以用来指定自定义的 Profile 用户配置文件,

如果在 Personalize 事件结束时,传递给这个事件的参数 args.Profile !=null 的话,

则 ProfileModule 类将使用这个 args.Profile

来作为当前的 HttpContext 的 Profile 属性的值,

在这里我想虽然我说了这么多,可是估计还是有很多人不懂,

那么我就这么说吧,直接说下面将要介绍的 Demo 的大致内容,

比如,一个网站的用户已经有成千上万了,如果,

我对单个的用户都一一使用 Profile 进行设置的话,

这个未免难了点,但是您可以通过角色来管理,

比如我的网站假设现在有 Admin ,SuperAdmin , User 三个角色,

如果我要分别对属于这三个角色中的用户登录时,我要使用不同的 Profile ,

简单点的就比如我要使用不同的背景颜色吧,

当然,您可以在每一个用户在 aspnet_Profile 用户表中都给他添加一个背景颜色,

然后再在每当用户登录时,都使用 Profile . 背景颜色 来读取其存储的颜色,

再修改页面背景色,当然,上面的这种方式是可以实现的,但是未免显得太愚笨,

而且会造成数据库开销增加(

我举的例子只是最简单的一个背景色而已,

如果内容更多的话,那数据库会一下子激增的)

而我再换另外一种方法来试试看,就是使用 Role 来进行管理,

我先对所有的用户进行分类,然后我在每次用户登录时

(会发出 Request 请求,这个可以被 ProfileModule 捕捉到)

都先在 Profile_Personalize 事件中使用其传递进来的参数 ProfileEventArgs args

来得到当前发出请求的 UserName ,

然后就可以根据这个 UserName 来获取其所属的角色,

然后再在数据库中针对其对应的角色取出相对应的 Profile 属性值,

将其赋值给当前的 Profile 对象的这个属性

(也就是修改当前的 Request ,修改完成后再将其交给 ASP.NET 处理),

然后再由 ProfileModule 类将这个属性赋值给当前的 HttpContext 的 Profile 属性值,

这样再在页面上就可以直接通过 Profile 来读取这个背景颜色了,

下面就来看针对这个 Profile_Personalize 事件的 Demo 吧

先看一些准备工作吧,

为了简单起见呢,我是用 ASP.NET 网站管理工具呢又往数据库中添加了三个用户

分别是 SuperAdmin , Admin , Users

并且为它们三个创建了 Profile

image

image

做好了准备工作了就该来创建 Profile_Personalize 事件了

在 Global.asax 中创建

<%@ Application Codebehind="Global.asax.cs"

         Inherits="WebForm.Global" Language="C#" %>

<script RunAt="server">

   //这里在 Global.asax 中定义的 Personalize 事件
   
void Profile_Personalize(object sender, ProfileEventArgs args)
    {
       
ProfileCommon userProfile;
        if (User == null)
        {
            return;
        }
      
//这里我只针对通过了身份验证的用户,其不包括匿名用户
        userProfile =
(ProfileCommon)ProfileBase.Create(User.Identity.Name, true);
        string[] userRoles = Roles.GetRolesForUser(User.Identity.Name);
        if (userRoles.Length > 0)
        {
            if (userRoles[0] == "SuperAdmin")
            {
             
  //从 Profile 中获取 SuperAdmins 用户的 Profile
                //作为当前 Request 的 Profile
                
                userProfile = userProfile.GetProfile("SuperAdmins");
            }
            else if (userRoles[0] == "Admin")
            {
                userProfile = userProfile.GetProfile("Admins");
            }
            else if (userRoles[0] == "User")
            {
               
userProfile.GetProfile("Users");
            }           
      
     //下面就来解释一下为什么会有 SuperAdmins ,Admins ,Users 这几个用户了
            //原因是我的网站中有三类角色,既然我要针对这三类角色进行 Profile 的统一管理的话,
            //那么我应该在数据库中保存这三类角色的 Profile ,而上面的这三个用户
            //实质上并不是作为用户使用的,而是作为存储 Profile 使用的
            //就拿 SuperAdmins 来说吧,他的 Profile 代表了所有的 SuperAdmin 的 Profile
            //这样就只需要保存一个 SuperAdmins 的用户的 Profile ,而不需要再像以前那样
            //要对每一个 SuperAdmin 角色中的用户都独自保存同一份 Profile 了

        }
        if (userProfile == null)
        {
            args.Profile = null;
        }
        else
        {
           
//使用刚才得到的 Profile 来覆盖掉 Request 原来的 Profile (修改)
            //再将其传递给 ASP.NET 进行处理

           
args.Profile = userProfile;
        }
    }       

</script>

下面就是来看一个页面了

<%@ Page Language="C#"%>

<script runat="server">

    protected void Page_Load(object sender, EventArgs e)
    {
        if (!User.Identity.IsAuthenticated)
        {
            System.Web.Security.FormsAuthentication.RedirectToLoginPage();
        }
       
       
//这里的 Profile.UserName 代表的是在 Personalize 事件中
        //被修改后传递过来的 Profile

        lblUserName.Text =
Profile.UserName;
        lblAddress.Text = Profile.住址;       
        lblConstellate.Text = Profile.星座;
      
//而这个 User.Identity.Name 则才是刚才通过密码用户验证的用户名
        lblName.Text =
User.Identity.Name;
        lblPhone.Text = Profile.电话.家庭电话 + "   " +
            Profile.电话.移动电话;
    }
</script>

<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title></title>
    <style type="text/css">
        .style1
        {
            width: 45%;
            height: 177px;
            border: 1px solid #8000FF;
        }
        .style2
        {
            height: 38px;
            font-size: small;
        }
    </style>
</head>
<body>
    <form id="form1" runat="server">
    <div style="font-family: 微软雅黑; font-size: small">
        <table class="style1">
            <tr>
                <td style="font-size: small">
                    登录名</td>
                <td style="font-size: small">
                    <asp:Label ID="lblName" runat="server">
                    </asp:Label>
                </td>
            </tr>
            <tr>
                <td style="font-size: small">
                    UserName</td>
                <td style="font-size: small">
                    <asp:Label ID="lblUserName" runat="server">
                    </asp:Label>
                </td>
            </tr>
            <tr>
                <td style="font-size: small">
                    住址
                </td>
                <td style="font-size: small">
                    <asp:Label ID="lblAddress" runat="server">
                    </asp:Label>
                </td>
            </tr>
            <tr>
                <td style="font-size: small">
                    电话</td>
                <td style="font-size: small">
                    <asp:Label ID="lblPhone" runat="server">
                    </asp:Label>
                </td>
            </tr>
            <tr>
                <td class="style2">
                    星座</td>
                <td class="style2" style="font-size: small">
                    <asp:Label ID="lblConstellate" runat="server">
                    </asp:Label>
                </td>
            </tr>
        </table>
    </div>
    </form>
</body>
</html>

其实呢,这个 Demo 的代码部分并不重要,

重要的是要让大家知道 Personalize 这个事件到底做了什么?

所以大家要好好思考下面的演示效果的各个部分

首先我以 XiaoZhen 登录(XiaoZhen 属于 SuperAdmin 这个角色)

image 
登录成功后

image

上面的截图显示出了 XiaoZhen 的 Profile ,

但是奇怪的是,在数据库中我明明只定义了 SuperAdmins ,Admins,

Users 三个用户的 Profile,而并没有给 XiaoZhen 定义 Profile 的,

但是这里为什么会显现呢?

这里就是 Personalize 事件在起作用了,

XiaoZhen 登录成功后,在页面的代码中定义了服务器代码,所以会发出 Request ,

而这些 Request 会被 ProfileModule 捕捉到从而触发 Personalize 事件,

而后我又在 Personalize 事件中判断出 XiaoZhen 这个用户是属于 SuperAdmin 角色的,

由此,我便将数据库中的 SuperAdmins 这个用户的 Profile 属性赋值给了 XiaoZhen ,

从而便将这个属于 SuperAdmins 的 Profile 赋值成为了 XiaoZhen 的 Profile ,

再来看一个 Admin 角色下的演示效果吧(SuHa 是属于 Admin 角色的)

image

上面解释了这么多,而后又有了 Demo ,并且 Demo 的效果也给附上去了,

我想现在大家应该是知道 Personalize 事件是干什么了的吧!!!

上面介绍完了 Personalize 这个事件,

那么接下来就该介绍 ProfileModule 的另外一个事件 –-ProfileAutoSaving 了

首先要说明的是,尽管 ProfileAutoSaving 事件是属于 ProfileModule 类的,

但是如果您要启用或者关闭这个功能的话,却是依赖于另外一个类来实现,

ProfileManager . AutomaticSavaEnabled = true 时(默认是 true),

表明将会启用在 ASP.NET 页面执行结束时自动保存用户配置文件的功能,

而如果启用了自动保存的功能的话,

ProfileModule 则会在 ASP.NET 页面结束时的 EndRequest 事件过程中

触发 ProfileAutoSaving 事件,

还有就是对于 AutomaticSavaEnabled 这个属性

还可以使用 web.config 的 profile 节进行配置

image

上面呢主要是解释了 ProfileAutoSaving 的一些基本的内容,

而其实质上是作什么的却还没有说明,接下来就解释一下吧,

不过,这的先来说明这个自动保存用户设置文件的功能,

这个功能呢,就是 Profile 机制会自动检测用户的 Profile 属性是否被更改过,

如果被更改了,那么就看做是 Dirty Data(脏数据),这样的话,

Profile 就必须在每次页面请求结束时自动调用 Sava()(不然咋叫做自动保存呢)

但是问题就是:

来自 MSDN

SettingsBase . Save 方法检查

用户配置文件中的每个 SettingsPropertyValue 的 IsDirty 属性值,

以确定由基元类型、字符串或 DateTime 对象构成的属性是否已更改。

Save 方法无法显式确定自定义类是否已更改。

您可以使用 ProfileAutoSaving 事件来确定某个自定义对象是否已被更改,

然后继续自动保存被修改的对象,或在没有对象被修改时取消自动保存。

上面呢,也就是说,Profile 自动检测 Profile 属性是否被更改的对象,

必须是基元类型或者字符串或者 DateTime ,

只有这样的对象才能够被检测出来是否有更改,

而像什么自定义类啊,复杂的什么 Color 对象啊这些的它都是检测不出来的,

这个和我们谈的有什么联系呢,

联系就是,这些它检测不出来的,

这个 Profile 自动检测机制都会自动的把它们是为 Dirty Data(即是它们没有改变),

这样问题就大了,当你使用复杂的数据类型时,它一律视为您改变了,

也就是说它一律会给你在每一个页面请求结束时都给你自动保存一下,

我的个天啊,要是我数据类型复杂点,Profile 属性多点,

请求多点,这网站还跑得起啊,别个时不时的给你保存一下,那性能可以大打折扣了啊,

所以呢,

这个时候就需要使用 ProfileAutoSaving 这个事件来手动控制什么时候呢,

我启用自动保存功能,又什么时候我又手动关闭这个自动保存功能,

这样就可以解决上面提到的性能问题了,比如我这个复杂类型根本没变,

这时我就将自动保存功能给关闭了,这样性能就保障了啊,

而如果复杂类型确实改变了,我又可以手动启用这个自动保存功能,

因为 ProfileAutoSaving 事件在每一次请求结束时都会触发,

所以实现手动打开和关闭自动保存功能也是很简单的,

说了这么多,举了这么多例子,我想也应该算是说明白了!!!

下面就该是看 Demo 了

先来看 Global . asax 中定义的 ProfileAutoSaving 事件吧

image

<%@ Page Language="C#"%>
<script runat="server">

    protected void Page_Load(object sender, EventArgs e)
    {
        if (User.Identity.IsAuthenticated == false)
        {
            System.Web.Security.
                FormsAuthentication.RedirectToLoginPage();
        }

        lblName.Text = User.Identity.Name;
    }

    protected void btnSava_Click(object sender, EventArgs e)
    {
        if (cboxAutoSaving.Checked)
        {
            //启用自动保存 Profile 功能
            Context.Items["ProfileAutoSaving"] = true;
        }
        else
        {
            //关闭自动保存 Profile 功能
            Context.Items["ProfileAutoSaving"] = false;
        }

        Profile.住址 = txtAddress.Text;
        Profile.电话.移动电话 = txtPhone.Text;
        Profile.星座 = txtConstellate.Text;

    }
</script>

<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title></title>
    <style type="text/css">
        .style1
        {
            width: 55%;
            border: 1px solid #8000FF;
            height: 200px;
        }
    </style>
</head>
<body>
    <form id="form1" runat="server">
    <div style="font-family: 微软雅黑; font-size: small">
        <table class="style1">
            <tr>
                <td>
                    用户名</td>
                <td>
                    <asp:Label ID="lblName" runat="server">
                    </asp:Label>
                </td>
            </tr>
            <tr>
                <td>
                    住址</td>
                <td>
                    <asp:TextBox ID="txtAddress" runat="server">
                    </asp:TextBox>
                </td>
            </tr>
            <tr>
                <td>
                    电话</td>
                <td>
                    <asp:TextBox ID="txtPhone" runat="server">
                    </asp:TextBox>
                </td>
            </tr>
            <tr>
                <td>
                    星座</td>
                <td>
                    <asp:TextBox ID="txtConstellate" runat="server">
                    </asp:TextBox>
                </td>
            </tr>
            <tr>
                <td>
                    <asp:CheckBox ID="cboxAutoSaving"
                    runat="server" Text="  是否自动保存" />
                </td>
                <td>
                    <asp:Button ID="btnSava" runat="server"
                    Text="保 存 Profile"
                        onclick="btnSava_Click" />
                </td>
            </tr>
        </table>
    </div>
    </form>
</body>
</html>

从上面的代码看,并没有使用 Profile . Sava()这个方法来实现保存 Profile

下面就看演示吧

首先我以 XiaoZhen 登录这个页面

image

image

然后再没有选择自动保存的情况下按 “保 存 Profile ”按钮,

此时您可以去数据库中验证,在 aspnet_Profile 数据表中并不会添加数据,

这是因为还没有启用自动保存功能,

下面就演示启用自动保存功能的情况,

image

再单击 “保 存 Profile ”按钮

再去数据表 aspnet_Profile 中查看

image

果然添加进去了,最重要的是我并没有显示的调用 Profile.Sava()方法,

这个就是自动保存的功能了,而上面呢也演示了关闭自动保存功能,

到此整个的 Demo 也算是完成了,不过还是有个需要提醒的就是,

我上面所使用的数据都是简单类型,

所以 Profile 机制也是可以完全检测出 Profile 属性又没有改变的,

所以其实是没有必要使用 ProfileAutoSaving 事件的

但是如果真正要使用到复杂数据类型的话,

那么一定要使用 ProfileAutoSaving 事件来控制何时打开自动保存,

何时关闭自动保存,否则性能会大打折扣的!!!

自上一篇博文和这一篇博文呢,便介绍完了 ProfileModule 的三个重要事件了,

其中这些事件都是各有用处,而且也是非常重要的,我想呢,

这两篇博文写的也还算是详细了,希望可以认真掌握好这三个事件!!!

 

                                                      2010—2—10

 

抱歉!评论已关闭.