OutputCache varying by a complex object property passed using ModelBinder from Session

661 Views Asked by At

We are using Couchbase for our Session and for OutputCache.

In this context, how can we cache by a complex object that is being passed to the method using a Custom Model Binder that retrieves a value from the Session?

This is the signature of the method I want to cache with the OutputCache attribute:

[HttpGet]
[OutputCache(CacheProfile = "MyObjectsCache", VaryByParam = "myParam")]
public ActionResult Index([ModelBinder(typeof (CustomVariableSessionModelBinder<MyClass>))] MyClass myParam)
{

Note: The ModelBinder is being used here for reasons beyond me and I cannot change it.

MyClass is a complex object that has an Id. I want to use the Id as the caching identifier.

public class MyClass
{
   public int Id{get;set;}
   //Other Properties

This is how the object is being retrieved from Session:

var sessionKey = typeof (MyNamespace.MyClass).FullName;
var httpContext = HttpContext.Current;
MyNamespace.MyClass newObject = null;

if (httpContext.Session != null)
{
    newObject = httpContext.Session[sessionKey] as MyNamespace.MyClass;
}

Is it possible yo use VaryByParam for this scenario or will I have to use VaryByCustom?

1

There are 1 best solutions below

1
On

I haven't tested this, but it should work. It's pretty much your only option anyways, though.

In addition to the built in ways to vary, you can vary by "Custom". This will call into a method in Global.asax you'll need to override: GetVaryByCustomString. Importantly for you situation here, this method is passed HttpContext, so you should be able to look into the session. Essentially, the solution will look something like:

public override string GetVaryByCustomString(HttpContext context, string custom)
{
    var args = custom.ToLower().Split(';');
    var sb = new StringBuilder();

    foreach (var arg in args)
    {
        switch (arg)
        {
            case "session":
                var obj = // get your object from session
                // now create some unique string to append
                sb.AppendFormat("Session{0}", obj.Id);
        }
    }

    return sb.ToString();
}

This is designed to handle multiple different types of "custom" vary types. For example, if you wanted to vary by "User", which is common, you can merely add a case for that in your switch. The important part is that the string returned by this method is actually what the output cache varies on, so you want that to be unique for the situation. This is why I prefixed the object's id with "Session" here. For example, if you just added the id, let's say 123, and then in another scenario you varied by user and that string was composed of just the user's id, which happened to be 123 as well. It would be the same string to the output cache, and you'd end with some weird results. Just be mindful of what the custom string looks like.

Now, you'd just alter your OutputCache attribute like:

[OutputCache(CacheProfile = "MyObjectsCache", VaryByParam = "myParam", VaryByCustom = "Session")]

Note: to vary by multiple custom things at once, you'd separate them with a ; (based on how the code above works). For example: VaryByCustom = "Session;User"