Loading...
Area: Optimizely CMS
Applies to versions: 10 and higher
Other versions:

Properties

Recommended reading 
Note: This documentation is for the preview version of the upcoming release of CMS 12/Commerce 14/Search & Navigation 14. Features included here might not be complete, and might be changed before becoming available in the public release. This documentation is provided for evaluation purposes only.

This topic explains how to access and use properties in Optimizely CMS. Properties store and present data for content such as pages and blocks. You can store properties for a single content item or as a dynamic property that can be inherited.

In this topic

How it works

When you define a property on a content type, CMS stores the property as a PropertyDefinition class and backs runtime instances of the property type by types inheriting from PropertyData. These classes deal with the data inside the property, such as protecting read-only state, preparing data from saving and loading, declaring value type, and so on. PropertyData classes also can provide a default user interface.

Accessing a property on a read-only page

Optimizely CMS returns content items in read-only mode from the API. The following code shows how to access a property that resides on the StandardPage property on PageData:

public class StandardPageController : PageControllerBase<StandardPage>
    {
        public ActionResult Index(StandardPage currentPage)
        {
           Object propertyValue = currentPage["PropertyName"];
           // do something else and return view
        }
    }

The accessor determines whether the property with the name PropertyName is a property on the page by checking Property defined on IContentData implemented by PageData (which inherits PageController). It returns the property value, if one exists. If the property is not on the page, a delegate is called to see if the property is found elsewhere. The default delegate looks for the property from the fetch data from function (if activated on the current page). The delegate checks the dynamic properties for the page. You can write your own delegate and change this behavior.

Accessing a property on a write-enabled page

If the page you are working with is write-enabled, the Property accessor returns properties that are defined on the page; you never get dynamic properties or fetch data from properties when the page is in write mode.

Accessing properties on strongly typed content

When you access properties on a strongly typed page, work with that page as you work with an ordinary .NET object. Given a strongly typed page with the following signature:

[ContentType]
public class StandardPage: SitePageData
{
  public virtual XhtmlString MainBody { get; set; }
}

Note: Why is the property declared as virtual? In the background, a proxy class is created for the page type, where the typed property on your class is connected to the backing PropertyData instance. This only works if properties are declared as virtual otherwise they cant be overridden. If the properties are not declared virtual, you need to implement get/set so they read/write data to the underlying property collection instead.

If you are using the generic EPiServer.Web.Mvc in your page inheritance, you can access the MainBody property like this:

public class StandardPageController : PageController<StandardPage>
{
  public ActionResult Index(StandardPage currentPage)
  {
    var mainBody = currentPage.MainBody;
    // do something else and return view
  }
}

Extension methods for strongly typed content

Use the GetPropertyValue and SetPropertyValue extension methods to take advantages of strongly typed models and also override the get and set behavior for the property. Add a using statement referencing the EPiServer.Core namespace to your class to use these extension methods.

The following example shows a strongly typed content model that has a fallback behavior for its PageHeading property. If the editor sets the PageHeading page property, the PageHeading property on the model returns the set value. If the editor does not set it, the property on the model returns the PageName property instead:

[ContentType]
public class StandardPage: SitePageData
{
  public virtual string PageHeading
  {
    get
    {
      var heading = this.GetPropertyValue(p => p.PageHeading);
 
      // If heading is not set, fall back to PageName
      return String.IsNullOrWhiteSpace(heading) ? PageName : heading;
    }
    set { this.SetPropertyValue(p => p.PageHeading, value); }
  }
}

Accessing properties on non-strongly typed content

If a property is unknown or if you want to access several properties (for example in a loop), you can access the value directly through its object representation.

public ActionResult Index(StandardPage currentPage)
  {
    object propertyValue = currentPage["PropertyName"];
    // do something else and return view
  }

If you know the type of your value, you can cast it directly.

public ActionResult Index(StandardPage currentPage)
  {
    string propertyValueString = (string)currentPage["PropertyName"];
    // do something else and return view
  }

If you are unsure of the actual type and want to avoid an exception, assign the property value with the as keyword:

public ActionResult Index(StandardPage currentPage)
  {
    string propertyValueAsString = currentPage["PropertyName"] as string;
    if (!String.IsNullOrEmpty(propertyValueAsString))
    {
       DoSomething();
    }
    // do something else and return view
  }

Do not assume the value type for a property type. For example, values of PropertyXhtmlString are of type XhtmlString, not string. So casting the value of a PropertyXhtmlString to string causes an exception, and using the as string operation yields null.

Using the GetPropertyValue extension method for non-strongly typed content

The EPiServer.Core namespace has extension methods for ContentData. If you import the EPiServer.Core namespace (for example with a using statement in code behind), it extends ContentData with the GetPropertyValue method, which can simplify property access by reducing the amount of code. For example, use GetPropertyValue to get the string representation of a property without first checking against null, or to provide a default value if the property is null.

The following usage examples show different overloads of GetPropertyValue:

