4

I have a scenario where each page of a site has a checkbox for "is in header" and "is in footer". The universe of items that needs to be traversed is a couple of hundred in nature, and I'm looking for the most efficient way to retrieve them.

Currently, I'm using foreach loops, but there's four levels of hierarchy I need to go through, so going through the children of each is not the most efficient method and takes a few seconds per page load. I've got caching on the rendering going, but while that hides the issue, it doesn't solve it from a code perspective.

I've been looking at Sitecore Query and the like as an option as well. I know fast query won't work because the data won't maintain its hierarchy, but I also see that Sitecore Query is slow. Also, I read that using Axes.Descendants and the like is slow as well.

One thought I had was to read the entire tree into a single object and cache it with .NET caching, but then there's an issue of security rights for a particular user...you'd need to use the SecurityDisabler to get the whole universe, then when outputting it somehow compare the logged-in user to the tree and exclude anything they don't have read rights to.

The last option might work, if someone can point me in that direction for manually checking if a user has rights to an item. Beyond that, does anyone have a suggestion for running this efficiently?

9

The question is quite broad and there are many ways you can achieve this obviously (you mentioned sitecore query and foreach loop).

Another option would be to use the sitecore content search (with rendering cache on top, of course).

So, you can define your custom POCO object with IsInFooter and IsInHeader fields and query your index.

Simplified code (without declaring the context):

var items = searchContext.GetQueriable<MySearchResultItem>().Where(x=>x.IsInHeader).ToList()
7

There are only two viable options; short of simply not building up navigation structures in this manner. I would normally have any form of navigation being components of their own, and have a menu hierarchy of navigational items define what they show. But that's a separate story.

Content Search

As already demonstrated by @Andrey Bobrov. Performance wise, this is your second best option.

Link Database

But you would need to change your two Checkbox fields to one Multilist field, where you would then "tag" your items with "HeaderNavigation" and "FooterNavigation". Advantage being; you could then easily expand upon this taxonomy later, should you desire.

Once tagged like this, you could retrieve the required items using LinkDatabase, which would outperform any other option.

An example found here: Effective use of Sitecore LinkDatabase

Do not - ever...

Use Sitecore Query at runtime. That includes fast:/ query.

  • 1
    I'm definitely staying away from Sitecore Query...just trying it out in the developer center was dragging, and fast query won't return the hierarchy I'd need. The client wants a menu of this size and complexity, which I think is overkill on a per-page basis...it's more a sitemap that should be constrained to a single page. This input is great for all of us to see. – Ken McAndrew May 12 '17 at 0:06
1

I am going to approach this from a different perspective than the code channel. And focus on the performance building the footer on the server and the rendering speed.

Build your footer using the best code practices that fit into your organization optimizing as much as possible. And then you cache the output of the footer rendering using Sitecore rendering caching options.

You did mention that you might be security trimming pages based on a users permissions. In that case you can set the key for caching to be by user. When a user publishes to the CD server, the cache will be cleared and the footer will re-render.

enter image description here

  • This was actually the first place I went with my efforts. This did the job as far as performance goes, but my colleagues were concerned about the code still not being optimized, which is the end result they want. But this is definitely a good answer to the issue for some folks. – Ken McAndrew May 12 '17 at 0:04
  • Understood about code optimization. Thanks for the update. – Chris Auer May 12 '17 at 0:13
1

So I accepted an answer because I think it's the overall best one, but I wanted to share a solution I came up with. It involved keeping the foreach loops nested, but moving it to a static class that's cached with MemoryCache for an hour. I use SecurityDisabler to ensure I'm getting the entire tree and passing the values I need into my own POCO.

The trick around the security bit I used was to add a field called Security, and passing in the item's Security value. Then, when I loop through to create the output in my CSHTML file, I add a check like:

navItems.Subpages.Where(x => x.Security.CanRead(user)

Where user is the Sitecore.Context.User. This is working as I'd expect, and the speed of the page load is comparable to using the Sitecore rendering caching (cacheable and cache by user, as suggested in another answer). The one advantage is that instead of each user having to get spun up once before the rendering hits, only the first user in gets the small hit, then everyone benefits.

I'd be curious to others' thoughts on this. I know I'm describing a bit abstractly, but everyone's given some great feedback on the topic.

  • 1
    The one problem I can think of with using MemoryCache vs. the Html Caching is that, what happens if it's cached in memory and you publish new items. They won't display until the cached items expire. Now you can probably get around this by customizing the on publish event to clear your custom memory cache, or just use the Sitecore caching engine instead, which should clear on publish. I generally stick with Html Caching when I can because it doesn't require a ton of customization. – Dylan Young May 12 '17 at 1:08
  • The problem is here is that you are bypassing the already written Sitecore security, and then adding the overhead of having to clear the cache on a publish with extra code. It would have been much better to just let Sitecore take care of the security and then add the cache to the rendering with a Vary By User option - then all the other things would be taken care of and you are doing it in a standard Sitecore way that other will recognize and it will be much more maintainable in the future. It is never a good idea to use the SecurityDisabler in production code. – Richard Seal May 12 '17 at 13:07

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.