Views: 7018
Number of votes: 3
Average rating:

Upgrading vs. property return type changes in EPiServer 7

As Linus mentioned in an earlier blog post on typed models in EPiServer 7, some property types have changes that may affect your templates when upgrading to EPiServer 7. This post aims to describe these changes and how to refactor your templates to work with the new behavior of the properties.

What has changed

Some property types have new value types. That is, the type of the value returned from the PropertyData.Value property is changed. PropertyData.Value is also what is returned from the default indexer of PageData (CurrentPage[“PropertyName”] for example), so that is also effected for these property types.

The following property types are affected:

Property Type Old Value Type New Value Type
PropertyXhtmlString string EPiServer.Core.XhtmlString
PropertyXForm string EPiServer.XForms.XForm
PropertyUrl string EPiServer.Url
PropertyDocumentUrl string EPiServer.Url
PropertyImageUrl string EPiServer.Url

Effects on weakly typed property access

A concrete example: Say you define a page type with an Xhtml property called “MainBody”. In EPiServer 6 you could (even though it wasn’t recommended since you would lose for example Dynamic Content support) use statements like these in your template:

<%-- Example 1 --%>
<%= CurrentPage["MainBody"] as string %>
<%-- Example 2 --%>
<%= (string)CurrentPage["MainBody"] %>

In a new EPiServer 7 site this would not work. The first example would never generate any output because the CurrentPage[“MainBody”] statement would be of the type EPiServer.Core.XhtmlString resulting in null after the as string operation. The second example would simply throw an exception if the property is set.

 

Compatibility mode

Because the type of constructs in the above example are more or less common in sites that may be upgraded, EPiServer 7 offers a compatibility mode which reverts the change. That is, the properties continue to return strings like in EPiSErver 6 when the site runs in compatibility mode.

To use the compatibility mode, include the LegacyPropertyValueType option in the operationCompatibility attribute of your siteSettings element in episerver.config:

 

   1:  <episerver>
   2:    <sites>
   3:      <site siteId="MySite" description="Short description of the site">
   4:        <siteSettings operationCompatibility="LegacyPropertyValueType" ... />
   5:      </site>
   6:    </sites>
   7:  </episerver>

Important note 1: Compatibility mode will be set by default when upgrading a site, so your templates continue to work like before unless you change this. Update: We decided to clarify that compatibility mode should be considered experimental and not for production use. Hence compatibility mode will NOT be set by default when upgrading. You should check your weakly typed templates to remove the need for compatibility mode.

Important note 2: It is not possible to run sites in compatibility mode and use properties of the affected types in strongly typed models. That would cause ambiguities so the CMS throws an exception if you try to do this.

 

Leaving compatibility mode

If you want to start using typed models in your upgraded site you need to refactor the existing code to remove the need for compatibility mode. That is, you need to find all usages of properties of the affected types and make sure that they are compatible with the type change.

Here are two Visual Studio style regular expressions that can help you find expressions that may need to be changed. Open the solution and use Visual Studio’s Find or Find and Replace dialogs (I prefer the latter for the possibility to display all results in the Find results window rather than jumping from match to match), check “Use:” and select “Regular expressions” and use one of the following expressions to search your solution:

 

Regex (VS syntax) Example match
\[:q\]:b+as:b+string CurrentPage[“MyProp”] as string
\(string\):b*:w+\[:q\] (string)CurrentPage[“MyProp”]

 

Note 1: These regexes will of course match many things that are not property access.

Note 2: Many instances of actual property access are not problematic, only access to the affected types may require refactoring.

Note 3: You may have other property access that isn’t found by these regexes, but nontheless requires refactoring.

 

Refactoring the code

Once you’ve found the expressions that need refactoring the changes are generally simple. To make it even simpler we’ve included the GetPropertyValue extension method (with several overloads), which is probably similar to many extension methods out there. GetPropertyValue lives in the EPiServer.Core namespace, so add a using statement for that namespace to be able to use it:

 

   1:  using System;
   2:  using EPiServer.Core;
   3:   
   4:  namespace EPiServer.Templates.AlloyTech.Pages
   5:  {
   6:      public partial class Page : TemplatePage
   7:      {
   8:          protected override void OnLoad(System.EventArgs e)
   9:          {
  10:              base.OnLoad(e);
  11:   
  12:              string mainBody = CurrentPage.GetPropertyValue("MainBody");
  13:          }
  14:      }
  15:  }

 

