Вы находитесь на странице: 1из 12

Caching In ASP.

net
One unfortunate by-product(s) of the rapid move away from standalone and client/server applications
to the Web-based environment is the disconnected nature of the HTTP protocol. Web servers and
clients briefly exchange data, and then (by default) each forgets about the other entirely until the
client makes another request. This meet-exchange-forget cycle particularly affects the way that the
individual pages of an application handle data—especially when there is a requirement to store data
that is either expensive to create (in terms of resource and processing usage), or which is used
regularly throughout the life of the application.

Since version 1.0, ASP has provided techniques to help you maintain state information and cache data
for an application, as the Application and Session objects that it exposes. ASP.NET 1.0 added another
headline feature, the application-level Cache object (complete with its support for output caching of
ASP.NET-generated HTML), which is especially useful because it supports features to expire and
invalidate items and pages stored in the cache. Now, ASP.NET version 2.0 adds new features that make
using the Cache object and output caching an even more compelling solution.

This article contains an overview of the primary techniques available in ASP.NET for caching values and
data between user requests, and then goes on to concentrate on the Cache object and output caching
—with particular emphasis on the new features in version 2.0. You'll see information about:

• Choosing the right approach for caching data.


• Application, Session and ViewState data caching.
• Global caching of data using disk files.
• The ASP.NET output caching features.
• Using the ASP.NET Cache object directly.

Author's Note: The ASP.NET version 2.0 code described in this article is based on the Beta 1 release
of the .NET Framework v2.0. All other code is based on the final V1.1 release of the .NET
Framework. You can download all the examples. If you install them on your own server, you may
have to edit the connection strings in the web.config file, depending on the location of your
database. There is a menu page named default.htm in each of the two folders in the samples (one
for ASP.NET v1.1 and one for v2.0 Beta1).

Choosing a Caching Technology


The most important feature when choosing one of the available caching technologies is cache visibility.
In other words, where do you need to be able to access the cached data? If it is just for the life of a
page or over repeated postbacks from a particular page, you can take advantage of the ViewState of
that page, which is basically a dictionary object that stores string values encoded into a hidden control
on the page. Or you can use output caching to cache a portion or the entire HTML generated for a
page so that the server does not have to re-execute the page for every request. In ASP.NET version
2.0, most of the new data source controls can cache their data, so you can also take advantage of that
capability.

However, to store data required to be accessible across more than one page, you need an alternative
technique. If you only need to make the values available for a specific user, you can use the Session
object to store values and data. As long as the user's browser supports cookies, or you turn on
cookieless session support, data in the user's Session store will be available from any page they open
while the session is active.

If you want to make your data visible to all the users of an application, you must use either the
ASP.NET Application object or the Cache object. The advantage of the Cache object is that you can
program expiration rules and manage the lifetime of the data more closely. Of course, you can use the
Application and Cache objects for user-specific data as long as you manage the identification of each
instance of the data, probably through unique or user-specific cache key names.

Finally, suppose you want to make cached data available to all users and all applications on a server,
or even on a networked group of servers? The only real answer here is to use some sort of disk or
memory persistence implemented and managed outside of ASP.NET. In fact this is reasonably easy to
do, using the classes implemented in the .NET Framework class library for reading and writing streams
(including memory streams) and disk files. In general, disk-based storage is the better option for
longer persistence periods, because it can survive server restarts, application failures, and most other
events that would cause other forms of cached data to be lost.

Table 1 shows the features of each technique briefly mentioned here, including a note about the
volume of data that each is designed to cope with. For example, ASP.NET transmits the ViewState of a
page over the network twice with each page request, and so you should keep it to a minimum. If you
have a great many concurrent users, even small volumes of data stored in each user's Session can
swallow up valuable memory on the server unless you configure alternative Session support features
such as the ASP.NET State Store or storage in a central SQL Server database.

Table 1. Cache Visibility: The table lists data visibility and optimum size for various caching
techniques.
Technique Visibility Optimum data size
Page ViewState Page Small
Data source controls Page Large
Page output caching Page Large
ASP Session User Small/Medium
ASP Application Application Medium
ASP.NET Cache Application Large
Disk file Global Large

