0

Can I do output caching in pages that I can detect in code behind if Session["user"] is null or not, and cache the page for 5 minutes if it is not cached yet (output caching not expired) and and the Session["user"] == null.

I don't want to use custom output cache because it will also cache for log in users. This way only anonymous visitors will see a cached, page, and logged in users will get the non cached ones

I do this because some of my pages looks different when the user is logged in or not. I want crawlers and regular visitors to see the original page, not getting a private data from a user that triggered the caching by mistake.

How can I do this in code behind C#?

Liron Harel
  • 10,819
  • 26
  • 118
  • 217
  • You should be able to use `GetVaryByCustomString`. Take a look at these http://blog.danielcorreia.net/asp-net-mvc-vary-by-current-user/ http://stackoverflow.com/questions/1043112/programatically-control-output-caching-disable-or-enable-cache-according-to-pa – smoksnes Jul 27 '16 at 05:08
  • @smoksnes but this will create a cache for each user, right? – Liron Harel Jul 27 '16 at 05:34
  • Yes, you're right. My bad. – smoksnes Jul 27 '16 at 05:40

1 Answers1

1

You should be able to create a custom OutputCacheProvider.

In your global.asax:

    public override string GetOutputCacheProviderName(HttpContext context)
    {
        bool isInSession = true; // Determine here.
        if (isInSession)
        {
            return "CustomProvider";
        }

        // Or by page:
        if (context.Request.Path.EndsWith("MyPage.aspx"))
        {
            return "SomeOtherProvider";
        }

        return base.GetOutputCacheProviderName(context);
    }

Then create your provider:

public class SessionBasedCacheProvider : OutputCacheProvider
{
    public override object Get(string key)
    {
        return null; // Do not cache.
    }

    public override object Add(string key, object entry, DateTime utcExpiry)
    {
        // Basically let it "fall through" since we don't want any caching.
        return entry;
    }

    public override void Set(string key, object entry, DateTime utcExpiry)
    {
        // Basically let it "fall through" since we don't want any caching.            
    }

    public override void Remove(string key)
    {
        // Basically let it "fall through" since we don't want any caching.
    }
}

By returning null from Set, you will not cache the items.

The key value that identifies the specified entry in the cache, or null if the specified entry is not in the cache.

And add your provider to the config:

  <system.web>
    <caching>
      <outputCache defaultProvider="AspNetInternalProvider">
        <providers>
          <clear/>
          <add name="CustomProvider" type="YourNamespace.SessionBasedCacheProvider, YourNamespace, Version=1.0.0.0, Culture=neutral"/>
        </providers>
      </outputCache>
    </caching>
</web>

To enable it you should be able to use. Just set this to the top of the ASPX-page. Note the Location attribute, depending on where you want it to cache.

<%@ OutputCache Duration="60" VaryByParam="None" Location="Server"  %>

https://msdn.microsoft.com/en-us/library/hdxfb6cy(v=vs.85).aspx

https://msdn.microsoft.com/en-us/magazine/gg650661.aspx

Alternatively you can always use the same CacheProvider, and let it determine if the item should be cached or not.

public class CustomOutputCacheProvider : OutputCacheProvider
{
    public override object Add(string key, object entry, DateTime utcExpiry)
    {
        // Determine if in session.
        bool isInSession = true;
        if (isInSession)
        {
            return null;
        }

        // Do the same custom caching as you did in your 
        // CustomMemoryCache object
        var result = HttpContext.Current.Cache.Get(key);

        if (result != null)
        {
            return result;
        }

        HttpContext.Current.Cache.Add(key, entry, null, utcExpiry,
            System.Web.Caching.Cache.NoSlidingExpiration, System.Web.Caching.CacheItemPriority.Normal, null);

        return entry;
    }

    public override object Get(string key)
    {
        return HttpContext.Current.Cache.Get(key);
    }

    public override void Remove(string key)
    {
        HttpContext.Current.Cache.Remove(key);
    }

    public override void Set(string key, object entry, DateTime utcExpiry)
    {
        HttpContext.Current.Cache.Add(key, entry, null, utcExpiry,
            System.Web.Caching.Cache.NoSlidingExpiration, System.Web.Caching.CacheItemPriority.Normal, null);
    }
}

Code from http://www.haneycodes.net/custom-output-caching-with-mvc3-and-net-4-0-done-right/

Note, I've not tested this with Session, but in theory it should work. But I suggest that you really test it before releasing it. Caching is always much harder than one might think... :(

UPDATE: Since Session isn't available you should be able to use a cookie instead.

protected void Session_Start(object sender, EventArgs e)
{
    Response.Cookies.Add(new HttpCookie("SessionCookie", "some_value"));
}

public override string GetOutputCacheProviderName(HttpContext context)
{
    bool isInSession = true; // Determine here.

    if (context.Request.Cookies["SessionCookie"] != null)
    {
        // Use your CustomProvider
    }

    if (isInSession)
    {
        return "CustomProvider";
    }

    // Or by page:
    if (context.Request.Path.EndsWith("MyPage.aspx"))
    {
        return "SomeOtherProvider";
    }

    return base.GetOutputCacheProviderName(context);
}

Just remember to delete the cookie, or handle it in some way.

smoksnes
  • 10,509
  • 4
  • 49
  • 74
  • Thanks. What do I put in the page that I wan this custom outputcache to work? – Liron Harel Jul 27 '16 at 05:59
  • If I use the second code (" same CacheProvider,"), I can remove the changed from the web.config? – Liron Harel Jul 27 '16 at 06:04
  • I've updated my answer. But you still need to register it in web.config. Otherwise .NET will not find your provider by name. Note that it's the name registered as ` – smoksnes Jul 27 '16 at 06:07
  • Should I use this code in the page: "<%@ OutputCache Duration="60" Location="CustomPRovider" VaryByParam="name" %>" – Liron Harel Jul 27 '16 at 06:09
  • Location is where the cache is stored. Eg. Server, Client. I've added a reference to it in my answer. And you don't need any VaryByParam since it's handled by the provider instead. – smoksnes Jul 27 '16 at 06:12
  • so it shuold be : VaryByCustom="CustomProvider" – Liron Harel Jul 27 '16 at 06:14
  • I don't think you need any VaryBy since the CustomProvider will take care of that. VaryBy is used to help determine which pages are different. But in your case it's handled by the CustomProvider. And by setting `GetOutputCacheProviderName` in global.asax you will determine there instead. – smoksnes Jul 27 '16 at 06:16
  • context.Session is null although the user is logged in Session["user"] = 5 – Liron Harel Jul 27 '16 at 06:25
  • if (context != null && context.Session != null) { isInSession = true; } – Liron Harel Jul 27 '16 at 06:27
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/118383/discussion-between-smoksnes-and-idan-shechter). – smoksnes Jul 27 '16 at 06:27