Disable caching on a partial view in MVC 3
Asked Answered
H

6

21

I have an issue with a partial View being cached when it shouldn't be. This partial View is used to display the Logon/Logoff on a page. It uses the simple code below to figure out which link to display

@if(Request.IsAuthenticated) {    
    <a href="@Url.Action("LogOff", "Account", new { area = "" })">Log Off</a> 
}
else {
    <a href="@Url.Action("LogOn", "Account", new { area = "" })">Log On</a>
}

This partial View is called from with all pages in my MVC3 application, using

@Html.Partial("_HeaderView")  

In most of my controllers, I have the output cache defined, so I can take advantage of caching my content.

[OutputCache(Duration = 86400, VaryByParam = "*")]

Now my issue is that the entire page is being cached when I don't want the partial view to be. This is causing wrong behavior where in it sometimes displays LogOff even if the user is not logged in etc. Is there a way to cache all the content, except for the partial view in question?

Hui answered 9/1, 2012 at 10:24 Comment(1)
Nick, sorry had to add the comment here. Anyhow, it works when I remove the Nostore and changed the duration to 1. The only issue now is when the user logs in, they are taken to the home page but I have to explicitly refresh it for changes to take effect (to show logout instead of logon).Hui
S
9

What you are looking for is called Donut Caching. Here's a great article explaining what it is and how to make it work http://www.devtrends.co.uk/blog/donut-output-caching-in-asp.net-mvc-3

Sideshow answered 9/1, 2012 at 11:15 Comment(3)
I did try this one, but it didn't work for me as expected. I still had the same issues as using the outputcache attribute on the partial action, namely refreshing the home page manually.Hui
I'd love to help, but not seeing the code you had that's going to be hard :) It worked for me though. When you test, please remember about proper browser settings (sometimes they override the server settings, and case a false impression something doesnt' work )Unmeet
Nope, it was just me having a really slow moment. Working with a flu obviously not a good idea. But this works now. Thanks for your helpHui
K
18

You can disable caching by decorating the controller which displays your _HeaderView partial with the following:

[OutputCache(NoStore = true, Duration = 0, VaryByParam = "*")]
public ActionResult HeaderView()
{
    return PartialView("_HeaderView");
}
Kershner answered 9/1, 2012 at 10:28 Comment(13)
Hi Nick. Thanks for your input. But I do not have a controller action associated with the partial view (for now anyway). I am calling it using Html.Partial(). Is the only workaround to have a controller action with the above?Hui
Maybe you could add a shared controller and add a HeaderView action which returns PartialView("_HeaderView"); and is decorated with the annotation in my answer above. Hopefully this will give you the control you need and isn't a massive change. Let me know if that works for you or you need more clarification.Kershner
Thanks Nick. I will implement the changes and let you know how it goes.Hui
HI Nick. When I put in the outputcache annotation, the partial view doesn't display at all. This is how my 2 controller actions looks like [OutputCache(Duration=86400, VaryByParam="")] public ActionResult Index() { return View(); } [OutputCache(Duration = 0, VaryByParam = "")] [ChildActionOnly] public ActionResult HeaderView() { return PartialView("_HeaderView"); } From within the Index page, I am calling Html.Action to display the partial viewHui
Use @Html.Partial("HeaderView") to display the partial and make sure _HeaderView.cshtml is in your shared views folder. Also, make sure you use the NoStore property as this is used to inform proxy servers and browsers that they should not store a permanent copy of the view.Kershner
Nick, if I use Html.Partial, it wouldn't touch the controller action would it? Then the cache annotation wouldn't be used?Hui
Monday morning syndrome! @Html.RenderAction("HeaderView"); What output are you getting now?Kershner
Initial testing suggests you only need to specify the NoStore and Duration parameters. I actually only specified the NoStore = true at first but got an error saying that Duration was required. After placing Duration = 0 everything seems to work as intended already. Can you confirm that? I like to have the least amount of code as possible, even more so when we are dealing with hardcoded strings.Mancunian
Just made a few more tests and it seems that I can use Location = OutputCacheLocation.None instead of Duration = 0 also, which is even more clear :)Mancunian
I'm getting System.InvalidOperationException: OutputCacheAttribute for child actions only supports Duration, VaryByCustom, and VaryByParam values. Please do not set CacheProfile, Location, NoStore, SqlDependency, VaryByContentEncoding, or VaryByHeader values for child actions.Masson
And System.InvalidOperationException: Duration must be a positive numberMasson
@Masson you can have a look at julealgon's comment, it should sort you outImprimatur
It doesn't allow using Location either as mentioned in the error message. We ended up using the donut caching technique.Masson
S
9

