OutputCache with VaryByCustom not working
Asked Answered
P

1

6

I'm trying to implement caching in ASP.NET MVC 4 using the OutputCache attribute.

Here is my controller action:

[HttpGet]
[OutputCache(Duration = CACHE_DURATION, VaryByCustom = "$LanguageCode;myParam", Location = OutputCacheLocation.Server)]
public JsonResult MyAction(string myParam)
{
    // this is called also if should be cached!
}

And here is the GetVaryByCustomString in the Global.asax:

public override string GetVaryByCustomString(HttpContext context, string arg)
{
    var pars = arg.Split(';');
    if (pars.Length == 0) return string.Empty;

    var res = new System.Text.StringBuilder();
    foreach (var s in pars)
    {
        switch (s)
        {
            case "$LanguageCode":
                var culture = CultureManager.GetCurrentCulture();
                res.Append(culture.Name);
                break;
            default:
                var par = context.Request[s];
                if (par != null)
                    res.AppendFormat(par);
                break;
        }
    }
    return base.GetVaryByCustomString(context, res.ToString());
}

This method is always called and returns the right value (e.g. "it123").

If I call the action with the only myParam parameter, the cache works correctly.

http://localhost:1592/MyController/MyAction?myParam=123 // called multiple times always read from cache

The problem is that when I call the action with another parameter, not included in the VaryByCustom string, the controller action is called anyway, also if is should be cached and the GetVaryByCustomString returns the same result.

http://localhost:1592/MyController/MyAction?myParam=123&dummy=asdf // called multiple times with different 'dummy' values always calls the action

Any idea?

Photocompose answered 10/4, 2014 at 15:40 Comment(0)
H
10

First you have to change your [OutputCache] to include VaryByParam="":

[OutputCache(Duration = CACHE_DURATION, VaryByCustom = "$LanguageCode;myParam", VaryByParam = "", Location = OutputCacheLocation.Server)]

Becuase by default its value is "*" (All).

Then in your GetVaryByCustomString() method, try to return your generated string instead of calling the base method:

return res.ToString();

Here's the source code of the base.GetVaryByCustomString() method:

public virtual string GetVaryByCustomString(HttpContext context, string custom) {

        if (StringUtil.EqualsIgnoreCase(custom, "browser")) {
            return context.Request.Browser.Type;
        }

        return null;
    }

As you can see, it doesn't do what you think it does, it only compares the Browser type with the string you provided and if there's no match it returns null, not the string you provided.

(I suspect the [OutputCache] change alone will suffice, but try changing the method as well)

Hanleigh answered 10/4, 2014 at 17:47 Comment(3)
You're right about the VaryByParam property. I changed my GetVaryByCustomString only for testing purposes, but now it's ok. Thanks.Photocompose
Thanks a ton! This answer was a lifesaver.Bakst
And for a parameterless action, use [OutputCache(Duration = CACHE_DURATION, VaryByCustom = "$LanguageCode;", VaryByParam = "", Location = OutputCacheLocation.Server)]Bakst

© 2022 - 2024 — McMap. All rights reserved.