Anders Hattestad
Feb 10, 2011
visibility 7418
star star star star star
(0 votes)

All pages available using Linq and DDS

I have been playing with some code that saves every published page into a Dynamic Data Store table. This is because I want to use Linq against that table and retrieve EPiServer pages.

I know there is a Querying EPiServer PageData using LINQ but I wanted to try out the DDS and check what kind of performance it gave.

I assume that all properties with the same name is the same property. If you are using a strong typed page type this shouldn't be a problem.

What I do first is to loop over all page types and make a store definition based on all properties those page types have. This I do on first access against the Store. and is done by providing a Dictionary<string, Type> with name and type.

Code Snippet
  1. if (storeDef == null)
  2.     DynamicDataStoreFactory.Instance.CreateStore(StoreName, storeBagType);
  3. else
  4. {
  5.     if (added)
  6.     {
  7.         storeDef.Remap(storeBagType);
  8.         storeDef.CommitChanges();
  9.     }
  10. }
Then I made myself a method that looped over all pages and added them to the store. By using a property bag with a name and value. It’s not necessary to have all the stores fields in the bag.

image

Then it was time for using the datastore. One cool thing about the DDS is that even you have created a store with string,type dictionary you can access it with a class. The only thing you need is that the propertynames are the same.

So i made myself a class that contains those properties

image and can use that to create strong typed selects from the store.

Code Snippet
  1. public class AllPagesStore<T> where T : MinimumPageData
  2. {
  3.     public static string StoreName { get { return "Itera.AllPagesStore.AllPages"; } }
  4.     public static IOrderedQueryable<T> AllPages()
  5.     {
  6.         return DynamicDataStoreFactory.Instance.GetStore(StoreName).Items<T>();
  7.     }

and then I made a class that inherits from my typed store method class with the EPiServer built in properties with Page prefix names

Code Snippet
  1. public class AllPagesStore : AllPagesStore<MinimumPageData>
  2. {
  3.     public static DynamicDataStore Store
  4.     {
  5.         get
  6.         {
  7.             return DynamicDataStoreFactory.Instance.GetStore(StoreName);
  8.         }
  9.     }

There is no ACL checks here, but the helper method that converts a query to a PageDataCollection can do that

Code Snippet
  1. public static PageDataCollection GetPages(IQueryable<T> query, int maxCount,AccessLevel access)
  2. {
  3.     var pages = new PageDataCollection();
  4.     foreach (var row in query)
  5.     {
  6.         var rowData = row.ToPropertyBag();
  7.         var page = EPiServer.DataFactory.Instance.GetPage(
  8.                 PageReference.Parse((string)rowData["PageLink"]),
  9.                 new LanguageSelector((string)rowData["PageLanguageBranch"])
  10.                 );
  11.         if (page != null && page.QueryDistinctAccess(access))
  12.         {
  13.             pages.Add(page);
  14.             if (pages.Count >= maxCount)
  15.                 break;
  16.         }
  17.     }
  18.     return pages;
  19. }

and even only return those pages we are interested in. Which is a huge problem with FindPagesWithCriteria, In the foreach here there will be executed a database call to retrieve the row from the database. I’m not particular happy with this implementation of the DDS. I would like to have a method to retrieve a given number of rows in one go. Paul Smith and I have agreed to disagree on this issue thou.

I first tried to have one store for each language and using the PageGuid as the guid in the store. That actually worked, but got a bit messy since you needed to know what store you wanted to access.

When I save to pages into the store I find the parents path and add a , before and after the PageCategory. this is to more easy do query’s against categories.

This means that we can do all kind of Linq searches

Code Snippet
  1. var query = from page in AllPagesStore.AllPages()
  2.             where
  3.                 page.PageTypeID == pageTypeID &&
  4.                 page.PageParentLinks.Contains("|" + startSearchLink + "|")
  5.             select page;
  6.  
  7. var query2 = from page in AllPagesStore.AllPages()
  8.              where
  9.                   (
  10.                       page.PageCategory.Contains(",1,")
  11.                       ||
  12.                       page.PageCategory.Contains(",2,")
  13.                   )
  14.                   &&
  15.                   (
  16.                       page.PageTypeID == "3"
  17.                       ||
  18.                       page.PageTypeID == "4"
  19.                   )
  20.              orderby page.PageStartPublish descending
  21.              select page;

 

Have uploaded some files in the code section, and as you may see this is work in progress code :)

AttachEvents.cs

Will update the big table with new values when a page is published or moved/deleted

Status.aspx

Is a admin plugin to start indexing all pages and add them to the big table. this will also show all properties that are available, and show those properties and pagetype where there are indifference.

AllPagesStore.cs

Is the class where you can access the store

MinimumPageData.cs

is a class with all the Page properties

Code here

Have not gotten around to test performance yet, but the ability to query against all fields and only retrieve the 5 latest should outperform the FindPagesWithCriteria. at least when we are looking at a big record set.

I think that EPiServer CMS6 should have something like this build in, and I think there is to long for some of us to wait for CMS7 that are promised Linq support on the pages.

Feb 10, 2011

Comments

Deane Barker
Deane Barker Feb 10, 2011 03:22 PM

Gosh, one of those examples seems so familiar... :-)

We work with a lot of corporate clients, and they're still very comfortable with SQL. I've often thought about doing a page-to-DB-table mapping, so pages are dropped into flat SQL tables with strongly typed columns for each property.

It's not nearly as extensible and cool as this, but for corporate developers with a strong preference for SQL, it would play really well and likely help us in sales situations. To be able to tell a CIO that his developers can query the repo with normal SQL would be a great selling point.

Anders Hattestad
Anders Hattestad Feb 10, 2011 04:06 PM

I copied it of the forum post :)

Anders Hattestad
Anders Hattestad Feb 11, 2011 08:33 AM

Good point Deane
Had not thought about that this code makes it easy to select pages from a normal query, to use in other system.

error Please login to comment.
Latest blogs
Optimizely Commerce 14 Now Supports .NET 10

In my previous post, Optimizely CMS 12 Now Fully Supports .NET 10 , I noted one remaining limitation: Commerce 14 (Commerce Connect) did not yet...

Bien Nguyen | Aug 7, 2026

The Top 10 Things I'm Actually Using AI For

A while back I wrote about ReviewPR, an Azure Function that uses AI to review Azure DevOps pull requests. That was one specific use case, but over...

KennyG | Aug 5, 2026

Drag-and-Drop Reordering for Commerce Media Collection in Optimizely Commerce Connect

Optimizely Commerce Connect ships a polished asset editor for the CommerceMediaCollection  property on catalog entries. It lets editors add, remove...

Linh Doan Cuu | Aug 5, 2026

Order tabs with drag and drop V2

I earlier did a very simple Blazor version to be able to sort tabs with drag and drop. I wanted to update it a bit and also display how the tabs ar...

Per Nergård (MVP) | Aug 4, 2026

Optimizely : Missing Language Manager Gadget Fix in CMS 12 After Upgrading to .NET 10

Recently, while upgrading an Optimizely CMS solution to .NET 10, we came across an issue where the Language Manager gadget completely disappeared...

Madhu | Aug 2, 2026 |

Flat or Nested? Weighing Your Content Modeling Options in Optimizely SaaS CMS

When building a headless site on Optimizely SaaS CMS, one of the earliest and most critical design milestones your team will face is defining your...

Vipin Banka | Jul 31, 2026