ASP.NET MVC 3 Custom RouteBase and OutputCache
Asked Answered
K

3

10

I have a problem with my custom RouteBase implementation and [OutputCache]. We have a CMS in which urls are mapped to certain content pages. Each type of content page is handled by a different controller (and different views). The urls are completely free and we want different controllers, so a "catchall" route is not usable. So we build a custom RouteBase implementation that calls the database to find all the urls. The database knows which Controller and Action to use (based on the content page type).

This works just great.

However, combining this with the [OutputCache] attribute, output caching doesn't work (the page still works). We made sure [OutputCache] works on our "normal" routes.

It is very difficult to debug outputcaching, the attribute is there we us it, it doesn't work... Ideas how to approach this would be very welcome, as would the right answer!

The controller looks like this:

public class TextPageController : BaseController
{
  private readonly ITextPageController textPageController;

  public TextPageController(ITextPageController textPageController)
  {
    this.textPageController = textPageController;
  }

  [OutputCache(Duration = 300)]
  public ActionResult TextPage(string pageid)
  {
    var model = textPageController.GetPage(pageid);
    return View(model);
  }
}

The custom route looks like this:

public class CmsPageRoute : RouteBase
{
  private IRouteService _routeService;
  private Dictionary<string, RouteData> _urlsToRouteData;

  public CmsPageRoute(IRouteService routeService)
  {
    this._routeService = routeService;
    this.SetCmsRoutes();
  }

  public void SetCmsRoutes()
  {
    var urlsToRouteData = new Dictionary<string, RouteData>();
    foreach (var route in this._routeService.GetRoutes()) // gets RouteData for CMS pages from database
    {
      urlsToRouteData.Add(route.Url, PrepareRouteData(route));
    }
    Interlocked.Exchange(ref _urlsToRouteData, urlsToRouteData);
  }

  public override RouteData GetRouteData(System.Web.HttpContextBase httpContext)
  {
    RouteData routeData;
    if (_urlsToRouteData.TryGetValue(httpContext.Request.Path, out routeData))
      return routeData;
    else
      return null;
  }

  public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
  {
    return null;
  }

  private RouteData PrepareRouteData(ContentRouteData contentRoute)
  {
    var routeData = new RouteData(this, new MvcRouteHandler());

    routeData.Values.Add("controller", contentRoute.Controller);
    routeData.Values.Add("action", contentRoute.Action);
    routeData.Values.Add("area", contentRoute.Area);
    routeData.Values.Add("pageid", contentRoute.Constraints["pageid"]); // variable for identifying page id in controller method

    routeData.DataTokens.Add("Namespaces", new[] { contentRoute.Namespace });
    routeData.DataTokens.Add("area", contentRoute.Area);

    return routeData;
  }

  // routes get periodically updated
  public void UpdateRoutes()
  {
    SetCmsRoutes();
  }
}

Thank you for reading until the end!

Kiser answered 14/9, 2012 at 11:39 Comment(6)
BTW I forgot to mention that I looked at the performance counter "ASP.NET Applications/Output Cache Entries", it is raised when I succesfully add a page to the cache, but not when the above controller is called using my custom RouteBase implementationKiser
Over the weekend I extracted the logics behind this to the smallest example, and it works. However in our bigger project it doesn't. So my effort should go into understanding why it doesn't work. Debugging output caching is not easy enough :(Kiser
where are you trying to cache? you want IIS to cache your pages or you want client (browser) to cache? caching in IIS may be difficult as IIS may work with simple forms of urls, but caching in client computer is easy by setting Response.ExpiresQuartan
I'll look it and provide you solution on that asapSunrise
Can you be more specific about what you mean by "OutputCache doesn't work"Baseler
Guys, the problem was not with the custom Route, but somehow (we don't understand how) only when we used the custom Route a Cookie that was being set caused the problems. The cookie was always set, and outputcache worked for non custom Route pages but didn't work for the pages that were routed by our custom Route...Kiser
D
1

In the end we tracked it down to a call to

 ... data-role="@this.FirstVisit" ...

in our _Layout.cshtml

This called a property on our custom view page that in turn called a service which always set a cookie. (Yikes setting cookies in services!, we know!)

Had it not been Friday and at the end of the sprint we might have noticed the cache busting Cache-Control: no-cache="Set-Cookie": Http Header.

I still don't understand why this only busted the cache for our custom RouteBase implementation and not all pages. All pages use the same _Layout.cshtml.

Duce answered 18/9, 2012 at 14:39 Comment(0)
F
0

You can try the following code if it helps

[OutputCache(Duration = 300, VaryByParam="*")] 
public ActionResult TextPage(string pageid) 
{ 
  var model = textPageController.GetPage(pageid); 
  return View(model);
}
Format answered 18/9, 2012 at 13:39 Comment(1)
VaryByParam="*" is default in MVC 3Kiser
P
0

You might look here to customize caching :

Also, check the cache location.

Pouter answered 18/9, 2012 at 14:7 Comment(1)
Thanks, I found these questions.Kiser

© 2022 - 2024 — McMap. All rights reserved.