There's not really a good way to do it. In a regular system you'd do it in SQL, but Sitecore has obfuscated their 'Nexus' code which I believe is where all of this data access/mapping logic happens thus I don't really know how that part of Sitecore works so it's a difficult to try to reverse engineer a query to do this efficiently.
What I use in my projects I use a recursive function like this.
I use a delegate to determine if the item is a match for what I'm looking for, and just recurse up the tree - dropping out to return null if it reaches the root node.
public delegate bool ItemSelectionCondition(Item sitecoreItem);
public static Item GetAncestor(Item sitecoreItem, ItemSelectionCondition selectionCondition)
{
Item parent = sitecoreItem.Parent;
if (selectionCondition(parent))
{
return parent;
}
else if (Sitecore.ItemIDs.RootID == parent.ID)
{
return null;
}
else
{
return GetAncestor(parent, selectionCondition);
}
}
So to work with this you'd need to define a delgate something like:
public static bool IsTemplateX(Item item)
{
return item.TemplateID == Constants.TemplateXId;
}
It's quite a simple solution that's flexible enough to work with any Ancestor or Parent type things that I've needed to do.
Be warned though it will take several seconds to run the first time if Sitecore hasn't cached all of the items it needs to look at, but subsequent times it will take a bearable amount of time.
EDIT: Since this answer has gotten a few downvotes I thought I'd explain why it's the best way to do this in Sitecore.
The reson why a recursive approach is better that iterative is two-fold. First, it's less code so it looks nicer - but if that's not enough the second reason is that if you're careful about how you've structured your code and are compiling your project you'll be able to take advantage of tail recursion.
The reason why using Item.Axes.Ancestors
(Or Item.Axes.GetAncestors()
in later versions of Sitecore) is bad is because it has five steps:
- Allocate a List of Items
- Iteratively add All Ancestors to that list
- Reverse the order of the List
- Convert the list to an array
- Use Linq to filter the array
This means that you're doing twice as much allocation as if you were to just recurse to the top of the content tree, but you're also ruling out the possibility of exiting the recursion at the first relevant item - and avoiding unecessary calls to the database.
I do have to conceed that the comment about using a Lucene Index however is right - that's the way that is potentially the fastest in almost all scenarios just because it can reduce your time finding the item down to a single Sitecore Query - or no Sitecore Query if you index the data about that ancestor that you need.