Monday, August 31, 2015

Sitecore Caching Revisited

Previously I wrote about using the Sitecore Custom Cache.  Unfortunately I don’t think I put enough detail in that post.  Some may have been a little confused by it. 

Creating the Cache

Really creating a cache is almost too easy, you simply create a class that inherits from CustomCache and then implement SetObject  and GetObject as I said in my previous post.
public class MyCustomCache : CustomCache
 {
     public MyCustomCache(string name, long maxSize)
         : base(name, maxSize)
     {
     }

     public void SetObject(string key, object value)
     {
         base.SetObject(key, value, Sitecore.Reflection.TypeUtil.SizeOfObject());
     }

     public new object GetObject(object key)
     {
         return base.GetObject(key);
     }
 }

The Cache Manager

The problem with stopping here is though we have the cache we do not have an instance of it created.  This is also a very simple task.  The easiest way to implement it is just to create a static instance of it somewhere.  More properly though would be to create a static cache manager. 
public static class MyCacheManager
{
    private static readonly MyCustomCache Cache = new MyCustomCache(
        "My.NameSpace.Custom.Cache",
        StringUtil.ParseSizeString(Sitecore.Configuration.Settings.GetSetting("My.Custom.Cache.Size.Setting.Name""5MB")));
 
    public static object Get(string key)
    {
        return Cache.GetObject(key);
    }
 
    public static void Set(string key, object value)
    {
        Cache.SetObject(key, value);
    }
}

Clearing the Cache

It really is that easy.  It may be wise though to add a cache clearing mechanism as well maybe add a new static method to the manager.
public static void Clear()
{
    Cache.Clear();
}


Of course you still need to trigger this call using the Sitecore Pipelines which is also very easy, just add the call anywhere Sitecore normally clears the cache.

Managing the Sitecore xDB Contact Card without requiring the Tracker


The Normal Way

When working with a user profile in Sitecore 8, we generally get the contact card by using Tracker.Current.Contact.  Of course if the tracker is null we will call Tracker.Start(), and of course that would also mean the Contact is null so we would also need to call Tracker.Identify().
This works out well in most situations since the tracker is maintained across the session.  If you don’t have a session though this could mean a big headache.  You would normally need to create a Session and an HttpContext, then create and push a tracker into the stack.  While this is not excessively resource intensive when you are doing a lot of calls it can add up quickly.

Getting the Card

There is an alternate way to get the card though.  You can use the ContactManager to load the contact by username, if there is not a contact identified by that username then it will return null and you can then create a new Contact and insert it.
var user = Context.User.Identity.Name;
if (string.IsNullOrEmpty(user)) return null;

var contactManager = Factory.CreateObject("tracking/contactManager"trueas ContactManager;
var contact = contactManager.LoadContactReadOnly(user);

Saving Updates

If you are updating this contact keep in mind that it will not persist across the session so you will need to flush it to the database before the end of the request.  This is also done with the Contact Manager by calling FlushContactToXdb and passing the contact, then by calling SaveAndReleaseContact also passing in the contact. 
var manager = Factory.CreateObject("tracking/contactManager"trueas ContactManager;
 
manager.FlushContactToXdb(contact);
manager.SaveAndReleaseContact(contact);
 

Submitting your Session

Just to be on the safe side of things it is not a bad idea to submit your context.  You may be thinking that that requires a session.  Fortunately Sitecore can create a very lightweight session context for this use.  You can use the SessionContextManager and call GetSession passing in the contact id, an empty guid, and two Boolean true values.  Once you have the session you can call Submit on the SessionContextManager and pass this session in.
var contextManager =
    Factory.CreateObject("tracking/sessionContextManager"trueas SessionContextManagerBase;
 
var session = contextManager.GetSession(c.ContactId, Guid.Empty, truetrue);
contextManager.Submit(session);

While this solution is not perfect, you can expand on this solution to build a robust contact manager that does not depend on there being an active session.