On this page

Skip to content

MemoryCache Application in ASP.NET MVC

TLDR

  • OutputCacheAttribute is an MVC built-in ActionFilter that uses MemoryCache by default for server-side caching.
  • To isolate cache based on different user permissions, you can use VaryByCustom in conjunction with GetVaryByCustomString in Global.asax.
  • NoStore and Location.None behave differently: NoStore only affects browser caching, while Location.None disables both browser and server-side caching.
  • To ensure the cache is updated immediately when the database changes, you can use SqlChangeMonitor combined with SqlDependency to listen for SQL Server changes.
  • Before using SqlDependency, you must ensure that the Service Broker feature is enabled in the database.

Caching Action Content with ActionFilter

OutputCacheAttribute is used to decorate Action Methods to enable caching. If not specifically configured, it uses MemoryCache for storage by default.

Key Property Descriptions

  • Duration: Cache duration (in seconds).
  • Location: Specifies the cache storage location (e.g., Server, Client, Any, etc.).
  • VaryByParam: Distinguishes cache versions based on parameters (e.g., QueryString or POST parameters). Setting it to * creates a cache for all parameter combinations.
  • CacheProfile: References a cache scheme defined in Web.config for centralized management.

TIP

Differences between NoStore and Location.None:

  • NoStore: Sets the Cache-Control header to no-store, informing the browser not to cache the response; it does not affect Web Server caching behavior.
  • Location.None: Sets the Cache-Control header to no-cache, and the Web Server will not store any cache.

Implementation: Cache Isolation for Different Users

When to encounter this issue: When an application needs to display different content based on user permissions or identity, but the default OutputCache causes different users to receive the same cached result.

Web.config Configuration:

xml
<system.web>
  <caching>
    <outputCacheSettings>
      <outputCacheProfiles>
        <add name="Default" duration="30" varyByParam="*" varyByCustom="Cookie" noStore="true" />
      </outputCacheProfiles>
    </outputCacheSettings>
  </caching>
</system.web>

Global.asax.cs Implementation: Override GetVaryByCustomString to generate a unique cache key.

csharp
public override string GetVaryByCustomString(HttpContext context, string custom) {
    const string OutputCacheKey = "OutputCacheId";

    if (custom.Equals("Cookie", StringComparison.OrdinalIgnoreCase)) {
        if (Request.Cookies[OutputCacheKey] == null) {
            string cacheId = Guid.NewGuid().ToString();
            Response.Cookies.Add(new HttpCookie(OutputCacheKey) {
                Value = cacheId,
                HttpOnly = true,
                Expires = DateTime.Now.AddHours(1)
            });
            return cacheId;
        }
        return Request.Cookies[OutputCacheKey].Value;
    }
    return base.GetVaryByCustomString(context, custom);
}

Clearing Cache Data When Updating the Database

When to encounter this issue: When the cached data source is a database, and you need to ensure that the cache is automatically invalidated or updated when the database content changes, rather than waiting for expiration.

Using SqlChangeMonitor to Monitor the Database

MemoryCache supports ChangeMonitor, where SqlChangeMonitor can listen for SQL Server change notifications via SqlDependency.

Global.asax.cs Configuration:

csharp
protected void Application_Start() {
    SqlDependency.Start(WebConfigurationManager.ConnectionStrings["MyDB"].ConnectionString);
}

protected void Application_End() {
    SqlDependency.Stop(WebConfigurationManager.ConnectionStrings["MyDB"].ConnectionString);
}

Controller Implementation Example:

csharp
private void CreateCache() {
    string connectionStr = WebConfigurationManager.ConnectionStrings["MyDB"].ConnectionString;
    CacheItemPolicy policy = new CacheItemPolicy();
    
    using (SqlConnection conn = new SqlConnection(connectionStr))
    using (SqlCommand cmd = new SqlCommand("SELECT Key1 FROM dbo.Config", conn)) {
        SqlDependency dependency = new SqlDependency(cmd);
        dependency.OnChange += SqlDependencyOnChange;

        conn.Open();
        string key1 = cmd.ExecuteScalar().ToString();

        SqlChangeMonitor monitor = new SqlChangeMonitor(dependency);
        policy.ChangeMonitors.Add(monitor);

        MemoryCache.Default.Set(CacheKey, key1, policy);
    }
}

WARNING

  • The Service Broker feature must be enabled in the database.
  • The SQL syntax must specify concrete columns, and the table name must include the Schema (e.g., dbo.TableName).
  • After SqlDependency is configured, you must execute the SqlCommand once for the monitoring to take effect.

Enabling Service Broker

If the database is not enabled, execute:

sql
ALTER DATABASE {DatabaseName} SET ENABLE_BROKER;

If you encounter a GUID mismatch error, use the force reset command:

sql
ALTER DATABASE {DatabaseName} SET NEW_BROKER WITH ROLLBACK IMMEDIATE;

Change Log

  • 2022-11-14 Initial document creation.