The above example uses the simplest overload of GetPropertyValue which only takes the property name. This overload will return null if the property value is null and otherwise call ToString on the property value. If MainBody again is an Xhtml property this will render the Xhtml in the current context, taking into account view/edit mode, the current principal etcetera. For the URL property types ToString just returns the URL as a string, and for the Xform property it returns the Guid ID of the XForm.

GetPropertyValue has many overloads, here are some usage examples with comments:

 

   1:  // 1. Get as string, defaults to null and otherwise calls ToString for the value
   2:  string mainBody = CurrentPage.GetPropertyValue("MainBody");
   3:   
   4:  // 2. Specify a fallback value
   5:  string mainBodyFallback = CurrentPage.GetPropertyValue("MainBody", String.Empty);
   6:   
   7:  // 3. Which is equivalent to
   8:  string mainBodyFallback2 = CurrentPage.GetPropertyValue("MainBody") ?? String.Empty;
   9:   
  10:  // 4. So a common usage is probably
  11:  string pageHeading = CurrentPage.GetPropertyValue("PageHeading", CurrentPage.PageName);
  12:   
  13:  // 5. Get typed value, defaults to null if property is not set or defined
  14:  XhtmlString xhtml = CurrentPage.GetPropertyValue<XhtmlString>("MainBody");
  15:   
  16:  // 6. Which supports fallback in the same way as the "ToString" overload
  17:  XhtmlString xhtmlWithFallback =
  18:      CurrentPage.GetPropertyValue<XhtmlString>("MainBody", new XhtmlString());
  19:   
  20:  // 7. Advanced: Specify a conversion for the value
  21:  // Note: this example is very similar to 1. since we just call ToString, EXCEPT
  22:  // if MainBody is something else than an Xhtml property in which case 1. would still
  23:  // return the ToString representation while this would throw an exception since
  24:  // it tries to cast the value to an XhtmlString before handing it over to the
  25:  // conversion lambda
  26:  string convertedXhtml =
  27:      CurrentPage.GetPropertyValue<XhtmlString, string>("MainBody", x => x.ToString());
  28:   
  29:  // 8. This is equivalent to 1. since it treats the property value as object
  30:  string mainBody2 =
  31:      CurrentPage.GetPropertyValue<object, string>("MainBody", x => x.ToString());
  32:   
  33:  // 9. If you don't need the value but just check if it is set you don't need the extension
  34:  if (CurrentPage["SecondaryBody"] == null)
  35:  {
  36:      // Show something else instead
  37:      messageLabel.Text = "Nothing here";
  38:  }

 

Of course you can use the extension methods in the WebForms code front as well, by importing the namespace:

 

   1:  <%@ Page Language="C#" AutoEventWireup="False" CodeBehind="Page.aspx.cs"
   2:      MasterPageFile="~/Templates/AlloyTech/MasterPages/MasterPage.master"
   3:      Inherits="EPiServer.Templates.AlloyTech.Pages.Page" %>
   4:  <%@ Import namespace="EPiServer.Core" %>
   5:   
   6:  <asp:Content ContentPlaceHolderID="MainBodyRegion" runat="server">
   7:      <%= CurrentPage.GetPropertyValue("MainBody") %>
   8:  </asp:Content>
Oct 16, 2012

johan.petersson
(By johan.petersson, 10/16/2012 6:52:34 PM)

Will GetPropertyValue return Dynamic Content?

Magnus Rahl
(By Magnus Rahl, 10/16/2012 7:08:45 PM)

The basic overload which calls ToString on the XhtmlString should, yes.

eric.pettersson
(By eric.pettersson, 2/15/2013 4:53:35 PM)

Just would like to add that if you are writing to the a xhtmlstring you need to create a new string of type xhtmlstring before you can set the property.
Had trouble with that today so i thougt i should share it here as well:

var yourstring = new XhtmlString("some information");
yourPage.MainBody = youstring;

Please login to comment.