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 2 years ago
You can also use the OfType LINQ extension method:
Joel Abrahamsson 2 years ago
Indeed! I can't believe I haven't noticed that method before.
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!
Ted Nyberg 2 years ago
Great post, Joel! And great tips, Henrik! I had missed that LINQ extension, too! :)
goldUniolidup 8 months ago
так, напэўна так і ёсць
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?
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.
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... :)