Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

OutputCache varying by a complex object property

I have a controller action that receives a complex object as a parameter, I need the OutputCache to vary by one of the properties of this complex object. Is this possible? How?

like image 694
Juliano Nunes Silva Oliveira Avatar asked Mar 04 '13 01:03

Juliano Nunes Silva Oliveira


People also ask

What is the use of OutputCache attribute in MVC?

The output cache enables you to cache the content returned by a controller action. That way, the same content does not need to be generated each and every time the same controller action is invoked. Imagine, for example, that your ASP.NET MVC application displays a list of database records in a view named Index.

What is Varybyparam OutputCache?

It allows varying the cached output by GET query string or form POST parameters. For instance, you can vary the user-control output to the cache by specifying the user-control name along with either a query string or a form POST parameter. For more information, see Caching Multiple Versions of User Control Output.

How caching is done in ASP net?

To manually cache application data, you can use the MemoryCache class in ASP.NET. ASP.NET also supports output caching, which stores the generated output of pages, controls, and HTTP responses in memory. You can configure output caching declaratively in an ASP.NET Web page or by using settings in the Web. config file.

Where is Output cache stored?

The output cache is located on the Web server where the request was processed. This value corresponds to the Server enumeration value. The output cache can be stored only at the origin server or at the requesting client. Proxy servers are not allowed to cache the response.


2 Answers

if you have a model like

public class person{
 public string Name {get;set;}
 public string location {get;set;}
} 

and in the (strongly typed)view you have a form

 @model Person

 @Html.BeginForm(){
  @Html.TextBoxFor(x=>x.Name)
  @Html.TextBoxFor(x=>x.location)
 }

and you submit the form to an ActionResult savePerson, with varying signature like

public ActionResult savePerson(Person p){
 // p.Name
 // p.location

}

or

public ActionResult savePerson(string Name, string location){

}

therefore i think if you annotate the ActionResult like

[OutputCache(Duration=3600, VaryByParam="Name")]
public ActionResult savePerson(Person p)
{
    //
    return View();
}

it will do for you, or if you have a complex model like

public class person{
 public string Name {get;set;}
 public Location loc {get;set;}
} 
public class Location{
  public string address
}

try

[OutputCache(Duration=3600, VaryByParam="Person.Location.address")]
public ActionResult savePerson(Person p)
{
    //
    return View();
}
like image 108
Dakait Avatar answered Nov 03 '22 01:11

Dakait


I had the same requirement as above and came up with a slightly different approach

The class

/// <summary>
/// This class is used to encapsulate search filters for monitor graphs
/// </summary>
public class DatacarMonitorSearchCriteriaModel
{
    public int? SynergyCode { get; set; }

    [Required]
    [DataType(DataType.Date)]
    public DateTime StartDate { get; set; }

    [Required]
    [DataType(DataType.Date)]
    public DateTime EndDate { get; set; }

    /// <summary>
    /// Filter to apply 
    /// </summary>
    public IEnumerable<int> Countries { get; set; }


    public DatacarMonitorSearchCriteriaModel()
    {
        Countries = new List<int>();
    }



}

OutputCacheComplexAttribute

/// <summary>
/// <para>
///     An instance of this class mimic the behaviour of OutputCacheAttribute but for complex objects.
/// </para>
/// <para>
///     It allows to cache the output of any action that takes complex objects 
/// </para>
/// </summary>
public class OutputCacheComplexAttribute : OutputCacheAttribute
{
    private readonly Type[] _types;

    private string _cachedKey;

    /// <summary>
    /// Initializes a new instance of the <see cref="OutputCacheComplexAttribute"/> class.
    /// </summary>
    /// <param name="types">Types that this attribute will lookup for in QueryString/Form data and store values in cache.</param>
    /// <exception cref="System.ArgumentOutOfRangeException">type;type cannot be null</exception>
    public OutputCacheComplexAttribute(params Type[] types)
    {
        if (types == null)
        {
            throw new ArgumentOutOfRangeException("type", "type cannot be null");
        }
        _types = types;
    }

    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        StringBuilder sbCachedKey = new StringBuilder();
        if (filterContext.HttpContext.Request.Url != null)
        {
            string path = filterContext.HttpContext.Request.Url.PathAndQuery;
            IDictionary<string, object> parameters = filterContext.ActionParameters;

            //we need to compute a cache key which will be used to store the action output for later retrieval
            //The cache key scheme is 
            //    {url}:{key 1}:{value};[{key 2}:{value 2}[; ... {key n}:{value n}]];  
            // where : 
            //  - url is the url of the action that will be executed
            //  - key n is the name of the n-th parameter
            //  - value n is the value of the n-th parameter as json string.
            foreach (KeyValuePair<string, object> kv in parameters)
            {
                var kv1 = kv;
                if (kv.Value != null && _types.AtLeastOnce(t => t.IsInstanceOfType(kv1.Value)))
                {
                    sbCachedKey = sbCachedKey.AppendFormat("{0}:{1};",kv.Key,
                        JsonConvert.SerializeObject(kv.Value, Formatting.None, new JsonSerializerSettings()
                        {
                            NullValueHandling = NullValueHandling.Ignore,
                            ReferenceLoopHandling = ReferenceLoopHandling.Ignore
                        }));
                }
            }

            _cachedKey = String.Format("{0}:{1}:{2}", GetType().Name, path, sbCachedKey.ToString());
        }


        if (!String.IsNullOrWhiteSpace(_cachedKey) && filterContext.HttpContext.Cache[_cachedKey] != null)
        {
            filterContext.Result = (ActionResult)filterContext.HttpContext.Cache[_cachedKey];
        }
        else
        {
            base.OnActionExecuting(filterContext);
        }
    }

    public override void OnActionExecuted(ActionExecutedContext filterContext)
    {
        if (!String.IsNullOrWhiteSpace(_cachedKey))
        {
            filterContext.HttpContext.Cache.Add(_cachedKey, filterContext.Result, null,
                DateTime.UtcNow.AddSeconds(Duration), Cache.NoSlidingExpiration,
                CacheItemPriority.Default, null);
        }

        base.OnActionExecuted(filterContext);
    }
}

Attribute usage

[OutputCacheComplex(typeof(DatacarMonitorSearchCriteriaModel), Duration = OutputCacheDurationInSeconds, Location = OutputCacheLocation.Server)]
public async Task<JsonNetResult<DatacarMonitorDetailModel>> ReadMonitorDetailsJson([DataSourceRequest] DataSourceRequest request, DatacarMonitorSearchCriteriaModel criteria)
{ 
     //some really complicated code here
}

with this new attribute, you can specify which type[s] to use for caching and the cache key will be computed based on values of each its properties.

like image 20
Saï Avatar answered Nov 03 '22 02:11

Saï