如何在控件公共属性上输出缓存web控件

本文关键字:控件 输出 缓存 web 属性 | 更新日期: 2023-09-27 18:18:15

我有一个web用户控件,它提供了一些潜在的密集数据计算,我希望它被缓存输出,以便每个页面视图不重新计算数据。它驻留在非常频繁查看的页面上,所以我让它正确工作是非常重要的!

在我们的街机中使用:http://www.scirra.com/arcade/action/93/8-bits-runner

点击统计,图表和统计数据都是从这个webusercontrol生成的。

控件的开始如下:

public partial class Controls_Arcade_Data_ArcadeChartData : System.Web.UI.UserControl
{
    public int GameID { get; set; }
    public Arcade.ChartDataType.ChartType Type { get; set; }
    protected void Page_Load(object sender, EventArgs e)
    {

现在我遇到的困难是输出缓存需要依赖于GamID和ChartType。

这个控件被重用与许多不同的GameID和类型的组合,我需要它来创建每个这些缓存,但我努力找出如何做到这一点。

例如,一个街机游戏可能通过GameID = 93Type = GraphData,另一个可能通过GameID = 41Type = TotalPlaysData,另一个可能通过GameID = 93Type = TotalPlaysData。这些都应该返回不同的数据,并具有不同的输出缓存。

这个控件在游戏页面上的使用方式是这样的(参数实际上是在代码后面设置的)

<div>Total Plays:</div>
<div class="count"><Scirra:ArcadeChartData runat="server" GameID="93" Type="TotalPlays" /></div>
<br /><br />
<div>Total Guest Plays:</div>
<div class="count"><Scirra:ArcadeChartData runat="server" GameID="93" Type="TotalGuestPlays" /></div>
etc.

感谢任何帮助!我花了一段时间在网上寻找,它一直出现在我需要解决的问题上,但无法解决这个问题。

编辑

编辑:我试过把这行添加到我的控制:<%@ OutputCache Duration="20" VaryByControl="GameID;Type" %>

但是它只是抛出错误Object reference not set to an instance of an object.在行的GameID是第一次在ASPX页面上使用控件设置。

如何在控件公共属性上输出缓存web控件

当控件从输出缓存中检索时,它不会被实例化为可以操作的实例;您只获得控件生成的输出,而不是控件本身。例如,正如您在问题中所说,您无法从后面的代码设置缓存控件上的属性。可变属性应该声明式地设置(使用ExpressionBuilder也可以工作,尽管我还没有尝试过)。

要在代码中查看控件是否已从输出缓存中检索到,请检查是否为null:

if (this.YourControlID != null) // true if not from cache
{
    // do stuff
}

尽管如此,Control输出缓存还是有点奇怪。

试试这个:

<%@ OutputCache Duration="20" VaryByControl="GameID;Type" Shared="true" %>

控件的输出通过与某个键关联来存储在输出缓存中。对于Shared="true",缓存键是所有指定属性的值,以及控件的ID。如果没有Shared="true",缓存键还包括Page类型,因此输出将因Page而异——这听起来不像您想要的。

如果在多个页面上使用Control,请确保尽可能在每个页面上使用相同的ID,因为ID是作为输出缓存键的一部分包含的。如果不能或不使用不同的ID,则将在缓存中为每个唯一ID获得Control输出的新副本。如果具有不同id的控件总是具有不同的属性值,那么这可能不是问题。

作为OutputCache指令的替代方案,您可以在类声明中设置一个属性:

[PartialCaching(20, null, "GameID;Type", null, true)]
public partial class Controls_Arcade_Data_ArcadeChartData : UserControl

您需要采取以下步骤:

1)在页面中添加以下输出缓存指令:

<%@ OutputCache Duration="21600" VaryByParam="None" VaryByCustom="FullURL" %>

2)将以下内容添加到global.asax:

    public override string GetVaryByCustomString(HttpContext context, string arg)
    {
        if (arg.Equals("FullURL", StringComparison.InvariantCultureIgnoreCase)
        {
            // Retrieves the page
            Page oPage = context.Handler as Page;
            int gameId;
            // If the GameID is not in the page, you can use the Controls 
            // collection of the page to find the specific usercontrol and 
            // extract the GameID from that.
            // Otherwise, get the GameID from the page
            // You could also cast above
            gameId = (MyGamePage)oPage.GameID;
            // Generate a unique cache string based on the GameID
            return "GameID" + gameId.ToString();
        }
        else
        {
            return string.Empty;
        }
    }

你可以从MSDN获得关于GetVaryByCustomString方法的更多信息,也可以在这里查看其他一些缓存选项。

在代码中创建一个缓存对象

HttpRuntime.Cache.Insert("ArcadeChartData" + GameID + Type, <object to cache>, null, System.Web.Caching.Cache.NoAbsoluteExpiration,new TimeSpan(0, 0, secondsToCache),CacheItemPriority.Normal, null);
上面的缓存项

将足以满足您的工作,但如果您真的想要使用输出缓存,请尝试后面代码中的代码

Response.AddCacheItemDependency("ArcadeChartData" + GameID + Type);
Response.Cache.SetExpires(DateTime.Now.AddSeconds(60));
Response.Cache.SetCacheability(HttpCacheability.Public);
Response.Cache.SetValidUntilExpires(true);

设置页面输出缓存的值与操作相同SetExpires和SetCacheability方法通过Cache属性。

我知道我的解决方案可能看起来很简单,可能很奇怪,但我试过了,它有效。你只需要在你的UserControl中添加这一行。

<%@ OutputCache Duration="10" VaryByParam="none" %>

注意:我只测试了框架4.0。此外,如果你必须改变UserControl (MyInt, My String在这个例子中)中的属性值,请在Page_Init事件中进行。

下面是我的代码:

页面:

<%@ Page Title="Home Page" Language="vb" MasterPageFile="~/Site.Master" AutoEventWireup="false" CodeBehind="Default.aspx.vb" Inherits="MyWebApp._Default" %>
<%@ Register Src="~/UserControl/MyUserControl.ascx" TagPrefix="uc" TagName="MyUserControl" %>
<asp:Content ID="HeaderContent" runat="server" ContentPlaceHolderID="HeadContent">
</asp:Content>
<asp:Content ID="BodyContent" runat="server" ContentPlaceHolderID="MainContent">
    <uc:MyUserControl ID="uc1" MyInt="1" MyString="Test"  runat="server" />
    <hr />
    <uc:MyUserControl ID="uc2" MyInt="3" MyString="Test"  runat="server" />
    <hr />      
    <uc:MyUserControl ID="uc3" MyInt="1" MyString="Testing" runat="server" />
</asp:Content>
用户控制:

<%@ Control Language="vb" AutoEventWireup="false" CodeBehind="MyUserControl.ascx.vb" Inherits="MyWebApp.MyUserControl" %>
<%@ OutputCache Duration="10" VaryByParam="none" %>
<div style="background-color:Red;">
    Test<br />
    <asp:Label ID="lblTime" ForeColor="White" runat="server" />
</div>

用户控制代码:

Public Class MyUserControl
    Inherits System.Web.UI.UserControl
    Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
        Debug.Write("Page_Load of {0}", Me.ID)
        Dim oStrBldr As New StringBuilder()
        For i As Integer = 1 To Me.MyInt
            oStrBldr.AppendFormat("{0}: {1} - {2} at {3}<br />{4}", Me.ID, i, Me.MyString, Date.Now.ToLongTimeString(), System.Environment.NewLine)
        Next
        Me.lblTime.Text = oStrBldr.ToString()
    End Sub

    Public Property MyInt As Integer
    Public Property MyString As String
End Class

请让我张贴,我有其他的解决方案,如果你希望,但他们更复杂。我也可以用c#

一个简单的技巧是把所有的图形放在一个新的页面中,接收GameId和Type作为查询字符串参数,使用开箱即用的输出缓存查询字符串参数,并在你的页面中放置一个iframe。你也可以利用浏览器的缓存,暂时不访问服务器。

好吧,为什么这很难使OutputCache在这种情况下工作的原因是因为它不是设计为使用属性的,但是它工作得很好与QueryString参数。所以我的下一个解决方案不是最专业的,也可能不是最好的,但它绝对是最快的,并且需要更少的代码更改。

因为它的工作最好QueryString,我建议你把你的UserControl在一个空白页,当你想使用你的UserControl做一个iframe链接到你的页面与UserControl QueryString。

你想在哪里使用你的UserControl:

<iframe src="/MyArcadeChartData.aspx?GameID=93&Type=TotalPlays"></iframe>

完整页面标记,MyArcadeChartData.aspx

<%@ Page ... %>
<%@ OutputCache Duration="20" VaryByParam="GameID;Type" %>
<Scirra:ArcadeChartData ID="MyUserControlID" runat="server />

完整页面代码,MyArcadeChartData.aspx.cs

protected void Page_Load(object sender, EventArgs e)
{
    //TODO: Put validation here
    MyUserControlID.GameID = (int)Request.QueryString["GameID"];
    MyUserControlID.Type = (YourEnum)Request.QueryString["Type"];
}

请不要让QueryString中的值可以被用户看到,请不要放敏感数据。

我也知道这不是最专业的解决方案,但据我所知,这是最容易实现的。

Regards and happy holidays

如果我理解正确的话,缓存不能正确工作,因为您有向控件提供值的属性,这可能部分地与正在进行的计算有关。

您可以创建一个HttpHandlerFactory,它接受请求,如果它们不在缓存中(之后插入到缓存中)进行计算,处理值的过期,然后将请求传递到页面。它根本就不是特定于对照组的。这样,您就可以在任何控件或页面中使用这些计算值,而不必实现担心自己计算的缓存策略。

如果这不是数据密集型的,您是否考虑过将它存储在会话中而不是缓存它?只是一个想法…

Arcade.ChartDataType.ChartType Type;
string GameKey = GameId + Type.toString();
storedData = callCalculation(GameId,Type);
Session[GameKey] = storedData;

我意识到这不是在缓存中,我只是想成为建设性的。