Try our conversational search powered by Generative AI!

Lee Crowe
Jan 26, 2011
  6730
(0 votes)

EPiServer null property values for saved but unpublished pages


Introduction

Recently I have been working on an EPiServer 6 build.  One of the requirements for this build to manage time table data.

I am not going to go into the full requirements but we are using PageTypeBuilder for our entity objects and using the EPiServer page tree as a content modelling repository for our entities.  Please see Mark Rodseth’s blog post detailing this approach here.

One of the reasons for the approach is to cut development time as all of the entities can be managed through EPiServer using PageTypeBuilder and the edit mode editing interface.

The content repository we are developing is in a complex structure and will more than likely confuse the editors.

To solve this issue I have been developing an edit mode plugin that will provide a user friendly interface to populate all of the relevant relational page tree entities.

Does this make sense so far? Excellent Smile


The Problem

The plugin I have created allows the user to create pages in a saved state and then publish them in batches when they are happy.

The plugin also contains many drop downs lists and list views which are populated from various areas of the content repository.

Are you still with me? Now I will try and get to the point Disappointed smile

I am using DataFactory.Instance.FindPagesWithCriteria calls to get entities of different page types and criterias.

The issue I have is that I have pages in the page tree that are in a saved state and have also never been published.

The FindPagesWithCriteria call returns the saved pages in a PageDataCollection object which is exactly what I want to happen, Yay.

The issue is that all non meta data properties have null values even though the page has these properties set Sad smile.

After some digging around I figured out the problem is caused because the PageLink (PageReference) property of the PageData object has no WorkID specified (remember the page is in a saved state and has never been published)

I haven’t bothered to reflect this in great detail but I would assume that as no WorkID is specified the code is following a different logical route.

I am sure an EPiServer employee could provide detailed information on the reasoning for this Smile, but to be honest I can’t be bothered to look into the inner workings.

Solution

I am sure this issue has been found and solved many times before but I thought I would blog about it just in case any developers were not aware of this GOTCHA.

To resolve the issue I have created some extension methods for the DataFactory class which will force a PageData object to be inline with the latest version.

Extension methods are below and are referencing PageTypeBuilder types.  If you are not using PageTypeBuilder ask yourself WHY as you should be Winking smile

   1:  public static List<T> GetLatestPages<T>(this DataFactory dataFactory, List<T> pages) where T : TypedPageData
   2:  {
   3:      if (pages.Count == 0)
   4:          return pages;
   5:   
   6:      const double batchSize = 100;
   7:      List<T> pagesToProcess = pages.Where(current => current.PageLink.WorkID == 0).ToList();
   8:   
   9:      foreach (string languageId in pagesToProcess.Select(current => current.LanguageID).Distinct())
  10:      {
  11:          List<T> pagesToBatch = pagesToProcess.Where(current => string.Equals(current.LanguageID, languageId, StringComparison.OrdinalIgnoreCase)).ToList();
  12:          int numberOfBatches = (int)Math.Ceiling(pagesToBatch.Count / batchSize);
  13:   
  14:          for (int i = 0; i < numberOfBatches; i++)
  15:          {
  16:              int start = (int)(i * batchSize);
  17:              List<T> batch = pagesToBatch.Skip(start).Take((int)batchSize).ToList();
  18:              Dictionary<int, int> pagesAndWorkIds = dataFactory.GetLatestPageWorkIds(batch);
  19:   
  20:              foreach (var item in pagesAndWorkIds)
  21:              {
  22:                  var currentItem = item;
  23:                  int index = pages.FindIndex(current => current.PageLink.ID == currentItem.Key);
  24:                  pages[index] = DataFactory.Instance.GetPage(new PageReference(currentItem.Key, currentItem.Value)) as T;
  25:              }
  26:          }
  27:      }
  28:   
  29:      return pages;
  30:  }
  31:   
  32:  public static T GetLatestPage<T>(this DataFactory dataFactory, T page) where T : TypedPageData
  33:  {
  34:      List<T> pages = GetLatestPages(dataFactory, new List<T> { page });
  35:      return pages[0];
  36:  }
  37:   
  38:  private static Dictionary<int, int> GetLatestPageWorkIds<T>(this DataFactory dataFactory, List<T> pages) where T : TypedPageData
  39:  {
  40:      int languageBranchId = LanguageBranch.ListEnabled().Where(current => string.Equals(current.LanguageID, pages[0].LanguageID)).FirstOrDefault().ID;
  41:   
  42:      StringBuilder sqlBuilder = new StringBuilder();
  43:      sqlBuilder.AppendFormat("SELECT [fkPageID], MAX([pkID]) as WorkId FROM [tblWorkPage] WHERE [fkLanguageBranchID] = {0} AND [fkPageID] IN (", languageBranchId);
  44:   
  45:      for (int i = 0; i < pages.Count; i++)
  46:      {
  47:          string seperator = (i < (pages.Count - 1)) ? "," : string.Empty;
  48:          sqlBuilder.AppendFormat("{0}{1}", pages[i].PageLink.ID, seperator);
  49:      }
  50:   
  51:      sqlBuilder.Append(") GROUP BY [fkPageID]");
  52:   
  53:      Dictionary<int, int> pagesAndWorkIds = new Dictionary<int, int>();
  54:   
  55:      using (SqlConnection connection = new SqlConnection(ConfigurationManager.ConnectionStrings["EPiServerDB"].ConnectionString))
  56:      {
  57:          using (SqlCommand command = new SqlCommand(sqlBuilder.ToString(), connection))
  58:          {
  59:              connection.Open();
  60:   
  61:              using (SqlDataReader reader = command.ExecuteReader())
  62:              {
  63:                  while (reader.Read())
  64:                      pagesAndWorkIds.Add(reader.GetInt32(0), reader.GetInt32(1));
  65:              }
  66:          }
  67:      }
  68:   
  69:      return pagesAndWorkIds;
  70:  }


