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?
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.
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.
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.
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.
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();
}
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With