如何在控件公共属性上输出缓存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 = 93
和Type = GraphData
,另一个可能通过GameID = 41
和Type = TotalPlaysData
,另一个可能通过GameID = 93
但Type = 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页面上使用控件设置。
当控件从输出缓存中检索时,它不会被实例化为可以操作的实例;您只获得控件生成的输出,而不是控件本身。例如,正如您在问题中所说,您无法从后面的代码设置缓存控件上的属性。可变属性应该声明式地设置(使用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;
我意识到这不是在缓存中,我只是想成为建设性的。