public ActionResult Index(StandardPage currentPage)
   {
       // 1. Get as string, defaults to null and otherwise calls ToString for the value
       string mainBody = currentPage.GetPropertyValue("MainBody");

       // 2. Specify a fallback value
       string mainBodyFallback = currentPage.GetPropertyValue("MainBody", string.Empty);

       // 3. Which is equivalent to
       string mainBodyFallback2 = currentPage.GetPropertyValue("MainBody") ?? string.Empty;

       // 4. So a common usage is probably
       string pageHeading = currentPage.GetPropertyValue("PageHeading", currentPage.PageName);

       // 5. Get typed value, defaults to null if property is not set or defined
       XhtmlString xhtml = currentPage.GetPropertyValue<XhtmlString>("MainBody");

       // 6. Which supports fallback in the same way as the "ToString" overload
       XhtmlString xhtmlWithFallback =
                currentPage.GetPropertyValue<XhtmlString>("MainBody", new XhtmlString());

       // 7. Advanced: Specify a conversion for the value
       // Note: this example is very similar to 1. since we just call ToString, EXCEPT
       // if MainBody is something else than an Xhtml property in which case 1. would still
       // return the ToString representation while this would throw an exception since
       // it tries to cast the value to an XhtmlString before handing it over to the
       // conversion lambda
       string convertedXhtml =
                currentPage.GetPropertyValue<XhtmlString, string>("MainBody", x => x.ToString());

       // 8. This is equivalent to 1. since it treats the property value as object
       string mainBody2 =
                currentPage.GetPropertyValue<object, string>("MainBody", x => x.ToString());
        
       // do something else and return view
   }

Using a property multiple times

If you access a property used multiple times in your code, you can save a reference to the property. Otherwise, the property is fetched from the internal collection for each call, which also may result in several unnecessary calls to the delegate if the property is not native to the page.

For example, use:

public ActionResult Index(StandardPage currentPage)
  {
    PropertyData property = currentPage.Property["PropertyName"];
    if (property != null && !property.IsNull)
    {
      DoSomething();
    }
    // do something else and return view
  }

Instead of:

public ActionResult Index(StandardPage currentPage)
  {
    if (currentPage.Property["PropertyName"] != null && !currentPage.Property["PropertyName"].IsNull)
    {
      DoSomething();
    }
    // do something else and return view
  }

Checking null values in properties

Optimizely CMS does not store properties with an empty value in the database. If you access a property from code, it is always null—not an empty string, 0, or false. This is because null is a very convenient way to check if something is not set by an editor or does not exist on this page. You just have to compare with null regardless of data type.

The following example throws a NullReferenceException if the value is empty or missing:

 public ActionResult Index(StandardPage currentPage)
 {
   StringBuilder sb = new StringBuilder();
   sb.Append(currentPage["PropertyName"].ToString());
   // do something else and return view
 }

StringBuilder.Append accepts null objects so this example is better than the previous example:

 public ActionResult Index(StandardPage currentPage)
 {
   StringBuilder sb = new StringBuilder();
   sb.Append(currentPage["PropertyName"]);
   // do something else and return view
 }

The following example throws a NullReferenceException if the value is empty or missing:

<%= @ViewModel.CurrentPage.Property["PropertyName"].Value.ToString()%>


Markup accepts any type and converts to string, so you do not need cast:

<%= @ViewModel.CurrentPage["PropertyName"] %>


The following example shows fallback for src attributes:

<img src='<%= @ViewModel.CurrentPage["ImageUrl"] ?? "/Missing.png" %>' />


To return a string, use the ?? operator as follows:

string s = currentPage["StringProperty"] as string ?? string.Empty;


The following example returns a value type:

DateTime date = (DateTime)(currentPage["DateProperty"] ?? DateTime.Now);
int i = (int)(currentPage["IntegerProperty"] ?? 0);


Another fallback in the markup using the ?? operator as follows:

<%= currentPage["Heading"] ?? currentPage.PageName %>

Updating values for a page

If you want to update properties for an existing page, make sure that you have a writable version of the page, as shown in the following example.

 public ActionResult Index(StandardPage currentPage)
 {
   PageData writablePage = currentPage.CreateWritableClone();
   // do something else and return view
 }

To set new values, assign them directly to the writablePage object by working against the properties for the most common page properties or by accessing the values directly:

writablePage.SomeProperty = "somevalue";

This is the same as:

writablePage["SomeProperty"] = "somevalue";

When you are done with the page, save it by using IContentRepository (available in DI container):

_contentRepository.Save(writablePage, EPiServer.DataAccess.SaveAction.Publish);

Note: You cannot save dynamic properties or properties that reside on other pages when you are working against IContentRepository.Save.

HTML encoding and Optimizely CMS page properties

HTML has a special handling for characters such as < > and symbols ? & /. Some browsers may truncate or corrupt spaces, or problems arise when the browser displays these types of characters. For your website to be XHTML valid, use the HttpUtility.HtmlEncode method on strings that you inject with inline expressions. HttpUtility.HtmlEncode is part of .NET Core. The HtmlEncode method encodes a string that is displayed in a web browser.

When outputting strings directly in Razor views normally you do not need to encode string. Razor by default encodes strings outputted inside a code block. The following example shows how to display the CMS Name property:

<h2>@ViewModel.CurrentPage.Name</h2>

Related topics

Do you find this information helpful? Please log in to provide feedback.

Last updated: Jul 02, 2021

Recommended reading