0

We use a ActionFilterAttribute to inject some parameters into actions and it works great.

But when we add OutputCache it varies exclusively on "MyID" when Html.RenderAction() is used and not when surfing directly to the action.

Any ideas how to get OutputCache to always recognize "MyID"?

Controller

[SiteIDs, OutputCache]
public ActionResult SiteContent(string myID)
{
    return Content(myID);
}

ActionFilter

public class SiteIDs : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        if (filterContext.ActionParameters.ContainsKey("MyID"))
        {
            filterContext.ActionParameters["MyID"] = GetMyIDByHostname();
        }

        base.OnActionExecuting(filterContext);
    }
}
Johan Olsson
  • 715
  • 11
  • 22

2 Answers2

0

With the OutputCacheAttribute, action filters will not execute when a page is retrieved from the cache. You should probably use mvcdonutcaching in order to have the action filters executed even when retrieving from the cache. I'd recommend reading this.

Henri Benoit
  • 705
  • 3
  • 10
  • I have tried MvcDonutCaching now and my ActionFilter runs every time but it does not work (neither via Html.RenderAction() or directly). I also looked at the source for both and it seems like [OutputCache] uses ActionParameters and [DonutOutputCache] RouteData when generating a key. – Johan Olsson Sep 16 '15 at 07:40
0

Option 1

According to this answer, you just need to use VaryByParam = "*" and it will automatically vary by the parameters you pass the action method.

[SiteIDs, OutputCache(VaryByParam = "*")]
public ActionResult SiteContent(string myID)
{
    return Content(myID);
}

However, that may not work by using an IActionFilter (haven't tried it). You might try using an IValueProvider instead (which is a cleaner way to do what you are doing with the action filter, anyway).

Option 2

You could use VaryByCustom and GetVaryByCustomString to vary the cache by hostname.

[SiteIDs, OutputCache(VaryByParam = "none", VaryByCustom = "hostname")]
public ActionResult SiteContent(string myID)
{
    return Content(myID);
}

And in your Global.asax file:

public class MvcApplication : System.Web.HttpApplication
{
    protected void Application_Start()
    {
        AreaRegistration.RegisterAllAreas();

        WebApiConfig.Register(GlobalConfiguration.Configuration);
        FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
        RouteConfig.RegisterRoutes(RouteTable.Routes);
        BundleConfig.RegisterBundles(BundleTable.Bundles);
        AuthConfig.RegisterAuth();
    }

    public override string GetVaryByCustomString(HttpContext context, string custom)
    {
        if (custom == "hostname")
        {
            return "HostName=" + context.Request.Url.Host;
        }

        return base.GetVaryByCustomString(context, custom);
    }
}

Keep in mind your action filter will only be hit if the OutputCache has not been set. So you need to vary the cache on the same value (or values) that you vary your ID from. The simplest solution is to use something that is already in HttpContext, such as host name.

Community
  • 1
  • 1
NightOwl888
  • 55,572
  • 24
  • 139
  • 212
  • Option 1: Yes, VaryByParam = "*" is default and it works with a IActionFilter when Html.RenderAction("MyAction", "MyController", new { notIncludingMyIDInRouteValueDictinary = "foo" }) is used but not when accessing the Action directly. I tried with a IValueProvider today, no difference. Option 2: Have not tried this yet. – Johan Olsson Sep 15 '15 at 13:43