Search code examples
asp.net-mvccachingumbraco

Cache partial view using Umbraco CachedPartial for different model


I'm using CachedPartial html helper which caches that partial view.

@Html.CachedPartial("PartialView", MyModel, 3600, true);

In my view, I have the following situation:

@Html.CachedPartial("PartialView", MyModel, 3600, true);

@Html.CachedPartial("AnotherPartialView", MyModel1, 3600, true);

@Html.CachedPartial("PartialView", MyModel3, 3600, true); // I want to reuse partial view

Seems that first and third view are identically because of CachedPartial ...

How to make cached partial by model parameter ?

I tried to use

@Html.CachedPartial("PartialView", MyModel, 3600, true, false, new ViewDataDictionary(MyModel3));

but same thing.


Edit: I used a different approach than DZL and it works

  public static IHtmlString CachedPartial( this HtmlHelper helper, string partialViewName, object model, string cacheKey = null )
  {
     if ( string.IsNullOrWhiteSpace( cacheKey ) ) {
        return helper.CachedPartial( partialViewName, model, AppSettings.PartialCachingSeconds, true );
     }

     Func<object, ViewDataDictionary, string> fc = ( o, v ) => cacheKey;

     return helper.CachedPartial( partialViewName, model, AppSettings.PartialCachingSeconds, true, contextualKeyBuilder: fc );
  }

and then

@Html.CachedPartial("PartialView", MyModel, "a_key");

@Html.CachedPartial("AnotherPartialView", MyModel1);

@Html.CachedPartial("PartialView", MyModel3, "another_key"); // I want to reuse partial view

Solution

  • If you want that you will need to create your own implementation of CachedPartial, something like this:

    using System;
    using System.Web.Mvc;
    using System.Web.Mvc.Html;
    using Umbraco.Web;
    using System.Web;
    using System.Runtime.Caching;
    
    public static class CachedPartialExtensions
    {
        public static IHtmlString MyCachedPartial(
            this HtmlHelper htmlHelper,
            string partialViewName,
            object model,
            int cachedSeconds,
            bool cacheByPage = false,
            string cacheKey = null,
            ViewDataDictionary viewData = null
        )
        {
            var newCacheKey = "fpc-";  //prefix to know which keys to clear on page publish (in Bootstraper.cs file)
            newCacheKey += partialViewName;
            if (cacheByPage)
            {
                newCacheKey += "page-" + UmbracoContext.Current.PageId;
            }
            if (!string.IsNullOrEmpty(cacheKey))
            {
                newCacheKey += "key-" + cacheKey;
            }
    
            var result = MemoryCache.Default.Get(newCacheKey) as MvcHtmlString;
            if(result == null)
            {
                result = htmlHelper.Partial(partialViewName, model, viewData);
                MemoryCache.Default.Add(new CacheItem(newCacheKey, result), new CacheItemPolicy
                {
                    AbsoluteExpiration = DateTimeOffset.Now.AddSeconds(cachedSeconds)
                });
            }
    
            return result;
        }
    }
    

    And then you will be able to provide your own keys for cache:

    @Html.MyCachedPartial("PartialView", Model, 60, cacheKey: "model1key", cacheByPage: true)
    @Html.MyCachedPartial("PartialView", Model2, 60, cacheKey: "model2key", cacheByPage: true)
    

    EDIT:

    From version 7 there is an overload for CachedPartial that allows the key to be passed in

    public static IHtmlString CachedPartial(
        this HtmlHelper htmlHelper, 
        string partialViewName, 
        object model, 
        int cachedSeconds, 
        bool cacheByPage = false, 
        bool cacheByMember = false, 
        ViewDataDictionary viewData = null, 
        Func<object, ViewDataDictionary, string> contextualKeyBuilder = null);
    

    The use case for this would be:

    @Html.CachedPartial(
        "PartialView", 
        MyModel3, 
        3600, 
        cacheByPage: true,
        contextualKeyBuilder: (model, viewData) =>
        {
           return (model as MyViewModel).Id.ToString();
        });