Best way to calculate LEVEL or DEPTH of a Page in the structure.

Vote:
 
Hi folks, have a project with HUGE performance issues and have narrowed it down to code written to determine the level of a page from the root. This calculation has to occur VERY frequently, due to the unfortunate dynamic nature of the content being retrieved. Below is our code written which takes WAY too long to execute. private static int Level(PageData aPage) { int iLevel = 0; if (aPage != null) { PageReference parentReference = aPage.ParentLink; while (parentReference != PageReference.EmptyReference && parentReference != Global.EPConfig.RootPage) { parentReference = Global.EPDataFactory.GetPage(parentReference).ParentLink; iLevel++; } } return iLevel; } So - Help please! How can I write this code better - or should I go and write a SQL Stored proc to do it for me quicker? Thanks folks - love your work. Cameron
#12685
Mar 25, 2008 18:35
Vote:
 
Hi! Two possible solutions. Update a hidden property (Number) of each page and calculate it's depth every time the page is saved. Or you can atleast cache the results for some time for each page. If you are using this code in a list, it's maybe better to use a PageTree (or PageTreeData) wich already have a Indent-value. Hope this helps. Regards, Micke
#14740
Mar 25, 2008 18:45
Vote:
 
Actually, it is not the PageTree/PageTreeData (or even the PageTemplateContainer) controls that offer the Indent value - it exists in the PageData class. If you still want your call to a "Level" method, it should be possible to implement it such as: private static int Level(PageData aPage) { return aPage.Indent; } Have you tried using that, and failed?
#14741
Mar 25, 2008 18:45
Vote:
 
The Indent property is a calculated value, and it is 0 for any page you get from GetPage(...). I'm not 100% sure, but I believe the PageTree updates the value on the fly. Try this in you global.asax.c: protected void Application_Start(Object sender, EventArgs e) { Global.EPDataFactory.LoadedPage += new EPiServer.PageEventHandler(EPDataFactory_LoadedPage); } void EPDataFactory_LoadedPage(object sender, EPiServer.PageEventArgs e) { // Find and cache the indent if (HttpContext.Current != null && e.Page != null) { int pageIndent = 0; string key = "IndentCache_" + e.Page.PageLink.ID.ToString(); object cachedIndent = HttpContext.Current.Cache[key]; if (cachedIndent != null) { System.Diagnostics.Debug.Write("Indent from cache"); pageIndent = (int)cachedIndent; } else { // Find and store indent pageIndent = Level(e.Page); // Store in cache, with dependency to the global // EPiServer cache, to handle moving pages etc. StorePageIndentInCache(key, pageIndent); System.Diagnostics.Debug.Write("Indent stored in cache"); } e.Page.Indent = pageIndent; } } private int Level(PageData aPage) { int iLevel = 0; if (aPage != null) { PageReference parentReference = aPage.ParentLink; while (parentReference != PageReference.EmptyReference && parentReference != Global.EPConfig.RootPage) { parentReference = Global.EPDataFactory.GetPage(parentReference).ParentLink; iLevel++; } } return iLevel; } private const string KEY_CACHE_PAGEINDENT_DISABLE = "EPfDisableCaching_PageIndent"; /// /// Adds the PageIndent to the cache, with a dependency to the global EPiServer /// page cache. /// private static void StorePageIndentInCache(string uniqueCacheKey, int pageIndent) { // Check if caching is disabled - can be for debugging or troubleshooting // Because of the "EPf" prefix, EPiServer will convert this // to a bool. Returns true if value is true, if not, returns false. // Also returns false if the key does not exist, or there is some // error parsing it as a bool. if ((bool)Global.EPConfig[KEY_CACHE_PAGEINDENT_DISABLE] == true) return; // Make the cache dependent on the EPiServer cache, so we'll remove this // when new pages are published, pages are deleted or we are notified by // another server that the cache needs refreshing String[] pageCacheDependencyKey = new String[1]; pageCacheDependencyKey[0] = DataFactoryCache.VersionKey; CacheDependency dependency = new CacheDependency(null, pageCacheDependencyKey); // Add to cache, without dependencies and expiration policies // If the cached item should be cached for limited time (regardless of // the cache dependency), add an absolute expiration date or a // sliding expiration to the item. // Also note, we use the Insert method that will overwrite any existing // cache item with the same key. The Add method will throw an exception // if an item with the same key exists. HttpContext.Current.Cache.Insert(uniqueCacheKey, pageIndent, dependency); } You can monitor the effect of this by using DebugView from SysInternals. It takes a little time, but after a while you should only see lines like this: "Indent from cache" /Steve
#14742
Mar 25, 2008 18:45
Vote:
 
Thanks Steve. This worked a treat - I was able to optimise even further, as I was only getting the PageData object so I could calculate the depth. As I always had the PageReference, I ended up just hitting the cache directly for this info. Heaps faster now! Cheers, Cameron
#14743
Mar 25, 2008 18:45
* You are NOT allowed to include any hyperlinks in the post because your account hasn't associated to your company. User profile should be updated.