It's important to note that you don't have to choose the most restrictive visibility for your cached data.
For example, disk-based storage—perhaps as an XML file—would be an ideal solution for data that is
expensive to retrieve from its source and yet changes infrequently (particularly if accessed only
occasionally by users). But what if each user requires a different set of data? You could still cache it
using some user-specific value in the filename, perhaps the Session ID or user login name. The only
downside is that you have to manage refreshing and removing stale data yourself.

The ASP.NET Application, Session and ViewState


You use the Application and Session objects in ASP.NET in essentially the same way as in "classic" ASP,
except that you should be aware of data typing issues. It's always a good idea to cast or convert values
to the correct type when you extract them. For example:

Dim iThisValue As Integer = 42

Application("MyInteger") = iThisValue

Dim iThatValue As Integer = CType( _

Application("MyInteger"), Integer)

In C#, that same code is:


int iThisValue = 42;

Application["MyInteger"] = iThisValue;

int iThatValue = (int) Application["MyInteger"];

Alternatively, you can use the methods exposed by the HttpApplicationState class (from the
System.Web namespace), which is the class used to implement the Application object in ASP.NET.
These methods include Add, Clear, Get, Remove, RemoveAt, and Set, plus properties and methods to
retrieve information about the keys for the stored items such as AllKeys, Count, GetKey, etc.

Author's Note: See the .NET Framework SDK topic "HttpApplicationState Members" in the "Reference
| Class Library" section for more details.

ASP.NET itself uses thread locking to automatically lock the Application (and Session) to prevent
concurrent updates to the values stored there from causing inconsistent results; however if you are
updating values in the Application you should use the Lock and Unlock methods as in previous versions
of ASP:

Application.Lock()

Dim iThisValue As Integer = CType( _

Application("MyInteger"), Integer)

iThisValue += iMyIncrement

Application("MyInteger") = iThisValue

Application(UnLock)

You aren't limited to storing only simple intrinsic .NET value types in the Application (or Session). You
can store any serializable class instance, for example a DataSet (in version 2.0, the DataTable is also
serializable). As an example, this code stores a DataSet in the Application:

Application("MyDataSet") = dsMyData

Dim dsRetrievedData As DataSet = _

CType(Application("MyDataSet"), DataSet)

ASP.NET Sessions
The HttpSessionState class (in the System.Web.SessionState namespace) implements ASP.NET's
Session object. The Session object has no Lock or Unlock method, though you can use the
IsSynchronized and SyncRoot properties to create your own thread-safe implementations if you wish.
However, for general use, the Session works just like the Application for reading and storing values.

Remember that the Session exposes some useful properties such as IsCookieless and IsNewSession,
which you may find useful. But the most important feature in ASP.NET in terms of improving page
execution efficiency is the implementation of read-only sessions. ASP.NET raises a whole series of
events as it loads and executes an ASP.NET page, and two of these are concerned with loading and
saving Session data. As a page loads, an event causes ASP.NET to search the Session for any values
that apply to the page, and load those it finds into the execution memory space for the page. As
execution of the page ends, another event causes ASP.NET to write those values back to the Session
store.

So it's pretty obvious that you can reduce page execution overheads by avoiding the two event calls if
you don't need to read or update session data. And if you only need to read it, but not update it, you
can specify read-only access to the Session. To specify that a page does not require access to the
user's session, add this directive:

<%@Page EnableSessionState="False" ... %>

To allow your application to read session data but not update it, thus avoiding the expensive process of
updating the session data at the end of the page, use this directive:

<%@Page EnableSessionState="ReadOnly" ... %>

Note that there is also a property named IsReadOnly on the HttpSessionState class that you can query
in your page code to see if the current session is read-only. You can also specify the session behavior at
machine or application level using the <pages> element within the <system.web> section of
machine.config or web.config.

</span><pages enableSessionState="[true|false|ReadOnly]" ... />

ASP.NET ViewState
If you need to cache only a single value or a small set of data for an individual page between
postbacks, you can use the ASP.NET ViewState. When an ASP.NET page contains a server-side HTML
form (a <form> section declared with the runat="server" attribute), it generates an HTML <hidden>
control that contains the values of the controls on the page, and other state information, in an encoded
format.

You can add values to ViewState and retrieve them using the same approach as with the Application
and Session objects:

Dim iThisValue As Integer = 42

Viewstate("MyInteger") = iThisValue

Dim iThatValue As Integer = CType( _

Viewstate("MyInteger"), Integer)

In C#, the equivalent code is:

int iThisValue = 42;

