ASP.NET中使用动态LoadControl和缓存问题

前不久,再测试使用LoadControl动态加载用户控件的时候,碰到1个问题,程序会出现InvalidCastException的错误。看了PartialCachingControl的帮助后,意识到问题出在用户控件输出缓存上。
       帮助上说,当用户控件使用@ OutputCache或PartialCachingAttribute指定缓存输出,且用户控件是通过使用 TemplateControl.LoadControl动态加载到页面中时,会为该用户控件创建PartialCachingControl控件实例。 当用户再次请求该页面时,该页面将使用缓存中PartialCachingControl来加载,因此像下面这段代码就会出错。
protected void Page_Load(object sender, EventArgs e)
{

     Control control = LoadControl("~/NewsControl.ascx");
     PlaceHolder1.Controls.Add(control);
   ((NewsControl)control).ShowItemCount = 10; //出错!缓存中取出来的control是PartialCachingControl
}
后来看到了msdn上的一篇文章:通过避免10个常见缺陷使网站平稳运行(http://www.microsoft.com/china/msdn/library/webservices/asp.net/WebAppFollies.mspx?mfr=true),讲到了这个问题,摘抄如下。
LoadControl 和输出缓存
极少有不使用用户控件的 ASP.NET 应用程序。在出现母版页之前,开发人员使用用户控件来提取公用内容,如页眉和页脚。即使在 ASP.NET 2.0 中,用户控件也提供了有效的方法来封装内容和行为以及将页面分为多个区域,这些区域的缓存能力可以独立于作为整体的页面进行控制(一种称为段缓存的特殊输 出缓存形式)。
用户控件可以采用声明的方式加载,也可以强制加载。强制加载依赖于 Page.LoadControl,它实例化用户控件并返回控件引用。如果用户控件包含自定义类型的成员(例如,公共属性),则您可以转换该引用并从您的代码访问自定义成员。以下代码加载用户控件并向 BackColor 分配一个值:
protected void Page_Load(object sender, EventArgs e)
{
// 加载用户控件并将其添加到页面中
Control control = LoadControl("~/MyUserControl.ascx");
PlaceHolder1.Controls.Add(control);

// 设置其背景色
((MyUserControl)control).BackColor = Color.Yellow;
}
以上代码实际上很简单,但却是一个等待粗心的开发人员掉进去的陷阱。您能找出其中的破绽吗?
如果您猜到该问题与输出缓存有关,那么您是正确的。正如您所看到的一样,上述代码示例编译和运行都正常,但是如果尝试将以下语句(完全合法)添加到 MyUserControl.ascx 中:
<%@ OutputCache Duration="5" VaryByParam="None" %>
则当您下一次运行该页面时,您将看到 InvalidCastException (oh joy!) 和以下错误消息:
“无法将类型为‘System.Web.UI.PartialCachingControl’的对象转换为类型‘MyUserControl’。”
因此,此代码在没有 OutputCache 指令时运行正常,但如果添加了 OutputCache 指令就会出错。ASP.NET 不应该以这种方式运行。页面(和控件)对于输出缓存应该是不可知的。那么,这代表什么意思?
问 题在于为用户控件启用输出缓存时,LoadControl 不再返回对控件实例的引用;相反,它返回对 PartialCachingControl 实例的引用,而 PartialCachingControl 可能会也可能不会包装控件实例,具体取决于控件的输出是否被缓存。因此,如果开发人员调用 LoadControl 以动态加载用户控件并且为了访问控件特定的方法和属性而转换控件引用,他们必须注意进行该操作的方式,以便不管是否具有 OutputCache 指令,代码都可以运行。
protected void Page_Load(object sender, EventArgs e)
{
    // Load the user control
    Control control = LoadControl("~/MyUserControl.ascx");
    PlaceHolder1.Controls.Add(control);

    // Set its background color (if possible)
    MyUserControl uc = control as MyUserControl;
    if (uc == null)
    {
        PartialCachingControl pcc = control as PartialCachingControl;
        if (pcc != null) uc = pcc.CachedControl as MyUserControl;
    }
    if (uc != null) uc.BackColor = Color.Yellow;
}
上面代码说明动态加载用户控件以及转换返回的控件引用的正确方法。以下是其工作原理概要:
如果 ASCX 文件缺少 OutputCache 指令,则 LoadControl 返回一个 MyUserControl 引用。Page_Load 将该引用转换为 MyUserControl 并设置控件的 BackColor 属性。
如 果 ASCX 文件包括一个 OutputCache 指令并且控件的输出没有被缓存,则 LoadControl 返回一个对 PartialCachingControl 的引用,此 PartialCachingControl 的 CachedControl 属性包含对基础 MyUserControl 的引用。Page_Load 将 PartialCachingControl.CachedControl 转换为 MyUserControl 并设置该控件的 BackColor 属性。
如 果 ASCX 文件包括一个 OutputCache 指令并且控件的输出被缓存,则 LoadControl 返回一个对 PartialCachingControl(其 CachedControl 属性为空)的引用。注意,Page_Load 不再继续执行操作。无法设置控件的 BackColor 属性,因为该控件的输出来源于输出缓存。换句话说,根本没有要设置属性的 MyUserControl。
 

你可能感兴趣的:(asp.net,object,null,测试,user,工作)