Get child pages filtered by page type

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>();

An extension method for getting child pages filtered by type

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>();
    }
}

Using FindPagesWithCriteria

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.

Comments

  1. Henrik Nystrom's avatar

    Henrik Nystrom 9 months ago

    You can also use the OfType LINQ extension method:

    IEnumerable<ArticlePage>  children = DataFactory.Instance.GetChildren(pageLink).OfType<ArticlePage>();
    
  2. Joel Abrahamsson's avatar

    Joel Abrahamsson 9 months ago

    Indeed! I can't believe I haven't noticed that method before.

  3. Henrik Nystrom's avatar

    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!

  4. Ted Nyberg's avatar

    Ted Nyberg 8 months ago

    Great post, Joel! And great tips, Henrik! I had missed that LINQ extension, too! :)

Add a comment

Allowed tags: <b>, <em>, <quote cite="">, <code>, <c-sharp-code>, <css-code>, <sql-code>, <xml-code>, <javascript-code>. If you want to display code examples, please remember to write &lt; for < and &gt; for >.

Follow me on Twitter

  1. Bookmarked: Google Scribe http://bit.ly/9iDp8s 2 days ago
  2. Blogged: The future of EPiMVC http://bit.ly/ck6EPg #episerver 2 days ago
  3. At Skånegläntan, as always #parentalleave 2 days ago
follow me

Latest comments

  1. Svante wrote "Yes, I noticed that it was a singleton, and I guess the real..." on Something to beware of when using EPiAbstractions and an IoC container
  2. Joel Abrahamsson wrote "Well, first of all you wont get any arguments from me regard..." on Something to beware of when using EPiAbstractions and an IoC container
  3. Svante wrote "Hmm... Since the issue really is with the public instance co..." on Something to beware of when using EPiAbstractions and an IoC container

About this site

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