Viewstate["MyInteger"] = iThisValue;

int iThatValue = (int) Viewstate["MyInteger"];

Just bear in mind that your data passes across the network with each page request and each postback,
and so this is definitely not an ideal approach for storing large volumes of page-specific data. A better
solution is to store some key value in the ViewState, and then use that key to store and retrieve the
real data from a database, a disk file, or one of the other types cache better suited to storing large
volumes of data.
As with sessions, you can disable the storage of ViewState for a page. ViewState is enabled by default,
and even when disabled some control and state information is still persisted. However, there is no
concept of a "read-only ViewState." To disable ViewState for the page, use the Page directive:

<%@Page EnableViewState="False" ... %>

To disable ViewState for all the ASP.NET applications on a server or for all the pages in a site, alter your
machine.config or web.config files as follows:

<pages enableViewState="false" ... />

Author's Note: See the .NET Framework SDK topic "Saving Web Forms Page Values Using View State"
in the "Building Applications | Creating ASP.NET Web Applications" section for more details.

Caching Data as a String


A useful point to keep in mind whenever you store data that is not a string value, especially when you
are dealing with more complex classes that persist their data internally (such as a DataSet), is that
most support a method to export the content as a string. For example, with a DataSet (and, in version
2.0, a DataTable), you can use the WriteXml method to write the data as an XML document, which is
itself just a text string. You can also provide the writeMode parameter (a value from the XmlWriteMode
enumeration) that specifies if a schema is included, and whether you want a simple XML
representation of the data or a diffgram that stores change information as well.

The following code shows how to serialize a DataSet as an XML document and store it in the ViewState
of the page, and then re-instantiate the DataSet for each postback:

Imports System.IO ' needed to use StringWriter

Dim dsMyData As New DataSet()

If Page.IsPostBack Then

Dim reader As New _

StringReader(CStr(ViewState("MyDataSet")))

dsMyData.ReadXml(reader)

Else

'... code to fill DataSet from source data store here ...

Dim writer As New StringWriter()

dsMyData.WriteXml(writer)

ViewState("MyDataSet") = writer.ToString()

End If
Author's Note: See the .NET Framework SDK topic
"StringWriter Members" in the "Reference | Class
Library" section for more details.

The downloadable sample code for this article contains an example of caching the
contents of a DataSet as an XML string value within the page ViewState. The write-
ViewState.aspx example uses the code listed above. The result looks like Figure 1.
When the server loads the first page, it fetches the data from the database; however, as you refresh
the page, it rebuilds the DataSet each time from the XML cached in the ViewState.

Caching Data as a Disk File


The .NET Framework provides plenty of easy-to-use classes for reading and writing disk files. You can
use these when you need to cache data required for more than one ASP.NET application. As an
example, suppose you want to cache a DataSet containing data to which all the applications on your
server require access. You could capitalize on the just-described technique for calling the WriteXml and
ReadXml methods, which work automatically with a disk file—all you have to do is provide the path
and filename.

However, you can also use a BinaryFormatter to write data from objects that are serializable to a
Stream. The code below demonstrates using a FileStream with a DataSet, but you could just as easily
cache the data in memory using a MemoryStream if required. Why bother with binary serialization
when the DataSet can serialize itself as XML? Well, the one extra feature here revolves around the fact
that, in version 2.0, the DataSet gains a new property named RemotingFormat. This can be set to a
value from the SerializationFormat enumeration (the two options are Xml or Binary). When set to
Binary, the volume of data generated from a DataSet is generally reduced by between 20 and 80
percent, which results in smaller disk files, faster loading, and ultimately better performance:

' create BinaryFormatter and Stream

Dim f As IFormatter = New BinaryFormatter()

Using s As New FileStream(datPath, _

FileMode.Create, FileAccess.Write, FileShare.None)

' specify Binary remoting format for the DataSet

' (new in v 2.0)

dsMyDataSet.RemotingFormat = SerializationFormat.Binary

' serialize the contents to the Stream as binary data

f.Serialize(s, dsMyDataSet)
s.Close()

End Using

To rebuild the DataSet from the disk file, you can use this code:

' now de-serialize the data back into a new DataSet

Dim dsMyDataSet As DataSet

Dim f As IFormatter = New BinaryFormatter()

Figure 2. DataSet
Serialization: The figure
illustrates the process of
serializing a DataSet to a
disk File and later
deserializing it back into a
DataSet.

