zimdanen
zimdanen

Reputation: 5626

Change Response.Cache._cacheability after setting it to NoCache

It seems as if, once you set the cacheability on Response.Cache to NoCache, there's no way to change it again. Here is a simple but complete illustration of the issue:

protected void Page_Load(object sender, EventArgs e)
{
    FieldInfo fi = typeof(HttpCachePolicy).GetField(
        "_cacheability",
        BindingFlags.NonPublic | BindingFlags.Instance);

    // Default value = 6
    HttpCacheability first = (HttpCacheability)fi.GetValue(Response.Cache);

    // Can change it to Public
    Response.Cache.SetCacheability(HttpCacheability.Public);
    HttpCacheability second = (HttpCacheability)fi.GetValue(Response.Cache);

    // Can change it to Private
    Response.Cache.SetCacheability(HttpCacheability.Private);
    HttpCacheability third = (HttpCacheability)fi.GetValue(Response.Cache);

    // Can change it to NoCache
    Response.Cache.SetCacheability(HttpCacheability.NoCache);
    HttpCacheability fourth = (HttpCacheability)fi.GetValue(Response.Cache);

    // Can't go back to Private!  Stuck on NoCache
    Response.Cache.SetCacheability(HttpCacheability.Private);
    HttpCacheability fifth = (HttpCacheability)fi.GetValue(Response.Cache);
}

Am I missing something? Is there a way to do this?

EDIT: Of course, it works if I set it with Reflection, but I'm worried that there's something else happening when you set to HttpCacheability.NoCache that I would miss if I went behind the scenes.. and would prefer to do it in an officially-supported way anyway.

EDIT2: Same thing seems to happen with Private; can you only go more restrictive?

protected void Page_Load(object sender, EventArgs e)
{
    FieldInfo fi = typeof(HttpCachePolicy).GetField(
        "_cacheability",
        BindingFlags.NonPublic | BindingFlags.Instance);

    // Default value = 6
    HttpCacheability first = (HttpCacheability)fi.GetValue(Response.Cache);

    // Can change it to Private
    Response.Cache.SetCacheability(HttpCacheability.Private);
    HttpCacheability second = (HttpCacheability)fi.GetValue(Response.Cache);

    // Can't change to Public!  Stuck on Private
    Response.Cache.SetCacheability(HttpCacheability.Public);
    HttpCacheability third = (HttpCacheability)fi.GetValue(Response.Cache);

    // Can change to NoCache - Can only go more restrictive?
    Response.Cache.SetCacheability(HttpCacheability.NoCache);
    HttpCacheability fourth = (HttpCacheability)fi.GetValue(Response.Cache);
}

Upvotes: 1

Views: 669

Answers (2)

Kung Func
Kung Func

Reputation: 212

Indeed, it seems that reflection is the only safe way. Looking deeper in the code, you will need to extract the object of type HttpCachePolicy from the Cache property of the Response as you can see it below:

 public class HttpCachePolicyWrapper : HttpCachePolicyBase
{
    private HttpCachePolicy _httpCachePolicy;
    ....
}

To do so, just use reflection for setting the value of the correct object from the Response.Cache object like in the code below:

FieldInfo fi = typeof(HttpCachePolicy).GetField("_cacheability", BindingFlags.NonPublic | BindingFlags.Instance);
var objectToChange = (HttpCachePolicy)context.Response.Cache.GetType().GetField("_httpCachePolicy", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(context.Response.Cache);
fi.SetValue(objectToChange, HttpCacheability.Public);

This just adds more conrete steps to the zimdanen's answer

Upvotes: 0

zimdanen
zimdanen

Reputation: 5626

Cracked open Reflector and took a look inside HttpCachePolicy:

public void SetCacheability(HttpCacheability cacheability)
{
    if ((cacheability < HttpCacheability.NoCache) || (HttpCacheability.ServerAndPrivate < cacheability))
    {
        throw new ArgumentOutOfRangeException("cacheability");
    }
    if (s_cacheabilityValues[(int) cacheability] < s_cacheabilityValues[(int) this._cacheability])
    {
        this.Dirtied();
        this._cacheability = cacheability;
    }
}

s_cacheabilityValues is set during the static constructor:

s_cacheabilityValues = new int[] { -1, 0, 2, 1, 4, 3, 100 };

Dirtied() is called, but it just seems to set some flags:

private void Dirtied()
{
    this._isModified = true;
    this._useCachedHeaders = false;
}

It does look ilke there are rules for changing the values, but there doesn't look like they have much effect. As such, probably safe to just change using reflection.

fi.SetValue(Response.Cache, HttpCacheability.Private);

Upvotes: 1

Related Questions