What you are looking for is called Donut Caching. Here's a great article explaining what it is and how to make it work http://www.devtrends.co.uk/blog/donut-output-caching-in-asp.net-mvc-3

Sideshow answered 9/1, 2012 at 11:15 Comment(3)
I did try this one, but it didn't work for me as expected. I still had the same issues as using the outputcache attribute on the partial action, namely refreshing the home page manually.Hui
I'd love to help, but not seeing the code you had that's going to be hard :) It worked for me though. When you test, please remember about proper browser settings (sometimes they override the server settings, and case a false impression something doesnt' work )Unmeet
Nope, it was just me having a really slow moment. Working with a flu obviously not a good idea. But this works now. Thanks for your helpHui
B
3

We should set cache profile in the Web.config file instead of setting cache values individually in pages to avoid redundant code. We can refer the profile by using the CacheProfile property of the OutputCache attribute. This cache profile will be applied to all pages unless the page/method overrides these settings.

<system.web>
  <caching>
    <outputCacheSettings>
      <outputCacheProfiles>
        <add name="CacheProfile" duration="60" varyByParam="*" />
      </outputCacheProfiles>
    </outputCacheSettings>
  </caching>
</system.web>

And if you want to disable the caching from your action which returns partial view [_HeaderView], you can override the config cache settings by decorating that specific action method like shown below:

[OutputCache(NoStore = true, Duration = 0, VaryByParam = "*")]
public ActionResult RenderPartialView()
{
    return PartialView("_HeaderView");
}

Hope this would help you!

Barbiturate answered 2/9, 2014 at 10:6 Comment(0)
O
1

this working for me..

 public ActionResult LogOff()
 {
     AuthenticationManager.SignOut();  
     var url = Url.Action("Index", "Web"); 
     HttpResponse.RemoveOutputCacheItem(url);           
     return RedirectToAction("Index", "Web");
 }
Overgrowth answered 8/1, 2015 at 10:41 Comment(1)
+1 I tried all other solutions, they didn't work well, this worked like a charm for me... Thanks :) Note: I didn't have to use AuthenticationManager.Signout().;Widgeon
P
1

Took a little while to figure this one out after getting back into MVC. Just put the Cache setting directly in the Partial Header View. As in when displaying the user name. No need for global or server-side code. Only problem is once a page is cached, it will not refresh right away after login. But we keep the speed when needed in the initial browsing for products. Ok trade off in our case.


    @if ( Request.IsAuthenticated)
    {
            @* when we are authenticated, don't cache any more! *@
    HttpContext.Current.Response.Cache.SetCacheability(HttpCacheability.NoCache);
    HttpContext.Current.Response.Cache.SetNoStore();
    HttpContext.Current.Response.Cache.SetNoServerCaching();
            @*@Html.Raw(DateTime.Now.ToString())*@
    @Html.ActionLink("Welcome " + ( String.IsNullOrEmpty(((System.Security.Claims.ClaimsIdentity)User.Identity).FindFirstValue("UserName")) ? User.Identity.Name : ((System.Security.Claims.ClaimsIdentity)User.Identity).FindFirstValue("UserName")), "Index", "Manage", routeValues: new { Area = "Store" }, htmlAttributes: new { title = "Manage"})
    }
    else
    {
    }

Praedial answered 10/2, 2017 at 20:17 Comment(0)
A
1

Fast forwarding to 2023, in .NET Core you can do something like this to turn off cache for some parts of the page:

<cache enabled="false">
@if(Request.IsAuthenticated) {    
    <a href="@Url.Action("LogOff", "Account", new { area = "" })">Log Off</a> 
}
else {
    <a href="@Url.Action("LogOn", "Account", new { area = "" })">Log On</a>
}
</cache>

Read more about the <cache>-tag helper here: https://learn.microsoft.com/en-us/aspnet/core/mvc/views/tag-helpers/built-in/cache-tag-helper?view=aspnetcore-7.0

Annals answered 4/3, 2023 at 12:43 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.