Using s As New FileStream(datPath, FileMode.Open, FileAccess.Read, FileShare.Read)

' de-serialize file contents into a DataSet

dsMyDataSet = CType(f.Deserialize(s), DataSet)

s.Close()

End Using

Author's Note: The preceding code takes advantage of


the Using construct, which was available in C# in v1.1
and is now available in VB.NET in v 2.0.

Figure 2 shows the provided example (serialize-to-disk.aspx) that uses this code to serialize a DataSet
to a disk file, and then de-serialize it back into a DataSet again. The page also displays some rows from
the DataSet to prove that it worked.
Many objects support the ISerializable interface, and so can be used with a BinaryFormatter as shown
above (though the actual format of the persisted data will vary with the object type). Suitable classes
include collection types such as HashTable and NameValueCollection, the ADO.NET DataSet and
DataTable, and the Image class that is used to create and store bitmap images.

Author's Note: See the .NET Framework SDK topic "BinaryFormatter


Members" in the "Reference | Class Library" section for more
details.

ASP.NET Output Caching


One extremely useful way to boost performance in ASP.NET is to avoid running server-side script to
provide a new version of the page for each request. To do that, you can use output caching, which is
useful when users regularly or repeatedly load the same page. ASP.NET implements output caching
internally, so it's seamless for developers. By specifying a directive in the ASP.NET page, the server
automatically caches the HTML generated by the page according to the conditions you specify.

The simplest output caching directive is:

<%@OutputCache Duration="#seconds" VaryByParam="None" />

The preceding line instructs ASP.NET to cache the HTML for the specified number of seconds,
irrespective of the parameters sent with the page request from the client. "Parameters" in this sense
means any values in the Request collections (Form, QueryString, Cookies, and ServerVariables). While
the page is cached, every request will return the HTML generated the last time that the page was
executed being sent to the client. In IIS 6.0, in Windows Server 2003, this detection of cached pages is
actually done inside the kernel-level HTTP redirector module, and so it is blindingly fast—quicker than
in IIS 5.0 and, of course, a great deal faster that re-executing the page.

There are many alternative ways to configure output caching in ASP.NET. The OutputCache directive
provides a range of options, for example:

<%@ OutputCache Duration="#seconds"

Location="[Any|Client|Downstream|Server|None]"

Shared="[true|false]"

VaryByControl="control-name(s)"

VaryByParam="[None|*|parameter-name(s)]"

VaryByHeader="header-name(s)"

VaryByCustom="[Browser|custom-string"] %>

Table 2 defines the attribute values used above.

Table 2. OutputCache Directive Attributes: The table lists the attributes you can use to fine-tune the
OutputCache directive.

Attribute Description
Duration Required in every ASP.NET OutputCache directive. The number of seconds that the
generated HTML will be cached.

Location Defines if page output can be cached on a proxy or downstream client. Often omitted, so
that caching is performed in the most efficient way. However, it is a good idea to include
Location="Any" where you do not actually need to control the location.

Shared Used only in a User Control. Defines if multiple instances of the control will be served from
one cached instance. If False (the default) multiple instances will be cached.

VaryByCont Used only in a User Control. Declares the ID of ASP.NET controls that the cache will be
rol varied by. For example: VaryByControl="MyTextBox;MyListBox" will cause a new version of
the page to be cached for each differing value in the specified controls within the user
control.

VaryByPara Required in every ASP.NET OutputCache directive. Defines the names of the parameters
m that will be used to determine if a cached version of the page will be delivered. For
example: VaryByParam="txtName;lstSelection" will cause a new version of the page to be
cached for each differing value in the QueryString, Form, Cookies or ServerVariables
collections that has one of the specified names.

VaryByHead The name(s) of any HTTP Header(s) that are used to determine if a cached version of the
er page will be delivered. For example: VaryByHeader="USER-AGENT;SERVER;REMOTE-HOST"

VaryByCust The special value "Browser" specifies that the cached version of the page will only be
om delivered to browsers with the same name and major version: VaryByCustom="Browser".
Alternatively, you can place a custom function in global.asax that returns a String value
based on some criteria you require, and the page will be varied for each client that causes
a different value to be returned. For example: VaryByCustom="MyGlobalFunction" (see the
following note for an example of this).

