I received an e-mail today asking how you can get all children of a EPiServer CMS page that are of a specific page type when you use Page Type Builder. Since this is a very common scenario I thought I’d post my reply here.
When you call DataFactory.Instance.GetChildren() with a page’s PageReference as a parameter the method will return a PageDataCollection with all children of the page. Normally all of the PageData objects in the returned collection are of type PageData. However, when you use Page Type Builder their underlying type is actually a subtype of PageData. That is, if you know that all of the returned children are of type ArticlePage you can do this:
IEnumerable<ArticlePage> children = DataFactory.Instance.GetChildren(pageLink).Cast<ArticlePage>();
If on the other hand you aren’t sure that all children is of the type you can filter out those who aren’t by using the Where LINQ extension method.
IEnumerable<ArticlePage> children =
DataFactory.Instance.GetChildren(pageLink)
.Where(child => child.GetType() == typeof(ArticlePage))
.Cast<ArticlePage>();
The above code will produce a set of pages whose page types are exactly ArticlePage. Often though you would also want to include pages whose page type are either ArticlePage or a descendant of ArticlePage. That’s accomplished by the below code, which also looks more elegant thanks to the is operator.
IEnumerable<ArticlePage> children =
DataFactory.Instance.GetChildren(pageLink)
.Where(child => child is ArticlePage)
.Cast<ArticlePage>();
If we use this type of filtering often we could create a nice little extension method for DataFactory that does it for us.
using System.Collections.Generic;
using System.Linq;
using EPiServer;
using EPiServer.Core;
using PageTypeBuilder;
public static class DataFactoryExtensions
{
public static IEnumerable<T> GetChildrenOfType<T>(
this DataFactory dataFactory, PageReference pageLink)
where T : TypedPageData
{
return dataFactory.GetChildren(pageLink)
.Where(child => child is T)
.Cast<T>();
}
}
The above solution does all filtering in memory. That’s usually fine performance wise, but there are situations where a page has thousands of children and it’s better to do the filtering in the database. In that case we can use the PageTypeResolver class to figure out a page type’s ID and then use FindPagesWithCriteria. The code below will produce the same result as the second code example except that it will include all descendants and not just children (add a critieria for PageParentLink if you just want children).
//Figure out the corresponding ID for the
//page type that matches the ArticlePage type
int pageTypeID = PageTypeResolver.Instance.GetPageTypeID(typeof(ArticlePage)).Value;
PropertyCriteria pageTypeCriteria = new PropertyCriteria();
pageTypeCriteria.Condition = EPiServer.Filters.CompareCondition.Equal;
pageTypeCriteria.Name = "PageTypeID";
pageTypeCriteria.Type = PropertyDataType.PageType;
pageTypeCriteria.Value = pageTypeID.ToString();
PropertyCriteriaCollection criterias = new PropertyCriteriaCollection();
criterias.Add(pageTypeCriteria);
IEnumerable<ArticlePage> descendants =
DataFactory.Instance.FindPagesWithCriteria(pageLink, criterias)
.Cast<ArticlePage>();
PS. For updates about new posts, sites I find useful and the occasional rant you can follow me on Twitter. You are also most welcome to subscribe to the RSS-feed.
This blog is built with EPiServer Community, EPiServer CMS, ASP.NET MVC and a bunch of other great products. The source code is available for download at the projects page, where you also can read more about this site and my other projects.
read more
Comments
Henrik Nystrom 9 months ago
You can also use the OfType LINQ extension method:
Joel Abrahamsson 9 months ago
Indeed! I can't believe I haven't noticed that method before.
Henrik Nystrom 9 months ago
Well, it's easy to miss, there is quite a lot of them... :)
Another LINQ extension that is heavily under-used is IEnumerable.Any(). Everyone is so used to using the ICollection.Count property that they use IEnumerable.Count() without realizing that there is a much better method to use instead!
Ted Nyberg 8 months ago
Great post, Joel! And great tips, Henrik! I had missed that LINQ extension, too! :)