The project I am working on has some helper classes to perform various different criteria based searching, some using FindPagesWithCriteria solely and others using a mix of FindPagesWithCriteria and LINQ.

These classes are being used in the live view of the site and in edit mode.  In the live view of the site everything works as expected as the user will only be able to view Published pages. 

Within my plugin I am having to determine whether the user is in edit mode and if so call the extension methods to make sure the PageData objects are populated correctly.

An example of calling one of the extension methods is below:

   1:  List<Timetable> timeTables = DataFactory.Instance.FindPagesWithCriteria(PageReference.StartPage, criterias).Cast<Timetable>().ToList();
   2:   
   3:  if (Functions.InEditMode)
   4:      DataFactory.Instance.GetLatestPages(timeTables);

 

Conclusion

Initially I wasn’t sure whether the access rights rules that are applied in all of the various PageData retrieval methods were causing the issues but that doesn’t seem to be the case.

I then started to think this may be a bug, but I am starting to think this is possibly by design and it is just the case that we are using EPiServer in a way that it have not been designed for.

Are we the only agency to suffer this problem or have any other agencies used a content repository entity approach with custom administration interfaces?  And if so have you suffered the same problems and created alternative solutions?

Thanks for reading this far, my blog style is not the greatest but I hope I have explained myself well enough and you have understood the point of my post. 

There was some serious hand banging the other day troubleshooting the issue.

Disclaimer

All code is provided as is and I accept no responsibility if it doesn’t work as you would expect.  You should always test your implementations yourself Winking smile

Jan 26, 2011

Comments

Please login to comment.
Latest blogs
Azure AI Language – Abstractive Summarisation in Optimizely CMS

In this article, I show how the abstraction summarisation feature provided by the Azure AI Language platform, can be used within Optimizely CMS to...

Anil Patel | Apr 18, 2024 | Syndicated blog

Fix your Search & Navigation (Find) indexing job, please

Once upon a time, a colleague asked me to look into a customer database with weird spikes in database log usage. (You might start to wonder why I a...

Quan Mai | Apr 17, 2024 | Syndicated blog

The A/A Test: What You Need to Know

Sure, we all know what an A/B test can do. But what is an A/A test? How is it different? With an A/B test, we know that we can take a webpage (our...

Lindsey Rogers | Apr 15, 2024

.Net Core Timezone ID's Windows vs Linux

Hey all, First post here and I would like to talk about Timezone ID's and How Windows and Linux systems use different IDs. We currently run a .NET...

sheider | Apr 15, 2024