Search code examples
asp.net-mvc-3web-configbrowser-cacheoutputcacheuser-roles

ASP.NET MVC3, How to Use Different OutputCacheProfile to Different User Role?


I know I can setup OutputCacheProfiles at web.config file.

I like to know how to apply different cache profile to different user role on page (controller) level?


Solution

  • You can decorate a controller with the OutputCache attribute which allows arguments to be passed as parameters. For example;

    [OutputCache(Duration = 3600, VaryByParam = "None")]
    

    There is no reason why you couldn't extend the attribute to take a further argument "RoleName" and perform a "Roles.IsUserInRole(RoleName)" and load different settings based upon each role.

    EDIT

    After comments from the author, I have reviewed my solution.

    Firstly, you can define you cache profiles within the Web.config;

    <caching>
      <outputCacheSettings>
        <outputCacheProfiles>
          <clear />
    
          <add name="Default" duration="60" />
          <add name="Admin" duration="10" />
        </outputCacheProfiles>
      </outputCacheSettings>
    </caching>
    

    I have extended the OutputCacheAttribute to account for authorisation of a user, and if the user authenticates, it loads that CacheProfile;

    public class AuthorisedOutputCache : OutputCacheAttribute
    {
      public string RoleName { get; set; }
    
      public override void OnActionExecuting(ActionExecutingContext filterContext)
      {
        // Default Profile.
        CacheProfile = "Default";
    
        if (HttpContext.Current.Request.IsAuthenticated)
        {
          if (Roles.IsUserInRole(RoleName))
          {
            CacheProfile = RoleName;
          }
        }
    
        base.OnActionExecuting(filterContext);
      }
    }
    

    Here is the Index.cshtml file for completeness;

    @model DateTime
    
    @{
      ViewBag.Title = "Index";
    }
    
    <h2>Index</h2>
    
    <p>
      The time is @Model.TimeOfDay.ToString()
    </p>
    

    Note: You will have to make sure to define a cacheprofile for each of your roles, aswell as a default for when no role is found.

    EDIT

    The author wished to know how to set the cache profile within the controller, I have posted a viable solution, but I don't like it because of the use of HttpContext.Items - so if anyone can suggest alternatives?

    Firstly, you must change the OnActionExecuting to OnActionExecuted;

    public class AuthorisedOutputCache : OutputCacheAttribute
    {
      public string RoleName { get; set; }
    
      public override void OnActionExecuted(ActionExecutedContext filterContext)
      {
        // Do you wish to force the profile?
        if (HttpContext.Current.Items["Cache.Force"] != null)
        {
          // Force the profile and remove the flag.
          CacheProfile = HttpContext.Current.Items["Cache.Force"].ToString();
          HttpContext.Current.Items.Remove("Cache.Force");
        }
        else
        {
          // If the profile has not been set - use the role based authorisation - 
          // otherwise, carry on as normal.
          if (string.IsNullOrEmpty(CacheProfile))
          {
            CacheProfile = "Default";
    
            if (HttpContext.Current.Request.IsAuthenticated)
            {
              if (Roles.IsUserInRole(RoleName))
              {
                CacheProfile = "Admin";
              }
            }
          }
        }
    
        base.OnActionExecuted(filterContext);
      }
    } 
    

    The following line allows you to set the profile within the controller;

    public ActionResult Index()
    {
      // Forces the cache profile to one of the name of "Mandatory".
      HttpContext.Items["Cache.Force"] = "Mandatory";
    
      return View(IndexViewName, DateTime.Now);
    }
    

    Let me know if I can be of further assistance,

    Matt