Author's Note: Remember that ASP.NET varies the HTML it generates based on the browser type
("uplevel"—meaning Internet Explorer 5 and above; or "downlevel"—meaning all the rest). Therefore you
might want to consider including the VaryByCustom="Browser" attribute if your page uses ASP.NET
controls that can generate browser-specific output. Examples are controls such as Menu and Panel. The
way that ASP.NET specifies style information for most other Web Forms controls also changes depending
on the browser type, and may affect the appearance. You can experiment with these effects using a
sample page from the book "Pro ASP.NET 1.1 Web Forms Techniques", see:
http://www.daveandal.net/books/7868/control-compatibility/default.aspx . The asp-cache-by-level option
shows how you can implement custom "vary by" functions. I've included the function code within the
downloadable code samples for this article.

You can use the example page named output-cache-params.aspx


(see Figure 3) to experiment with output caching. As the
VaryByParam attribute is set to "*", all the controls on the page will
invalidate the cached HTML copy, and so changing the value in any
Figure 3. Output Caching:
The figure illustrates how
the VaryByParam="*"
attribute affects output
caching.
one will cause the page to be re-executed (the time it was last executed is shown in the page).
However, refreshing the page within the 10-second cache duration period specified in the OutputCache
directive does not cause it to be re-executed.

One interesting point demonstrated in this example is that using the ASP.NET auto-postback feature
causes the page to be re-executed more than once. For example, changing the value in the second
drop-down list initiates a postback automatically. And, because the values in the controls have
changed, ASP.NET invalidates the cache and re-executes the page. However, if you then click the
Refresh button, it executes the page again—rather than serving it from the output cache. This is
because the name and caption of the button are included in the parameters posted back to this page
when the button is clicked, whereas they are not included when the postback is caused by the drop-
down list that has the AutoPostback="True" setting.

Partial Page Caching (Fragment Caching)


ASP.NET also supports partial caching of a page, sometimes referred to as fragment caching. To use
this, you create sections of the page as user controls, containing the Control directive and with the
.ascx file extension. Inside each user control you can specify an OutputCache directive (which can
specify the ID of any control(s) it contains in a VaryByControl attribute, or use a VaryByParam
attribute):

<%@OutputCache Duration="30" VaryByControl="MyListBox" />

However, when you specify output caching for the page hosting the user control, ASP.NET caches the
whole page based on the Duration value specified in the hosting page; therefore, any user controls
included on the page will be re-executed only when the page itself expires. For this reason, you should
set the Duration attribute in the user control(s) to the same, or to a
multiple of the Duration attribute value in the hosting page.

You can see the effects of partial page caching in the example page
named output-cache-fragment.aspx. That page hosts a simple user
control that contains a drop-down list box. As you can see from Figure
4, the output cache duration for the user control is ten seconds, while
the duration for the hosting page is only five seconds. As you refresh
the page, the execution times show that the page is re-executed
every five seconds, while the user control is only re-executed every
ten seconds. However, the hosting page contains the
VaryByParam="*" attribute in its OutputCache directive, and so any
Figure 4. Partial Page
change to the selected value in the list box—even though it is within
Caching with a User Control:
the user control—will cause ASP.NET to re-execute both the hosting
The page cache Duration is
page and the user control the next time the page is requested.
5 seconds, while the cache
Duration setting of the user
If your user control generates output that is not specific to one page,
control contained on the
or if you use multiple instances in the same page and they do not
page is 10 seconds.
need to be varied individually, you can reduce output cache
requirements by including the Shared attribute:

<%@OutputCache
Duration="60"
VaryByControl="MyListBox"
Shared="True" />
Alternatively, if you create user controls as Class files, you can use an attribute to specify partial
caching. In VB.NET the attribute is <PartialCaching(#seconds)>, for example:

<PartialCaching(120)> _
Public Class MyControl
Inherits UserControl
... implementation here ...
End Class
In C#, the equivalent syntax is:

[PartialCaching(120)]
public class MyControl : UserControl {
... implementation here ...
}
Configuring Output Caching in ASP.NET 2.0
ASP.NET 2.0 adds new capabilities to the output cache feature, as well as supporting new configuring
options for ASP.NET pages and user controls in the machine.config and web.config files. There new
version supports a <caching> section where you can define the operation of output caching and SQL
database cache invalidation (a topic you'll see explored in more depth in the second part of this
article). This is the overall layout of the <caching> section:

<system.web>
<caching>

<cache disableMemoryCollection="[true|false]"
disableExpiration="[true|false]" />

<outputCache enabled="[true|false]">
<diskCache enabled="[true|false]" path="directory-path"
maxSizePerApp="size-in-MB" />
</outputCache>

<outputCacheSettings>
<outputCacheProfiles>
<add name="identifying-string"
enabled="[true|false]"
duration="#seconds"
diskCacheable="[true|false]" shared="[true|false]"
location="[Any|Client|Downstream|Server|None]"
varyByControl="control-name(s)"
varyByParam="[None|*|parameter-name(s)]"
varyByHeader="header-name(s)"
varyByCustom="[Browser|custom-string"] />
</outputCacheProfiles>
</outputCacheSettings>

