Which solution do you prefer to take a single node from the xml cache?
Hi all,
like in many projects we and you are building, we sometimes need to get for example the contact node on another page. In this way we can add a contact button which will redirect the customer to the contact page.
But you have many solutions to accomplish this and we would like to know which one fits best for solving this and has the best performance:
Solution 1:
var _contactNode = Umbraco.TypedContentAtRoot()
.DescendantsOrSelf("contact")
.FirstOrDefault();
Using the UmbracoHelper, take the IPublishedContent root node in your content structure and search for a descendant which has a document type alias of contact.
Solution 2:
var _contactNode = Model.Content.Site().Children
.Where(x => x.DocumentTypeAlias == "contact")
.FirstOrDefault();
Take the typed content of the currentpage which then wil take its root node by calling the .Site() function and then search for a child node of document type contact.
Solution 3:
var _contactNode = Umbraco
.TypedContentSingleAtXPath("//home/contact");
Using the UmbracoHelper search for a node of type contact using a xpath.
There are somethings explained what to avoid in querying.
A lot also depends on how many content you have. Solution one will do fine on a small site. But when you have 20000 content items several levels deep, it will be slow.
That said, I often have the need to query for specific nodes throughout the site (the homepage, a special "configuration" node or just some content "roots" like a "products" or "news" node under which respective content is stored). So caching those specific nodes works best for me.
I'm using strongly-typed models more often than not, so caching goes like this:
This caches a "PageNewsRoot" node in the runtime cache, for a specific language (in case the site is multilingual). It just requires any page to be passed as a parameter. I'm not using XPath (although I should!)
This method goes under a static class called "CachedNodes", so a call to this method goes, for example, like this (very simplified):
The only issue with this approach is that I have to clear the cache in case contents change. I'm using a brutal approach, just clearing the runtime cache each time a document is published or deleted, although it could be more fine-grained (only if documents of specific doctypes are altered). It goes like this:
public class SiteEvents : ApplicationEventHandler
{
protected override void ApplicationStarted(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext)
{
base.ApplicationStarted(umbracoApplication, applicationContext);
ContentService.Published += ContentService_Published;
ContentService.Deleted += ContentService_Deleted;
}
private void ContentService_Deleted(IContentService sender, DeleteEventArgs<IContent> e)
{
ClearRuntimeCache();
}
private void ContentService_Published(Umbraco.Core.Publishing.IPublishingStrategy sender, PublishEventArgs<IContent> e)
{
ClearRuntimeCache();
}
private void ClearRuntimeCache()
{
//Clear all cache for keys starting with "cachenodefor_"
ApplicationContext.Current.ApplicationCache.RuntimeCache.ClearCacheByKeySearch("cachednodefor_");
//Any other action you need
}
}
I know this might be totally out of the scope of the question, but I just wanted to mention my approach in case somebody finds it useful (or faulty, in which case please do comment!)
I'm not a fan of caching nodes. This is already cached by Umbraco itself. But if you do it you should change you cache clearing methods.
The methods you use of the content service only get executed on the server they are fired. So in a load balanced environment this will not work, because the cache will only be cleared on the server where the content is changed. All other servers will have the old version in cache.
If you want to cache than you should hook in to the cacherefreshers. This will make sure that the cache is cleared on all servers.
public class UmbracoStartup : ApplicationEventHandler
{
protected override void ApplicationStarting(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext)
{
// handle cache updated event, so we can clear cache on all servers
PageCacheRefresher.CacheUpdated += this.PageCacheRefresherCacheUpdated;
}
private void PageCacheRefresherCacheUpdated(PageCacheRefresher sender, CacheRefresherEventArgs e)
{
switch (e.MessageType)
{
case MessageType.RefreshById:
case MessageType.RemoveById:
var contentId = (int)e.MessageObject;
// do cache clearing here
break;
case MessageType.RefreshByInstance:
case MessageType.RemoveByInstance:
var content = e.MessageObject as IContent;
// do cache clearing here
break;
}
}
}
You are absolutely right. The example I gave was NOT suitable for load balanced environments, I should have mentioned that it was intended for single-server environments only. I only wanted to demonstrate the approach.
Regarding your remark about nodes being cached by Umbraco, it is true, but having to query the node tree to get a node can be VERY time consuming as I've found out myself in sites with over 10k-20k nodes. Even navigating upwards to the home page can take time. So I prefer to explicitly cache frequently used nodes in such cases.
Been doing some testing latetly..with different query options on a large multisite environment. XPath look up is fast without caching the result.
What i some times do is cache the ids of the node. That way you don't need to cache the content. The id is something that does not change when content is changed, so could be cached without clearing the cache after a publish
Ah, XPath...I miss the XSLT days where everything would come back instantly, no matter how complex the XPath query or how deep in the content structure it was.
Working with strongly-typed models is, of course, much faster (thanks to IntelliSense), but there are many pitfalls regarding performance.
An alternative option, is that you have a content picker on the home node (Or a site settings node) for selecting the contact page (or other pages that are used regularly in things like footers, headers, etc).
Then you can simply get the selected node from the home page and use Umbraco.TypedContent() to retrieve said node (as the picker stores the ID).
Which solution do you prefer to take a single node from the xml cache?
Hi all,
like in many projects we and you are building, we sometimes need to get for example the contact node on another page. In this way we can add a contact button which will redirect the customer to the contact page.
But you have many solutions to accomplish this and we would like to know which one fits best for solving this and has the best performance:
Solution 1:
Using the UmbracoHelper, take the
IPublishedContent
root node in your content structure and search for a descendant which has a document type alias ofcontact
.Solution 2:
Take the typed content of the currentpage which then wil take its root node by calling the
.Site()
function and then search for a child node of document typecontact
.Solution 3:
Using the UmbracoHelper search for a node of type
contact
using a xpath.Other suggestions?
/Michaël
Michaël,
The best solution here is the XPath. That is always the fastest option.
An alternative to solution in 1 to avoid the descendantsorselft call would be :
Have you read this page : https://our.umbraco.org/documentation/Reference/Common-Pitfalls/
There are somethings explained what to avoid in querying.
A lot also depends on how many content you have. Solution one will do fine on a small site. But when you have 20000 content items several levels deep, it will be slow.
Dave
Dave,
thanks for your input here! Normally we only have small websites that won't exceed more then 500 nodes.
Will read the common pitfalls which we haven't read before, looks very interesting.
/Michaël
@Dave,
one more question or opinion, when using ModelsBuilder do you convert your result to the correct class like for example:
So that you can use the properties of that node instead of using
.GetPropertyValue()
? Or will this have an impact on the performance?/Michaël
Hi Michaël,
You can do this
That would return them in the correct type. And also filter if you have differrent types as children
Dave
Dave,
something new learned today! Didn't know about the
.OfType<>
possibility!/Michaël
I think this is also valid
I agree with Dave. XPath is the fastest option.
That said, I often have the need to query for specific nodes throughout the site (the homepage, a special "configuration" node or just some content "roots" like a "products" or "news" node under which respective content is stored). So caching those specific nodes works best for me.
I'm using strongly-typed models more often than not, so caching goes like this:
This caches a "PageNewsRoot" node in the runtime cache, for a specific language (in case the site is multilingual). It just requires any page to be passed as a parameter. I'm not using XPath (although I should!)
This method goes under a static class called "CachedNodes", so a call to this method goes, for example, like this (very simplified):
The only issue with this approach is that I have to clear the cache in case contents change. I'm using a brutal approach, just clearing the runtime cache each time a document is published or deleted, although it could be more fine-grained (only if documents of specific doctypes are altered). It goes like this:
I know this might be totally out of the scope of the question, but I just wanted to mention my approach in case somebody finds it useful (or faulty, in which case please do comment!)
Hi Sotiris,
I'm not a fan of caching nodes. This is already cached by Umbraco itself. But if you do it you should change you cache clearing methods.
The methods you use of the content service only get executed on the server they are fired. So in a load balanced environment this will not work, because the cache will only be cleared on the server where the content is changed. All other servers will have the old version in cache.
If you want to cache than you should hook in to the cacherefreshers. This will make sure that the cache is cleared on all servers.
Dave
You are absolutely right. The example I gave was NOT suitable for load balanced environments, I should have mentioned that it was intended for single-server environments only. I only wanted to demonstrate the approach.
Regarding your remark about nodes being cached by Umbraco, it is true, but having to query the node tree to get a node can be VERY time consuming as I've found out myself in sites with over 10k-20k nodes. Even navigating upwards to the home page can take time. So I prefer to explicitly cache frequently used nodes in such cases.
Hi Sotiris,
Been doing some testing latetly..with different query options on a large multisite environment. XPath look up is fast without caching the result.
What i some times do is cache the ids of the node. That way you don't need to cache the content. The id is something that does not change when content is changed, so could be cached without clearing the cache after a publish
Dave
Caching the id is a very good idea indeed.
Ah, XPath...I miss the XSLT days where everything would come back instantly, no matter how complex the XPath query or how deep in the content structure it was.
Working with strongly-typed models is, of course, much faster (thanks to IntelliSense), but there are many pitfalls regarding performance.
Hi Sotiris,
For really expensive queries you could always use a XPathNavigator to work on the xml directly.
See here : https://our.umbraco.org/documentation/Reference/Common-Pitfalls/#xpathnodeiterator-for-when-you-need-direct-xml-support
Dave
An alternative option, is that you have a content picker on the home node (Or a site settings node) for selecting the contact page (or other pages that are used regularly in things like footers, headers, etc).
Then you can simply get the selected node from the home page and use Umbraco.TypedContent() to retrieve said node (as the picker stores the ID).
is working on a reply...
This forum is in read-only mode while we transition to the new forum.
You can continue this topic on the new forum by tapping the "Continue discussion" link below.
Continue discussion