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 2 years 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 2 years ago

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

  3. Henrik Nystrom's avatar

    Henrik Nystrom 2 years 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 2 years ago

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

  5. goldUniolidup's avatar

    goldUniolidup 8 months ago

    так, напэўна так і ёсць

  6. Patrik Berglund's avatar

    Patrik Berglund 5 months ago

    Any idea of what could be wrong if the criteria search above says it can't cast pages of type PageData to the PageTypeBuilder subclass' type?

  7. Joel Abrahamsson's avatar

    Joel Abrahamsson 5 months ago

    Yeah, it's due to a bug introduced in R2 where the GetPages method (used by FPWC) doesn't respect changes made by event handlers. If you're using PTB 1.3.1 you can use the AsTyped() extension method on the result as a work around.

  8. Patrik Berglund's avatar

    Patrik Berglund 5 months ago

    Thanks!

    Unfortunately, a lot of projects here is dependent on our internal project which contains the method using the criteria search, so I'll have to wait upgrading from 1.3.0 until the work load decreases.

    I created a work-around using PageTypeResolver.Instance.ConvertToTyped() which appears to work nicely :)
    ...Which is apparently what happens in AsTyped() too... :)

Follow me on Twitter

  1. @chraas You might want to use it for complex parts of the site and not use it for simple rendering pages. 2 days ago
  2. @chraas In theory more SOLID, but I'm not sure it's worth the price. 2 days ago
  3. @chraas Be warned that you're introducing one big chunk of complexity with EPiMVP :) 2 days ago
follow me

Latest comments

  1. Berra S wrote "Read your post at http://joelabrahamsson.com/entry/using-xfo..." on PageData objects not returned as typed when using Page Type Builder and FindPagesWithCriteria
  2. Linus wrote "1 up for behaviour being as close as expected as possible!" on A common problem with Page Type Builder and UniqueValuePerLanguage set to false
  3. Joel Abrahamsson wrote "Hi Hans, Could it be that you previously didn't have Page..." on Page Type Builder 2.0 released

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