<sqlCacheDependency
enabled="[true|false]"
pollTime="#milliseconds">
<databases>
<clear />
<add name="identifying-string"
connectionStringName="name-string"
pollTime="#milliseconds" />
<remove name="identifying-string" />
</databases>
</sqlCacheDependency>

</caching>
</system.web>

• The <cache> element defines the settings for memory-based caching and whether cached
items will be expired automatically when the server requires the memory or disk space they
are occupying (see the section "Cache Memory and Automatic Invalidation").
• The <outputCache> section specifies the global setting for output caching, whether it is
enabled, and where and whether cached items can be saved to disk. Disk caching is enabled
by default, and the maximum disk usage per application is set to 5 MB.
• You use the <outputCacheSettings> section to define individual output caching profiles that
you can inherit in your pages, so that you don't have to edit the OutputCache directive in
every page (see the section "Output Cache Profiles in ASP.NET 2.0").
• The <sqlCacheDependency> section defines the databases and connection details that are
used for the new automatic SQL data cache invalidation feature in ASP.NET 2.0. You'll see more
about that in Part 2 of this article.

Cache Memory and Automatic Invalidation


It's important to remember that the ASP.NET cache will hold on to items only while there is enough
memory available without compromising operation of the server. If you continue to add items to the
cache after the free cache memory is exhausted, ASP.NET invalidates and removes the oldest, least
used, or lowest priority items. So, just because you cached an item doesn't guarantee it will still be
there even if you specified a priority other than the default.

So it's a good idea to test for the presence of an item before or as you retrieve it from the ASP.NET
cache. You can also use cache dependencies and an event handler (as demonstrated later on) to
detect when items are removed from the cache, though this is not always possible in a Web application
as the events are not likely to be fired when your ASP.NET page is
executing and can handle them.

Output Cache Profiles in ASP.NET 2.0


If you regularly use the same sets of values in OutputCache directives
across different ASP.NET pages, you can set up cache profiles in
ASP.NET version 2.0. Simply declare the profile in the
<outputCacheProfiles> section of machine.config or web.config using
an <add> element. Within the <add> element, specify the attributes
you want for this profile—for example:
<outputCacheProfiles> Figure 5. Using a
<add name="MyPageProfile" CacheProfile: The figure
enabled="true" shows the results of using a
duration="60" CacheProfile value in the
varyByParam="*" /> OutputCache directive.
</outputCacheProfiles>
Then, in any ASP.NET page or user control, you just specify this profile in your OutputCache directive:
<%@ OutputCache CacheProfile="MyPageProfile" %>
The example page named output-cache-profile.aspx demonstrates the use of output caching profiles.
When you run the page, it looks just like the example in Figure 3. However, as you can see if you view
the source for the page (use the "[view source]" link at the bottom of every example page, as shown in
Figure 5), you will see that it uses the OutputCache directive shown above.

In the next part of this article, you'll see how to capitalize on ASP.NET's new SQL Cache invalidation
support, which lets you cache and reuse data from SQL Server until that data has changed—a
capability that nearly every data-driven Web application sorely needs. SQL Cache Invalidation enlists
SQL Server to solve the problem of how often to refresh data stored in a database. Essentially, rather
than arbitrarily deciding to refresh data on a schedule or having to run a query to check whether data
has changed, you can force SQL Server to notify your application whenever specific data changes. By
doing this you can ensure that your application displays the most up-to-date information possible,
while simultaneously ensuring that your application queries the database as infrequently as possible.
This has the effect of improving the efficiency of your application while reducing the demands on your
database server.

Вам также может понравиться