Download source code - 1.4 Kb
Intro
Being a server and database guy who got thrown into web programming, I still
look to optimizing items with code when possible. Mainly that is because
budgeting, staffing, and throughput concerns are always on the mind of Admins.
After all, Admins love people who cause them to have to come in at
pre-O'dark-thirty to reset something. Also from a business sense, if you
adversely affect client business relationships with code you may be undermining
your advancement abilities.
Resource used for this article
The 17th chapter of Steven Walther's ASP.NET Unleashed (Second Edition)
struck a chord with me. It deals with cashing, and more specifically cashing
that does not use cashing for the entire page.
Onwards
We all know that it is possible through includes to use .ascx pages to
compartmentalize our repeatedly recycled sections of the page as a whole. After
all it is tedious to have to perpetually cut and paste in header and footer code
items. If you are not using .ascx pages for code reuse you must be working for a
living or something. Myself, I have to bow to the phone and email so anything
that gives me more time is a godsend.
Sure in my master .aspx page I could just have <%@ Outputcache
Duration="300" % VaryByParam="None"> and save myself some typing. But as
Steven points out, what happens if you rely upon banners ads for revenue
generation? If you keep brining up the same banner, your other clients may begin
to vacate due to a lack of hit-count. This is how your company might get paid
after all in this model, or it may be a means to reducing workload for an Admin.
Also what if one of your client's ad campaigns is only for a limited time? What
if your layouts are time specific? Like for a sweepstakes, if it is dependant
upon a holiday/event theme, or if the banner is a coupon to be printed out and
sent in for some time specific offer. Clients don't want it running forever and
you don't want to wait until midnight to manually remove the ad or the xml
file.
How about cashing on a item by item
basis: cache.Insert( "myItem", "Run that new ad!", _ New
cacheDependancy( MapPath( "myAdFile.xml" ) ) ) |
So if the "myAdFile.xml" item is modified the mytItem is
auto dumped from cache. |
These items are using the cacheDependancy class for file
dependencies
|
How about cashing based on on multiple items that may be
updated: Dim myFiles( ) As String = _ { MapPath( "myAdFile.xml" ),
_ { MapPath( "myAdFile2.xml" ), _ { MapPath( "myAdFile3.xml" ) }
cache.Insert( "myItem", "Run that new ad", _ New cacheDependancy( myFiles
) ) |
In this case if any of the three .xml pages in the string array are modified
the myItem is dropped from cache. |
So in effect you could use it to drop the use of an XML file over the use of
another in reserve. Or you could have a special color schema in XML for holiday
related times of the year where background colors or other page attributes would
be festive. Of course then you have times where you are away from the desk and
an event, like a holiday, may be around the corner. For me that is X-MAS and
college football bowl games (I mean New Years). Time to switch from
red/white/green for Santa into black/silver/gold for Baby New Years. Now I need
a trigger.
Now for some work in SQL Server to set up some triggers as this will
be the helping item to make this happen. A trigger is basically a stored
procedure that fires whenever a delete, insert, or update is made against a
table or a view.
[Personal choice: use the view extensively so the actual table is not
touched and be sure to validate everything the user enters first.
See also:
http://msdn.microsoft.com/events/devdays/sessions/default.aspx
AND
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnnetsec/html/ThreatCounter.asp
for the creation of "hack-proof" ASP.NET for more security
considerations]
If you want you can also check out xp_cmdshell that runs a string as
an operating system command to write a file to a directory.
So the MakeChng.sql item looks like this:
CREATE TRIGGER UpdateCache
On Products
FOR UPDATE, DELETE,
INSERT
AS
DECLARE @CMD Varchar( 200 )
SELECT @cmd = 'echo ' + Cast(
getDate( ) As Varchar 50 ) ) +
' > c:\layoutChng.txt'
EXEC
master..xp_cmdshell @cmd, no_output
The layoutDepndsOn.aspx page would be:
<% Import Namespace="System.Data" %>
<% Import
NameSpace="System.Data.SqlClient" %>
<Script
Runat="Server">
Sub Page_Load
Dim dstProducts As
DataSet
dstProducts = cache( "ProductsDS" )
If
dstProducts IS Nothing Then
dstProducts = GetProducts(
)
cache.Insert( "Products", dstProducts, _
New
cacheDependancy( "c:\layoutChng.txt" ) )
End
If
dgrdProducts.DataSource =
dstProducts
dgrdProducts.DataBind( )
End Sub
Function GetProducts( ) as DataSet
Dim conNorthwind As
SqlConnection
Dim strSelect As String
Dim dadProducts As
SqlDataAdapter
Dim dstProducts As DataSet
conNorthwind = New
SqlConnection( "Server=LocalHost;UID=BigAdmin;PWD=YeaRight;Database=Northwind"
)
strSelect = "Select Top 20 * From Products Order By
ProductID"
dadProducts = New SqlDataAdapter( strSelect,
ConNorthwind )
dstProducts = New DataSet(
)
dadProducts.Fill( dstProducts, "ProductsDS"
)
Return dstProducts
End Function
</Script>
<HTML>
<head><title>layoutDepndsOn.aspx</title>
</head>
<body>
<asp:
DataGrid ID="dgrdProducts" Runat = "Server"
/>
</body>
</HTML>
Or in the event you want to manually control it with a button click (from
home or with the aid of an over-night control person) use this:
[Based on:
"ASP.NET Unleashed" (Second Edition) by Steven Walther , pg 796-7]
<%@ Page %>
<Script Runat="Server">
Sub UpdateItem1( s As Object, e As EventArgs )
Cache( "item1" ) =
txtNewValue.Text
End Sub
Sub UpdateItem2( s As Object, e As EventArgs
)
Dim arrKeyDepends() As String = { "item1" }
Cache.Insert( "item2",
txtNewValue.Text, _
New CacheDependency( Nothing, arrKeyDepends ) )
End
Sub
</Script>
<html>
<head>
<title>Button Based
Dependency -
BBDepend.aspx</title>
</head>
<body>
<form
runat="Server">
<asp:TextBox id="txtNewValue" Runat="Server"
/>
<p>
<asp:Button Text="Update Item1" OnClick="UpdateItem1"
Runat="Server" ></asp:Button>
<asp:Button Text="Update Item2"
OnClick="UpdateItem2" Runat="Server" ></asp:Button>
<hr
/>
Item1 = <%=Cache( "item1" )%>
<br />
Item2 =
<%=Cache( "item2"
)%>
</p>
</form>
</body>
</html>
Also I hope these tables above help as a quick reference.
Varying by parameter values: AS IN <%@ outputcache
duration="300" VaryByParam="none" %>
|
*
|
indicating that different cached versions of a page should be
created whenever the page is requested with different parameters |
none
|
indicating that different cached versions of a page should not be
created whenever the page is requested with different parameters |
PID
|
or something based on a query string to have different cached versions of a
page based on each request. |
Varying by Header attribute: AS IN <%@ outputcache
duration="300" VaryByParam="*" VaryByHeader="User-Agent"
%>
|
User-Agent
|
by which browser to alter by based upon which browser is making
the request |
Varying by Custom String: AS IN <%@ outputcache
duration="300" VaryByParam="*" VaryByCustom="VBScript" %> - - A
function in the Global.asax file must be created for a custom string -
-
|
VBScript
|
Dependant upon if the browser supports VBScript -- Use
HttpBrowserCapabilities class to find major version, if it supports JAVA
applets, etc. |
Browser
|
The major version of the page will be cached; so for IE 5.0 and IE 5.5 the
same thing will be cached. |
Host
Accept -
Language
|
Varying by location: AS IN <%@ outputcache
duration="300" VaryByParam="*" Location="Client" %> - - This is
browser-dependant though - -
|
Any
|
The Default value. so could be on any location including server,
downstream servers, and the browser. |
Client
|
To be cached only on the browser. |
Downstream
|
In a server farm arrangement, on any server down the stream. |
None
|
No page cashing to be made. |
Server
|
cache it only on the server. |
HttpCachePolicy Class for more control
|
SetExpires(
)
|
Used to have cache expire on an absolute date and time |
|
Setcacheability( )
|
Used with setting as to how the page should be cached. |
|
Nocache
|
Prevents cashing with cache-Control:no-cache header. |
Private
|
Default value. Cannot be cached on proxy servers, but can be cached on a
browser. |
Public
|
Page can be cached on proxy servers and browsers |
Server
|
Only to cache on the server |
Memory use preservation via sharing cashed pages: AS IN
<%@ outputcache duration="300" VaryByParam="*" Shared="true"
%>
|
Shared="true"
|
Used to share the cached item across multiple user
controls. |
|
Why an item was removed from Cache Log:
Reasoning
|
DependencyChanged
|
Removed because of key or file dependency. |
Expired
|
Expiration policy criteria was met causing it to be removed. |
Removed
|
Removed by an explicit call to either Remove or Insert
method |
Underused
|
Removed do to lack of resources on the server. |
I hope this helps with your working relationships with your various Admins,
clients, or helps reduce your after hours visits.
Please do check out this very comprehensive book for more detailed
information. It is worth the price, and comes with a wealth of resources. I know
it gave me a lot to think about and even more ideas for projects.