HttpResponse.RemoveOutputCacheItem is not working

2019-01-18 11:50发布

问题:

I have an ActionResult which is cached.

[OutputCache(Duration = 3600, VaryByParam = "product_Id")]
public ActionResult ProductPreview(Guid product_Id)
{
     // just for testing the cache
     System.Threading.Thread.Sleep(4000);
     return PartialView("ProductPreview", _repository.CreateProductModel(product_Id));
}

The good part is that the cache is working. After the first load, the result is shown without any 4seconds delay.

However, i need to clear the cache when some changes has been made to that product.

I tried to clear cache doing like this:

public ActionResult RemoveCache()
{
    var url = Url.Action("ProductPreview", "Common");
    // also tried with parameter
    // var url = Url.Action("ProductPreview", "Common", new { @product_Id = "productId" });
    HttpResponse.RemoveOutputCacheItem(url);

    return RedirectToAction("Index");
}

I also tried to call RemoveCache method with both ajax and full page refresh, and non of them is working.

What can i do? Where is the problem?

回答1:

The RemoveOutputCacheItem works only with route parameters, not query string. So you could modify your route definition:

routes.MapRoute(
    "Default",
    "{controller}/{action}/{product_Id}",
    new { controller = "Home", action = "Index" }
);

Now you can use the RemoveOutputCacheItem method:

public ActionResult RemoveCache(Guid product_Id)
{
    var url = Url.Action("ProductPreview", "Common", new { product_Id = product_Id });
    // the url must look like this: /Common/ProductPreview/eeb2fe32-db58-4fc3-87c8-b47480fbe094
    // for the RemoveOutputCacheItem method to work
    HttpResponse.RemoveOutputCacheItem(url);
    return RedirectToAction("Index");
}

UPDATE:

Here's my test case:

Controller:

public class HomeController : Controller
{
    public ActionResult Index()
    {
        return View();
    }

    [OutputCache(Duration = 3600, VaryByParam = "product_id")]
    public ActionResult ProductPreview(Guid product_id)
    {
        var model = string.Format(
            "{0} - {1}", 
            product_id, 
            DateTime.Now.ToLongTimeString()
        )
        return PartialView("_Foo", model);
    }

    public ActionResult RemoveCache(Guid product_id)
    {
        var url = Url.Action(
            "ProductPreview", 
            "Home", 
            new { product_id = product_id }
        );
        HttpResponse.RemoveOutputCacheItem(url);
        return RedirectToAction("Index");
    }
}

View (~/Views/Home/Index.cshtml):

@{
    var productId = Guid.NewGuid();    
}

@Html.ActionLink("product 1", "ProductPreview", new { product_id = Guid.NewGuid() })
<br/>
@Html.ActionLink("product 2", "ProductPreview", new { product_id = productId })
<br/>
@Html.ActionLink("product 3", "ProductPreview", new { product_id = Guid.NewGuid() })
<br />

@Html.ActionLink(
    "clear cache for the second product", 
    "RemoveCache", 
    new { product_id = productId }
)

Partial view (~/Views/Home/_Foo.cshtml):

@model string
@Model

and in global.asax:

public static void RegisterRoutes(RouteCollection routes)
{
    routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

    routes.MapRoute(
        "Default",
        "{controller}/{action}/{product_id}",
        new { controller = "Home", action = "Index", product_id = UrlParameter.Optional }
    );
}

UPDATE 2:

Now that you have shown your code it seems that you are using the Html.RenderAction helper and the ProductPreview is a child action. Child actions are not stored in the same cache as normal views and the HttpResponse.RemoveOutputCacheItem helper doesn't work at all with cached child actions. If you look carefully in my previous example you will see that I used standard links for the ProductPreview action.

Currently what you are trying to achieve is not possible in ASP.NET MVC 3. If you want to use donut output caching I would recommend you the following article. Hopefully this functionality will be added in ASP.NET MVC 4.